Coding Test Note

 

1. Graph Traversal

1.1 Adjacency matrix → Adjacency list

1
2
3
4
5
matrix2list = lambda mat: [{v2 for v2, con in enumerate(graph[i]) if con} for v1 in range(len(mat))]
adj_mat = [[0, 1, 0],
           [1, 0, 1],
           [0, 1, 0]]
adj_list = matrix2list(adj_mat)

1.2 DFS

1
2
3
4
5
6
7
8
9
10
11
12
13
def dfs(graph, src):
  s         = [src]
  visited   = {src}
  traversal = [src]
  while s:
    cur_v = s.pop()
    for next_v in (v for v in graph[cur_v] if v not in visited):
      s.append(next_v)
      visited.add(next_v)
      traversal.append(next_v)
  return traversal

traversal = dfs(adj_list, src)

1.3 BFS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from collections import deque

def bfs(graph, src):
  q         = deque([src])
  visited   = {src}
  traversal = [src]
  path      = {src: [src]}
  while q:
    cur_v = q.popleft()
    for next_v in (v for v in graph[cur_v] if v not in visited):
        q.append(next_v)
        visited.add(cur_v)
        traversal.append(cur_v)
        path[next_v] = path[cur_v] + [next_v]
  return traversal, path

traversal, path = bfs(adj_list, src)

1.4 Dijkstra Algorithm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from heapq import heappush, heappop
from math import inf

def dijkstra(graph, src):
    dists = {v: inf for v in graph}
    paths = {v: [] for v in graph}
    q     = []
    heappush(q, [0, [src]])  # (dist, [path])

    while q:
        cur_dist, cur_path = heappop(q)
        if cur_dist < dists[cur_path[-1]]:
            dists[cur_path[-1]] = cur_dist
            paths[cur_path[-1]] = cur_path
            for next_v, dist in graph[cur_path[-1]]:
                heappush(q, (cur_dist + dist, cur_path + [next_v]))
    return dists, paths

# graph = {1: [(2, 4)]}  # weight(1 -> 2) = 4
dists, paths = dijkstra(graph, src)

1.5 Floyd-Warshall Algorithm

1
2
3
4
5
6
7
8
9
from math import inf

def floyd_warshall(graph):
    dists = [len(graph)*[inf] for _ in len(graph)]  # node should start with 0
    for w in graph:  # v1 -> w -> v2
      for v1, v2 in product(graph, graph):
        dists[v1][v2] = min(dists[v1][v2], dists[v1][w] + dists[w][v2])
    return dists
dists = floyd_warshall(graph)

1.6 Kruskal Algorithm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def kruskal(graph):
    def find_parent(parents, x):
        if parents[x] != x:
            parents[x] = find_parent(parents, parents[x])
        return parents[x]
    def union_parents(parents, a, b):
        pa, pb = find_parent(parents, a), find_parent(parents, b)
        parents[pb] = pa if pa < pb else parents[pb]
        parents[pa] = pb if pa > pb else parents[pa]

    edges = []
    for v1 in graph:
        for v2, w in graph[v1]:
            edges.append((w, v1, v2))

    parents = list(range(len(graph)))
    dist = 0
    for w, v1, v2 in sorted(edges):
        if find_parent(parents, v1) != find_parent(parents, v2):
            union_parents(parents, v1, v2)
            dist += w
    return dist

dist = kruskal(graph)

2. Sorting

2.1 Selection Sort

1
2
3
4
def selection_sort(arr):
  for sel in range(len(arr)):
    argmin = min(range(sel, len(arr)), key=lambda i: arr[i])
    arr[sel], arr[argmin] = arr[argmin], arr[sel]

2.2 Insertion Sort

1
2
3
4
5
6
7
def insertion_sort(arr):
  for sel in range(1, len(arr)):
    for i in range(sel, 0, -1):
        if arr[i-1] > arr[i]:
            arr[i-1], arr[i] = arr[i], arr[i-1]
        else:
            break

2.3 Quick Sort

1
2
3
4
5
6
7
def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot, *tail = arr
    left  = [x for x in tail if x <= pivot]
    right = [x for x in tail if x > pivot]
    return quick_sort(left) + [pivot] + quick_sort(right)

2.4 Count Sort

1
2
3
4
5
6
7
8
def count_sort(arr):
    cnt = (max(arr)+1)*[0]
    for x in arr:
        cnt[x] += 1
    rst = []
    for x, cnt in enumerate(cnt):
        rst += cnt*[x]
    return rst

3. Binary Search

1
2
3
4
5
6
7
8
9
10
11
def binary_search(arr, target):
    s, e = 0, len(arr)-1
    while s <= e:
        mid = (s+e)//2
        if arr[mid] == target:
            return mid
        elif target < arr[mid]:
            e = mid-1
        elif arr[mid] < target:
            s = mid+1
    return -1

4. Dynamic Programming

4.1 Top-Down

1
2
3
4
5
6
7
8
cache = 100*[None]

def fibo(x):
    if x <= 2:
        cache[x] = 1
    elif cache[x] is None:
        cache[x] = fibo(x-1) + fibo(x-2)
    return cache[x]

4.2 Bottom-Up

1
2
3
4
5
6
7
cache = 100*[None]

def fibo(x):
    cache[1] = cache[2] = 1
    for i in range(3, x+1):
        cache[i] = cache[i-1] + cache[i-2]
    return cache[x]
1
2
3
4
5
6
def solution(arr):
    cache = len(arr)*[None]
    cache[1] = cache[2] = 1
    for i in range(3, len(arr)):
        cache[i] = max(cache[i-1], cache[i-2] + arr[i])
    return cache[len(arr)-1]