feat: more content
This commit is contained in:
parent
7bf132f456
commit
7836c9185c
@ -2,12 +2,16 @@
|
|||||||
| Algorithm | Best Time Complexity | Average Time Complexity | Worst Time Complexity | InPlace | Stable |
|
| Algorithm | Best Time Complexity | Average Time Complexity | Worst Time Complexity | InPlace | Stable |
|
||||||
| --------- | ------------------------ | -------------------------- | --------------------- | ------- | ------ |
|
| --------- | ------------------------ | -------------------------- | --------------------- | ------- | ------ |
|
||||||
| Bubble | $O(n)$ comp, $O(1)$ swap | $O(n^2)$ comp, swap | $O(n^2)$ comp, swap | Yes | Yes |
|
| Bubble | $O(n)$ comp, $O(1)$ swap | $O(n^2)$ comp, swap | $O(n^2)$ comp, swap | Yes | Yes |
|
||||||
| Selection | $O(n^2)$, $O(1)$ swap | $O(n^2)$ comp, $O(n)$ swap | $O(n^2)$, O(n) swap | Yes |
|
| Selection | $O(n^2)$, $O(1)$ swap | $O(n^2)$ comp, $O(n)$ swap | $O(n^2)$, O(n) swap | Yes | No |
|
||||||
| Insertion | $O(n)$, $O(1)$ swap | $O(n^2)$ comp, swap | $O(n^2)$ comp, swap | Yes | Yes |
|
| Insertion | $O(n)$, $O(1)$ swap | $O(n^2)$ comp, swap | $O(n^2)$ comp, swap | Yes | Yes |
|
||||||
| Merge | $O(n \log(n))$ | $O(n \log(n))$ | $O(n \log(n))$ | No | Yes |
|
| Merge | $O(n \log(n))$ | $O(n \log(n))$ | $O(n \log(n))$ | No | Yes |
|
||||||
| Quick | $O(n \log(n))$ | $O(n \log(n))$ | $O(n^2)$ | Yes | No |
|
| Quick | $O(n \log(n))$ | $O(n \log(n))$ | $O(n^2)$ | Yes | No |
|
||||||
| Counting | $O(n + k)$ | $O(n + k)$ | $O(n + k)$ |
|
| Counting | $O(n + k)$ | $O(n + k)$ | $O(n + k)$ |
|
||||||
| Radix | $O(nk)$ | $O(nk)$ | $O(nk)$ | No | Yes |
|
| Radix | $O(nk)$ | $O(nk)$ | $O(nk)$ | No | Yes |
|
||||||
|
|
||||||
|
Counting: O(N+k),
|
||||||
|
|
||||||
|
Radix: $O(w\times (N+k))$: Each item to sort as a string of w digits, sort by rightmost digit (using stable counting sort), k is radix
|
||||||
### Bubble
|
### Bubble
|
||||||
Invariant: After `i` iterations, largest `i` elements are sorted at the back of the array
|
Invariant: After `i` iterations, largest `i` elements are sorted at the back of the array
|
||||||
```java
|
```java
|
||||||
@ -21,7 +25,6 @@ static void sort(int[] input) {
|
|||||||
if (input[i] > input[i+1]) swap(input, i, i+1);
|
if (input[i] > input[i+1]) swap(input, i, i+1);
|
||||||
} } }
|
} } }
|
||||||
```
|
```
|
||||||
|
|
||||||
### Selection
|
### Selection
|
||||||
Invariant: After `i` iterations, smallest `i` elements are sorted to the front of the array
|
Invariant: After `i` iterations, smallest `i` elements are sorted to the front of the array
|
||||||
```java
|
```java
|
||||||
@ -35,10 +38,8 @@ static void sort(int[] input) {
|
|||||||
```
|
```
|
||||||
### Insertion
|
### Insertion
|
||||||
Invariant: At the end of kth iteration, leftmost `k` elements are sorted (but not in final correct position)
|
Invariant: At the end of kth iteration, leftmost `k` elements are sorted (but not in final correct position)
|
||||||
|
|
||||||
1. Outer loop executes N-1 times
|
1. Outer loop executes N-1 times
|
||||||
2. inner loop runs O(n) if arrayis reverse sorted
|
2. inner loop runs O(n) if arrayis reverse sorted
|
||||||
|
|
||||||
```java
|
```java
|
||||||
static void sort(int[] arr) {
|
static void sort(int[] arr) {
|
||||||
// iterate through each element in the array
|
// iterate through each element in the array
|
||||||
@ -79,15 +80,274 @@ void quickSort(array A, int low, int high) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
## Linked List
|
## Linked List
|
||||||
|
- Stack: Last In First Out, Push and Pop from Head
|
||||||
|
- Queue - First In First Out, Push to tail, pop from head
|
||||||
## Binary Heap
|
## Binary Heap
|
||||||
## Union Find
|
- Height: floor$(\log N)$, Left Node: $2n$, Right Node: $2n+1$, parent node = $\frac{n}{2}$
|
||||||
|
- Min Comparisions: n-1, min swaps: 0
|
||||||
|
- Max comparisons: 2(n) - 2(no of 1s) - 2(trailing 0 bits)
|
||||||
|
- Max swaps: $\frac{n}{2}$ until `x=1` then sum
|
||||||
|
|
||||||
|
```pseudocode
|
||||||
|
insert(node) {
|
||||||
|
arr.append(node)
|
||||||
|
bubbleUp(arr)
|
||||||
|
}
|
||||||
|
bubbleUp(node) {
|
||||||
|
while(A[i] > A[parent(i)]) {
|
||||||
|
swap(A[i], A[parent(v)]);
|
||||||
|
v = parent(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bubbleDown(node) {
|
||||||
|
A[1] = A[A.length-1]
|
||||||
|
i = 1; A.length--;
|
||||||
|
while(i < A.length)
|
||||||
|
if A[i] < (L = larger of i's children) {
|
||||||
|
swap(A[i], L); i = L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
optimised heapify() {
|
||||||
|
for (i = A.length/2; i >= 1; --i) shiftDown(i);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- Change value at index
|
||||||
|
- Update value
|
||||||
|
- If new value > old node, then bubbleUp, else bubbleDown
|
||||||
|
- Root element is the largest, 2nd largest element is child of root, 3rd largest doesn't have to be
|
||||||
|
- Max swaps: n / 2 integer divide until x = 1 and then sum
|
||||||
|
- Max comparisons: 2n - 2(num of 1s) - 2(trailing 0s)
|
||||||
|
|
||||||
|
### Invariants
|
||||||
|
- Every vertex is greater than every value of its children
|
||||||
|
- Must be a complete binary tree
|
||||||
|
- Height is always log(N), as every level has to be filled
|
||||||
|
## Union Find $O(\alpha N)$
|
||||||
|
- MIN height needed = $2^h$ nodes
|
||||||
|
- Keep track of rank[i], uppperbound height of a subtree rooted at vertex i
|
||||||
|
- `findSet(i)`: From vertex i, recursively go up the tree, until root of tree. Path compression used after each call of findSet
|
||||||
|
- `IsSameSet(i,j)`: Check if `FindSet(i) == FindSet(j)`
|
||||||
|
- `unionSet(i,j)`: Shorter tree joined to the taller tree (rank comes into play)
|
||||||
|
```python
|
||||||
|
def union(a,b):
|
||||||
|
ap = findSet(a)
|
||||||
|
bp = findSet(b)
|
||||||
|
if rank[ap] < rank[bp]: parents[ap] = bp
|
||||||
|
elif rank[bp] < rank[ap]: parents[bp] = ap
|
||||||
|
else: parent[bp] = ap; rank[ap] += 1
|
||||||
|
```
|
||||||
## Hash Table
|
## Hash Table
|
||||||
|
The hash table size (modulo) sould be a large prime about 2x size of expected number of keys
|
||||||
|
```python
|
||||||
|
def hash_string(s):
|
||||||
|
sum = 0
|
||||||
|
for c in s: sum = (sum*26)%M + ord(c)%M
|
||||||
|
return sum
|
||||||
|
```
|
||||||
|
- Open Addressing: All keys in single array, collision resolved by probing alternative address
|
||||||
|
- Closed Addressing: Adjacency List, append collided keys to auxiliary data structure
|
||||||
|
### Linear
|
||||||
|
`i = (h(v) + k*1)%M`
|
||||||
|
- On deletion set the value to removed, it knows that the values after it are valid during probing
|
||||||
|
- If the values tend to cluster, linear probing is bad
|
||||||
|
### Quadratic
|
||||||
|
`i = (h(v) + k*k)%M`
|
||||||
|
- Issues is that we might have infinite cycles with this scheme.
|
||||||
|
- If $\alpha < 0.5$ and M is prime, then we can always find empty slot
|
||||||
|
### Double Hashing
|
||||||
|
`i = (h(v) + k*h2(v))`, h2 is usually a smaller prime than M.
|
||||||
## Binary Search Trees
|
## Binary Search Trees
|
||||||
|
- Height: $O (\log_2(N)) < h < N$
|
||||||
|
- BST Property: For vertex X, all vertices on the left subtree of X are strictly smaller than X and all vertices on the right subtree are strictly greater than X
|
||||||
|
- `minHeight=ceil(log(n))-1`
|
||||||
|
- `maxHeight=n-1`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def search(target):
|
||||||
|
if curr == None: return None
|
||||||
|
if curr == target: return curr
|
||||||
|
if curr < target: return search(curr.right)
|
||||||
|
return search(curr.left)
|
||||||
|
def left(node): return node if node.left == None else return left(node.left)
|
||||||
|
def successor(node):
|
||||||
|
if node.right != null: return left(node.right)
|
||||||
|
p = node.parent, T = node
|
||||||
|
while(p != null and T == p.right): T = p, p = T.parent
|
||||||
|
if p == null: return None
|
||||||
|
return p
|
||||||
|
def remove(node, key):
|
||||||
|
if node == None: return None
|
||||||
|
if node.val < key: node.right = remove(node.right, key)
|
||||||
|
if node.val > key: node.left = remove(node.left, key)
|
||||||
|
if node.left == None and node.right == None:
|
||||||
|
node == None
|
||||||
|
elif node.left == None and node.right != None:
|
||||||
|
node.right.parent = node.parent
|
||||||
|
node = node.right
|
||||||
|
elif node.right == None and node.left != None:
|
||||||
|
node.left.parent = node.parent
|
||||||
|
node = node.left
|
||||||
|
else: # both nodes exist, find successor
|
||||||
|
successorV = successor(key)
|
||||||
|
node.val = successorV # replace this with successor
|
||||||
|
delete(T.right, successor) # delete successor
|
||||||
|
```
|
||||||
|
```python
|
||||||
|
def validBst(node, minV, maxV):
|
||||||
|
if not node: return True
|
||||||
|
if node.val <= minV or node.val >= maxV:
|
||||||
|
return False
|
||||||
|
left = validBst(node.left, minV, node.value)
|
||||||
|
right = validBst(node.right, node.value, maxV)
|
||||||
|
return left and right
|
||||||
|
```
|
||||||
|
## BBST AVL Tree
|
||||||
|
- each node augmented with height
|
||||||
|
- height: $h < 2\log(N)$
|
||||||
|
- `height=-1` (if empty tree), `height=max(left.h, right.h)+1`, computed at end of `insert(v)` or `remove(v)` operation
|
||||||
|
- `BF(n)=left.h-right.h`
|
||||||
|
- height balanced IF $|$left.h $-$right.h$|\leq 1$
|
||||||
|
```python
|
||||||
|
def h(node): node.height if node else -1
|
||||||
|
def rotateLeft(node):
|
||||||
|
if node.right == None: return node
|
||||||
|
w = node.right
|
||||||
|
w.parent = node.parent
|
||||||
|
node.parent = w
|
||||||
|
if (w.left != None): w.left.parent = node
|
||||||
|
w.left = node
|
||||||
|
node.height = max(h(node.left), h(node.right))+1
|
||||||
|
w.height = max(h(w.left), h(w.right))+1
|
||||||
|
return w
|
||||||
|
|
||||||
|
def balance(node):
|
||||||
|
bal = h(node.left) - h(node.right)
|
||||||
|
if bal == 2: # left heavy
|
||||||
|
bal2 = h(node.left.left)-h(node.left.right)
|
||||||
|
if bal2 != 1: node.left = rotateLeft(node.left)
|
||||||
|
node = rotateRight(node)
|
||||||
|
elif bal == -2:
|
||||||
|
bal2 = h(node.right.left)-h(node.right.right)
|
||||||
|
if bal2 != -1: node.right = rotateRight(node.right)
|
||||||
|
node = rotateLeft(node)
|
||||||
|
def insert(node, val):
|
||||||
|
if node == None: return Node(val)
|
||||||
|
if node.key < val:
|
||||||
|
node.right = insert(node.right, v)
|
||||||
|
node.right.parent = node
|
||||||
|
if node.key > val:
|
||||||
|
node.left = insert(node.left, v)
|
||||||
|
node.left.parent = node
|
||||||
|
node = balance(node)
|
||||||
|
node.height = max(h(node.left), h(node.right)) +1
|
||||||
|
return node
|
||||||
|
```
|
||||||
|
### Invariant
|
||||||
## Graph Structures
|
## Graph Structures
|
||||||
|
### Trees
|
||||||
|
- V vertices, E = V-1 edges, acyclic, 1 unique path between any pair of vertices
|
||||||
|
- Root a tree by running BFS/DFS from the root
|
||||||
|
```python
|
||||||
|
def isTree(AL, directed=True):
|
||||||
|
def dfs(node, visited, parent):
|
||||||
|
visited[node] = True
|
||||||
|
for neighbour in AL[node]:
|
||||||
|
if not visited[neighbour]:
|
||||||
|
if dfs(neighbour, visited, node): return True
|
||||||
|
elif neighbour != parent: return True
|
||||||
|
return False
|
||||||
|
n = len(AL)
|
||||||
|
visited = [False]*n
|
||||||
|
if dfs(0, visited, -1):
|
||||||
|
return False #cycle detected.
|
||||||
|
if False in visited:
|
||||||
|
return False # unconnected
|
||||||
|
edges = sum(len(n) for n in AL.values())
|
||||||
|
if directed:
|
||||||
|
if edges != n-1: return False
|
||||||
|
else:
|
||||||
|
if edges != 2*(n-1): return False
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
### Complete Graph
|
||||||
|
- $V$ vertices, $E=\frac{V\times(V-1)}{2}$ edges
|
||||||
|
```python
|
||||||
|
def isComplete(AL):
|
||||||
|
n = len(AL)
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(i+1, n):
|
||||||
|
if j not in AL[i]: return False
|
||||||
|
```
|
||||||
|
### Bipartite
|
||||||
|
- V vertices that can be split to 2 sets, where no edge between members of same set
|
||||||
|
- no odd length cycle
|
||||||
|
- Complete Bipartite: all V from one set connected to all V from other set.
|
||||||
|
```python
|
||||||
|
def isBipartite(AL):
|
||||||
|
n = len(AL)
|
||||||
|
colors = [-1]*n
|
||||||
|
for start in range(n): # incase it isn't connected
|
||||||
|
if colors[start] == -1:
|
||||||
|
q = deque([(start,0)]) # (vertex,color)
|
||||||
|
colors[start] = 0
|
||||||
|
while q:
|
||||||
|
curr, color = q.popleft()
|
||||||
|
for neighbour in AL[curr]:
|
||||||
|
if colors[neighbour] == -1:
|
||||||
|
colors[neighbour] = 1-color # flip color
|
||||||
|
q.append((neighbour, 1-color))
|
||||||
|
elif colors[neighbour] == color:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
### DAG
|
||||||
|
- Directed, no cycle
|
||||||
|
```python
|
||||||
|
def isDag(AL):
|
||||||
|
def dfs(node, visited, stack):
|
||||||
|
visited[node] = True
|
||||||
|
stack[node] = True
|
||||||
|
for n in AL[node]:
|
||||||
|
if not visited[n]:
|
||||||
|
if dfs(n, visited, stack):
|
||||||
|
return True # pass it down
|
||||||
|
elif stack[n]:
|
||||||
|
return True # back edge found
|
||||||
|
stack[node] = False
|
||||||
|
return False
|
||||||
|
n = len(AL)
|
||||||
|
visited = [False]*n
|
||||||
|
stack = [False]*n
|
||||||
|
for i in range(n):
|
||||||
|
if not visited[i]:
|
||||||
|
if dfs(i, visited,stack):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
```
|
||||||
|
```python
|
||||||
|
def isDagBfs(AL):
|
||||||
|
n = len(AL)
|
||||||
|
inDeg = [0]*n
|
||||||
|
for nbs in AL:
|
||||||
|
for nb in nbs:
|
||||||
|
inDeg[nb] += 1
|
||||||
|
q = deque([v for v in range(n) if inDeg[v] == 0]) # queue of 0 indeg
|
||||||
|
while q:
|
||||||
|
curr = q.popleft()
|
||||||
|
for nb in AL[curr]:
|
||||||
|
inDeg[nb] -= 1
|
||||||
|
if inDeg[nb] == 0: q.append(nb)
|
||||||
|
return all(i == 0 for i in inDeg) # all inDeg == 0
|
||||||
|
```
|
||||||
|
### Storage
|
||||||
|
- Adjacency Matrix - Check existance of edge in $O(1)$. Storage $O(V^2)$
|
||||||
|
- Adjacency List - Storage $O(V+E)$
|
||||||
|
- Edge List - Storage $O(E)$
|
||||||
## Graph Traversal
|
## Graph Traversal
|
||||||
### Topo Sorting
|
- Reachability Test: DFS/BFS and check if visited
|
||||||
#### Lexographic Kahn's Algorithm $O(V\log(V)+E)$
|
- ID CCs: run DFS on a vertex. all visited=true is in same CC
|
||||||
|
- count CCs: for all u in V, if unvisited, incr CCCount, then DFS
|
||||||
|
### Lexographic Kahn's Algorithm (Topo Sort) $O(V\log(V)+E)$
|
||||||
Non Lexographic Variant is O(V+E)
|
Non Lexographic Variant is O(V+E)
|
||||||
```python
|
```python
|
||||||
from heapq import heappush, heappop
|
from heapq import heappush, heappop
|
||||||
@ -114,7 +374,21 @@ def topoSort(AL):
|
|||||||
return res
|
return res
|
||||||
```
|
```
|
||||||
## Single Source Shortest Path
|
## Single Source Shortest Path
|
||||||
|
```python
|
||||||
|
def relax(from,to, weight):
|
||||||
|
if dist[v] > dist[u]+weight: # can be shortened
|
||||||
|
dist[v] = d[u]+weight
|
||||||
|
path[v] = u
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
```
|
||||||
|
- On unweighted graphs: BFS
|
||||||
|
- On graphs without negative weight: Dijkstra
|
||||||
|
- On graphs without negative weight cycles: Modified Dijkstra
|
||||||
|
- On Tree: BFS/DFS
|
||||||
|
- On DAG: DP
|
||||||
### Bellman Ford Algorithm $O(V\times E)$
|
### Bellman Ford Algorithm $O(V\times E)$
|
||||||
|
- Can detect negative weight cycles
|
||||||
```python
|
```python
|
||||||
def bellman_ford(AL, numV, start):
|
def bellman_ford(AL, numV, start):
|
||||||
dist = [INF for _ in range(V)]
|
dist = [INF for _ in range(V)]
|
||||||
@ -138,11 +412,38 @@ def bellman_ford(AL, numV, start):
|
|||||||
print(start, u, dist[u])
|
print(start, u, dist[u])
|
||||||
```
|
```
|
||||||
### Breadth First Search
|
### Breadth First Search
|
||||||
|
Instead of standard visited array, replace with dist[] array, where initial distances are infinite. Then set source to 0. if D[v] = inf, then set it to d[u]+1
|
||||||
### Dijkstra's Algorithm $O((V+E)\log Vk)$
|
### Dijkstra's Algorithm $O((V+E)\log Vk)$
|
||||||
#### Modified Dijkstra's Algorithm
|
- BFS with a priority queue
|
||||||
### Dynamic Programming
|
```python
|
||||||
|
def dijkstra(AL, start):
|
||||||
|
V = len(AL)
|
||||||
|
dist = [float('inf') for u in range(V)]
|
||||||
|
dist[start] = 0
|
||||||
|
pq = []
|
||||||
|
heappush(pq, (0, start))
|
||||||
|
while pq:
|
||||||
|
dist, u = heappop(pq)
|
||||||
|
if (dist > dist[u]): continue
|
||||||
|
for v, w in AL[u]:
|
||||||
|
if dist[u]+w >= dist[v]: continue
|
||||||
|
dist[v] = dist[u]+w
|
||||||
|
heappush(pq, (dist[v], v))
|
||||||
|
```
|
||||||
|
### DFS O(V)
|
||||||
|
DFS can be used to solve SSSP on a weighted tree. Since there is only 1 unique path that connects source to another vertex, that path is teh shortest path
|
||||||
|
### Dynamic Programming O(V+E)
|
||||||
|
SSSP on a DAG. Find the topo sort and relax in the order of the topo sort.
|
||||||
|
```python
|
||||||
|
order = khan_toposort() # O(V+E)
|
||||||
|
while not order.empty():
|
||||||
|
u = order.popleft()
|
||||||
|
for v, w in AL[u]: relax(u, v, w)
|
||||||
|
```
|
||||||
## Minimum Spanning Tree
|
## Minimum Spanning Tree
|
||||||
|
- Tree that connects to all vertices of G
|
||||||
### Kruskal's Algorithm $O(E\log(V))$
|
### Kruskal's Algorithm $O(E\log(V))$
|
||||||
|
- sort edges, then loop over these edges, and greedily take the next edge that does not cause cycles
|
||||||
If the weight of the edges is bounded, then you can use counting sort to bring the complexity down to O(E)
|
If the weight of the edges is bounded, then you can use counting sort to bring the complexity down to O(E)
|
||||||
```python
|
```python
|
||||||
def kruskalMST(EL, numV, numE): # edge list has (weight, from, to)
|
def kruskalMST(EL, numV, numE): # edge list has (weight, from, to)
|
||||||
@ -159,7 +460,9 @@ def kruskalMST(EL, numV, numE): # edge list has (weight, from, to)
|
|||||||
UF.unionSet(u, v)
|
UF.unionSet(u, v)
|
||||||
print(cost)
|
print(cost)
|
||||||
```
|
```
|
||||||
### Prim's Algorithm
|
### Prim's Algorithm $O(E \log(V))$
|
||||||
|
- Starts from a vertex, and queues all edges to PQ
|
||||||
|
- if head vertex has not been visited, take that edge and repeat from 1
|
||||||
```python
|
```python
|
||||||
def prims(AL, numV):
|
def prims(AL, numV):
|
||||||
pq = []
|
pq = []
|
||||||
@ -179,3 +482,13 @@ def prims(AL, numV):
|
|||||||
if not taken[v]: heappush(pq, (w, v))
|
if not taken[v]: heappush(pq, (w, v))
|
||||||
print(cost)
|
print(cost)
|
||||||
```
|
```
|
||||||
|
## Miscellaneous
|
||||||
|
- make a graph where the vertices are a pair <rectangle, horizontal/vertical>
|
||||||
|
- define the edges as the following
|
||||||
|
- for each <rectangle, vertical>, make a directed edge to <neighboring rectangle in vertical direction, horizontal>
|
||||||
|
- for each <rectangle, horizontal>, make a directed edge to <neighboring rectangle in horizontal direction, vertical>
|
||||||
|
- (still undefined: what is the weight?)
|
||||||
|
- run multi-source shortest path, where the sources are <starting rectangle, vertical> and <starting rectangle, horizontal>
|
||||||
|
- (still undefined: what shortest path algorithm to be used here?)
|
||||||
|
- take the shortest path to <target rectangle, vertical> as the answer
|
||||||
|
- (is this correct? any other vertex to consider?)
|
||||||
|
Loading…
Reference in New Issue
Block a user