Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
● EX: a complete binary tree with 2^n - 1 nodes has a height of n-1, so is balanced
● AVL Property: heights of left and right subtrees of every node differ by at most 1
○ Maintain height and Balance Factor of each subtree in subtree root node
○ balance factor: Height of right subtree - Height of Left subtree -> {-1,0,1} -> computer in O(1)
○ When insert or remove node into AVL, root node BF change by at most 1 or -1
● If BF -2 or 2-> Rebalance and Rotation
○ If root.left has balance factor +1 // CASE 2 perform left rotate on root.left
● Perform right rotate on root // CASE 1
● Else if root’s balance factor is +2, do opposite rotations, applying Case 2 to root.right
● Right Rotate Code (root){ TreeNode orph = root.left.right then TreeNode rotate = root.left;
○ rotate.setRight(root); then rotate.right.setLeft(orph);
○ updateHeight(rotate.right) then updateHeight(rotate); then return rotate;
● Insert: only 1 rotation needed, Delete: continue to check and rebalance recursively O(logn)
● Cost AVL Maintenance (Rotation O(1) + check & rebalance O(1) per level = BST ops ϴ(log n)
2-3-4 Trees (B-Tree) -> a complete binary tree-> Each node can hold 1,2, or 3 keys
● Grows bottom-up (not top-down), A non-leaf node with t keys have t + 1 children
● Every path from root to bottom of tree has the same height h = 2^(h+1) -1 keys
○ If every node has 1 key (min), “same height” property implies that tree is a complete binary
tree of height h-> O (log n) height with n keys
● Insert: insert into existing leaf to maintain same path height ->if full, insert into temp overloaded node
○ Split overloaded node into 2 and push the mid key up to the parent (key #2/4), then
recursively split (splitting takes O(1) so worst case insertion is O(log n)) | Delete: O( log n)
Red-Black Tree binary rep. of a 234 tree: height O(log n) -> Convert each node in 234 to mini binary tree,
every node map to a black node with 0-2 red children.
Graph Searches -> Graph G = (V, E) is a set of V vertices and E Edges | A graph has O(n^2) edges
● Directed Graph: u->v | asymmetric A -> B does not imply B -> A | has v(v-1) edges
● Undirected Graph: u-v | symmetric A - B | has n(n-1)/2 edges
● DENSE GRAPH: O(n^2) edges | SPARSE GRAPH: O(n) edges
● Adjacency Matrix: M(i,J) -> 1 if edge (i, j) exists, 0 otherwise | undirected: M is symmetric
○ Space: ϴ( V^2) | Check if Edge exists: ϴ(1) | Enumerate Al Edges: ϴ(V^2)
● Adjacency List: Array A[1…..n] -> A[i] contains list of edges (i, j)
○ Space: ϴ(V+E) | Check if Edge exists: ϴ(E) | Enumerate All Edges:ϴ(V+E)
Breadth-First-Search (BFS)-> use FIFO queue that tracks vertices to be searched | Cost = ϴ(|V| +|E|)
● Good for: Shortest distances, Bipartite graphs, State-space search in AI
● Initially, Q contains only starting vertex v, which is marked; v.distance 0; v.parent null
● While Isnotempty THEN u = Q.dequeue() THEN for each edge (u,w)
○ if w is not marked THEN mark w; w.distance = u.distance + 1; w.parent = u THEN Q.enqueue(w)
● D(v,u) is the smallest # of edges on any path from v to u | Unreachable vertex has distance is infinity.
● Parent pointers from a tree of shortest paths pointing back to the starting vertex
● Remove parent node from Q same time you add its children nodes to the Queue.
Bipartite Graph : consists of 2 sets of L and R vertices and edges go between L and R
● Pick a starting V and label V on side L, run BFS, if we discover vertex w via edge (u,w) label w on opposite
side from u, graph is bipartite is BFS never labels both endpoints of an edge (u,w) with the same side.
Depth-First-Search(DFS): First Start, Last Finished using a Stack -> Cost = ϴ(|V| + |E|)
● POP off stack when finished | PUSH when discovered.
● Good for: Cycle detection, Dependency resolution, Reachability Compiler analyses
● Algo: each vertex has a start & finish time & time ticks after each assessment of a vertex, if no where to go,
close out end time then back track until can move forward again. If not reach all, start untouched v: +1 time
● DFSVisit(v) AND v.start = time
○ for each edge (v,u) -> if (u.start is not yet set) THEN DFSVisit(u) || after for-loop: v.finish = time
● G contains a cycle if DFSVisit(v) ever finds an edge (v,u) where u is started but not finished.
○ Proof: If DFSVisit(u) finds adjacent vertex w that is started, not finish, then DFSVisit(w) was called
earlier and is not yet done. Hence, DFS found a path from w to u. But edge (u,w) also exists: cycle.
● Topological Order: If a directed graph does not contain a cycle, we can assign an order to its vertices
○ TOP of directed, acyclic graph (DAG) is total ordering of reverse of finish times
○ If given a TOP order, draw arrows to each letter like in graph, if arrow in opp. Direction: not TOP
○ Proofs: If look at edge (u,v) and v is unstarted vertex, then v has a smaller finishing time than u.
■ V cannot be “in-progress” when (u,v) is traversed because then it would create a cycle in
the graph and the edge (u,v) would not need to be traversed. *****
■ Could start at last vertex then back track because all nodes would end immediately and
first vertex would end at time 1.
○ G=(V,E) has a unique TOP order if has directed path of |V|−1 edges that touch all |V| (Hamiltonian)
Dijkstra's Algorithm -> weighted edges and shortest path (length of path is sum of edge weights)
● Problem: given a starting vertex v, find path of least total weight from v to each other vertex in the graph.
● D Algo: at each step, explore edges out of vertex v with smallest v .dist, and relax all its adjacent vertices.
Of all vertex.dist, choose the smallest one globally, continue with that vertex. Stop when each vertex has
had its outgoing edges explored once. Parent edges form a shortest path tree
● Relaxation: maintain, for each vertex v, the length of the shortest path to v seen so far. Store this shortest
path estimate as v.dist. At each step explore edges out of v and relax adjacent edges before chooses next.
● Starting vertex v gets v.dist=0; all other u get u.dist = ∞, mark all vertices as unfinished
● while (any vertex unfinished) -> V unfinished vertex with smallest v.dist
○ for each edge (v,u)-> if (v.dist + w(u,v) < u.dist) -> u.dist = v.dist + w(u,v) -> mark v finished
● Correctness of D Algo: when we explore the edges out of vertex v, v has its correct shortest-path distance
D(start, v) stored in current best estimate v.dis
● Use a Priority Queue: PQ of unfinished vertices based on distance, PQ.extractmin() to get next smallest,
and to update Decrease the v.dist
○ v.dist = 0; D[v] = PQ.insert(starting vertex v) | For other vertices u -> U.dist = ∞; D[u]=PQ.insert(u)
○ while (PQ not empty) THEN V = PQ.extractMin()
■ for each edge (v,u), if (v.dist + w(v,u) < u.dist) THEN u.dist=v.dist+w(v,u) THEN
D[u].dec(u)
● Inner for loop: O(E) | Time of each iteration of for loop: O(logV) | extractmind(): O(logV) caled O(V) times
● Running Time: all PQ take Θ( log V) -> DIJ Cost = Θ((|V| + |E|) log |V|)
○ Dense: Θ(V^2 logV) | Sparse: Θ(VlogV)
● Run time on Adjacency Matrix: O(V) to find neighbors of each vertex | Overall: O(V^2logV)
○ Inner loop: V takes LogV | Outer loop: V Time | Log v to extractmin() | = V(log v + vlogv)
Fibonacci heap: takes Θ(log n) time to do an extractMin() but O(1) for insert or decrease.
● Cost of Fib heap with DIJ is Θ(V + E + VlogV) -> Dense: Θ(V^2) | Sparse: Θ(VlogV)
Single-Source Shortest-Paths -(SSSP problem): edge weights upper bounded by a constant w.
● The longest path between two vertices in a graph is w(n-1)-To make DIJ run on this graph in O(nW + m)
time
● Use an array of buckets w(n-1), each vertex has a spot in the table and as you perform DIJ algo, you update
the paths stored in each index as appropriate and move to the next smallest distance.
● Find min by starting at left most bucket & scan right till non-empty bucket: Bucket index=pos weight
Negative Edges: DIJ fails when about to traverse a (-) edge, can’t convert graph w/ (-) edges to run properly w/ DIJ.
Greedy Algorithms + Min Spanning Tree (min all weights of edges)-min total cost to span tree & connect vertices
● If cycle exists, an edge can be removed w/o disconnecting any vertex-> T is (undirected) acyclic graph: tree.
● A Tree T spans G if it has n-1 vertices and Graph G may not have unique MSTs
● Proof: The always chooses the next best greedy edge to create a globally optimal solution and the algo
must terminate. It terminates by not creating cycles, as the cycle would go on forever and never terminate
Prim’s Algo -> cost of Θ((|V| + |E|) log |V|)-> same as Dijkstra-> good for dense graphs -> connect power grid
● Start at arb. vertex, pick edge e of min w(e) that connects a vertex in T to a vertex not yet in T.(undir. graph)
● After any # of edges are chosen, the algo’s current edge set is a subset of some MST for G
● If T has V-1 edges, then it spans G.
● Implementation -> can use a min-first PQ-> like DIJ (total weight of path) vs (weight of last edge on path)
○ Maintain set of unconn vertices->
○ For each unconn v, maintain a v.conn, weight of lowest-weight edge connecting v to any vertex in
T.
○ When add edge (u,v) to T, update connections to each x adjacent to v.
○ If W(v, x) < x.conn, than x.conn = w(v,x)
● Starting vertex v gets v.conn = 0; all other u get u.conn = ∞ | mark all vertices as unconnected
● while (any vertex unconnected) -> V = unconnected vertex with smallest v.conn
○ for each edge (v,u) -> if (uconn > w(u,v)) THEN uconn = w(u,v) THEN mark v connected
Kruskal’s Algo-> O(ElogE)-> good for sparse graphs
● Algo: Add to T the edge e of minimum w(e) that does not form a cycle when combined with edges already in
T. -> Can’t pick/add an edge that adds a cycle -> First create a sorted array-> maintain list of components
Greedy Decisions
● Dijkstra: Pick the vertex v with minimum v.dist that has not yet been expanded
● Prim: pick the edge e of minimum w(e) that connect a vertex in T to a vertex not in T
● Kruskal: add to T the edge e of min. w(e) that doesn’t form a cycle when combined with edges already in T
Important Algorithm Run-Times
● Binary Search: ϴ(logn) |HeapSort: ϴ(nlogn) | Mergesort: ϴ(nlogn) |Counting Sort: ϴ(n+k) -> ϴ(n) | R adix
Sort: ϴ(d(n+k)) : d = # of digits, n = # of values, b = base -> base 10 | Comparison Sorts: Ω (n logn) |
Array Inserts(Doubling vs. Add One): int [ ] arr = new int [n] = ϴ(n)
Hash Function Design (convert h-codes to indices between 0-m) -> use prime number -> Correct and efficient
● Division Mapping: k mod m | Multiplicative Mapping: [((c * A)mod 1.0)*m ]:cast int-mod 1.0 take floor of #
● Required 2 objs that are equal have equal hashcodes | Optimal 2 objs not equal have not equal h-codes
● Ideal performance-> distribute keys equally and independently across [0,m)
○ Order matters: multiply by primes(tuple) | Order not matter(set): just add.
● Simple Uniform Hashing: index: Assume it is equally likely for any key to map to each value from [0,m)
Master Method: T(n) = aT(n/b) + f(n): a = # of recursive calls in an algorithm of size n/b with f(n) work
● Master Method Theorem (Compare f(n) to n^logb(a))-> 1. If f(n) = O (n^logb(a)) -> T(n) = ϴ(n^logb(a))
○ 2. If f(n) = ϴ(n^logb(a) log ^k (n))
■ A. k > -1 , T(n) = ϴ( n^logb(a) log^(k+1)n B. k = -1 , T(n) = ϴ(n^logb(a) log log n)
■ C. k < -1, T(n) = ϴ(n^logb(a))
○ 3. If f(n) = Omega( n^logb(a)) -> T(n) = ϴ (f(n))
● If f(n) poly Greater g(n): exception-> n^2 log n is poly. Equal to n ^2 but n^3 log n is greater than n ^2
● Master methed not apply if a is not a constant or less than 1 or f(n) is negative
^ L = more buckets = better for time /// v L = less buckets = better for space
○ n/m <= L (upper bound on alpha), when n/m is bigger than L, double array size and re-hashTakes
approx. ϴ(n). To map to a new, larger table when maintaining a load factor
Write in :
1.
ALL OPS
Linked List (No Tail): ϴ(n)- unless insert at head
Linked List (With Tail): search ϴ(n), insert ϴ(1) [ unorder ϴ(n)] ,
Unorder Array: generally ϴ(n), interst at end is ϴ(1), insert could be ϴ(1) with replace, n inserts ϴ(n)
Order Array: Search ϴ(logn), all else ϴ(n)
Min-heap array: search ϴ(n), find mind ϴ(1), all else ϴ(logn), n inserts ϴ(n logn)
Hash table: Average: ϴ(L) 1 + n/m Worst: ϴ(n)-> no load factor
AVL: all ϴ(logn)
BST: Balanced:ϴ(logn) worst-case -Unbalanced: ϴ(n)
2-3-4 and RB : ϴ(logn)
2.
Linked List (no ϴ(n) At head: ϴ(1) ϴ(n) ϴ(n) ϴ(n) At Tail: ϴ(n^2)
Tail) At Tail: ϴ(n)
In mid: ϴ(n)
Linked List (with ϴ(n) At Tail: ϴ(1) (no ϴ(n) ϴ(n) At head/tail:ϴ(1) ϴ(n)
Tail) loop) Unorder: ϴ(n) In mid: ϴ(n)
Unorder: ϴ(1) Order: ϴ(1) Order: ϴ(1) after found
Order: ϴ(n)
Unordered Array ϴ(n) At end: ϴ(1) Find: ϴ(n) ϴ(n) From end: ϴ(1) ϴ(n)*
(doubling) Middle: ϴ(n) Extract: remove From mid: ϴ(n) if shift, ϴ(1) if
index, replace last replace
node at index ϴ(1)
+ϴ(n) to find
Ordered ϴ(logn)- binary At end: ϴ(1) Find: ϴ(1) Finds: From end: ϴ(1)
Array(small-big) search Middle: ϴ(n) Extract: ϴ(n)- shift ϴ(1) From mid: ϴ(n)
elements Extract:
K ex() : ϴ(n)** ϴ(1)
BST Balanced:ϴ(logn)
Unbalanced: ϴ(n)
● Neg-weights -> Bellman-Ford ( Θ(|V||E|)) | shortest path for every pair Floyd-Warshall (Θ(|V|^3))