Algorithm Basic Notes
Sorting Algorithm

Summary
- 强制稳定: 增加(唯一)时间戳, 修改 CompareTo 接口定义 => 当主元素相同时, 时间戳小的元素更小
Selection Sort
- swap: O(n)
- compare: O(n^2)
Insertion Sort
- swap: O(n^2/4)
- compare: O(n^2/4)
Shell Sort
- swap: O(n^2/4)
- compare: O(n^2/4)
Merge Sort
- 利用 Merge Sort 计算逆序对个数: left[i] > right[j] => inversions += (mid - i + 1), 即所有 i~mid 元素都与 j 元素为逆序对
    // merge and count
    private static long merge(int[] a, int[] aux, int lo, int mid, int hi) {
        long inversions = 0;
        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k];
        }
        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)           a[k] = aux[j++];
            else if (j > hi)            a[k] = aux[i++];
            else if (aux[j] < aux[i]) { a[k] = aux[j++]; inversions += (mid -
            i + 1); }
            else                        a[k] = aux[i++];
        }
        return inversions;
    }
    // return the number of inversions in the subArray b[lo..hi]
    // side effect b[lo..hi] is rearranged in ascending order
    private static long count(int[] a, int[] b, int[] aux, int lo, int hi) {
        long inversions = 0;
        if (hi <= lo) return 0;
        int mid = lo + (hi - lo) / 2;
        inversions += count(a, b, aux, lo, mid);
        inversions += count(a, b, aux, mid+1, hi);
        inversions += merge(b, aux, lo, mid, hi);
        assert inversions == brute(a, lo, hi);
        return inversions;
    }
    /**
     * Returns the number of inversions in the integer array.
     * The argument array is not modified.
     * @param  a the array
     * @return the number of inversions in the array. An inversion is a pair of
     *         indices {@code i} and {@code j} such that {@code i < j}
     *         and {@code a[i]} > {@code a[j]}.
     */
    public static long count(int[] a) {
        int[] b   = new int[a.length];
        int[] aux = new int[a.length];
        for (int i = 0; i < a.length; i++)
            b[i] = a[i];
        long inversions = count(a, b, aux, 0, a.length - 1);
        return inversions;
    }
    // return Kendall tau distance between two permutations
    public static long distance(int[] a, int[] b) {
        if (a.length != b.length) {
            throw new IllegalArgumentException("Array dimensions disagree");
        }
        int n = a.length;
        int[] ainV = new int[n];
        for (int i = 0; i < n; i++)
            ainV[a[i]] = i;
        Integer[] bNew = new Integer[n];
        for (int i = 0; i < n; i++)
            bNew[i] = ainV[b[i]];
        return Inversions.count(bNew);
    }
Quick Sort
- partition: 哨兵(最后再将其归位) + 大循环 + 2 小循环, 交换元素法
- partition: 辅助数组 brr, 3 循环(3 次扫描 arr) 分别将小/等/大于 guard 的数加入 brr
- partition: 哨兵(最后再将其归位) + lo + hi, 外加 2 个动指针 leftLimit 与 rightLimit, 表示小于区的上界和大于区的上界
// lt eq gt three parts
void quick3waySort(int *a, int lo, int hi) {
    if (hi <= lo) return;
    int lt = lo, i = lo+1, gt = hi;
    int v = a[lo];
    while (i <= gt) {
        int cmp = a[i].compareTo(v);
        if      (cmp < 0) exch(a, lt++, i++);
        else if (cmp > 0) exch(a, i, gt--);
        else i++;
    }
    sort(a, lo, lt - 1);
    sort(a, gt + 1, hi);
}
Heap Sort
- Built on Priority Queue
- swap: 2NlgN + 2N (2NlgN for sink N times, 2N for construct MaxHeap)
- compare: NlgN + N (NlgN for sink N times, N for construct MaxHeap)
// MaxPQ
void swim(int k) {
    while (k > 1 && less(k/2, k)) {
        exch(k/2, k);
        k = k/2;
    }
}
void sink(int k) {
    while (2*k <= N) {
        int j = 2*k;
        if (j < N && less(j, j+1)) j++;
        if (!less(k, j)) break;
        exch(k, j);
        k = j;
    }
}
Radix Sort
基数排序 (可用于混乱 shuffle 数组):
- 从个位到高位放入桶
- 从高位到个位放入桶
Sorting Algorithms Performance

Tree Algorithm
Binary Search Tree
Hibbard Deletion
2-3 Tree
2-3 Tree is Balance Tree:
插入:
- 1+1=2node -> 3node
- 1+2=3node -> 4node -> 2node
- 将 4node 结点中间元素移至父结点, 其余 2 元素分离为子 2node 节点
Red-Black BST
- 基于 2-3Tree, 将 3node 用红色标记
- 关键: 将红色标记向上传递至根部
    // is node x red; false if x is null ?
    private boolean isRed(Node x) {
        if (x == null) return false;
        return x.color == RED;
    }
    // make a right-leaning link lean to the left
    private Node rotateLeft(Node h) {
        // assert (h != null) && isRed(h.right);
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = x.left.color;
        x.left.color = RED;
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }
    // make a left-leaning link lean to the right
    private Node rotateRight(Node h) {
        // assert (h != null) && isRed(h.left);
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = x.right.color;
        x.right.color = RED;
        x.size = h.size;
        h.size = size(h.left) + size(h.right) + 1;
        return x;
    }
    // flip the colors of a node and its two children
    private void flipColors(Node h) {
        // h must have opposite color of its two children
        // assert (h != null) && (h.left != null) && (h.right != null);
        // assert (!isRed(h) &&  isRed(h.left) &&  isRed(h.right))
        //    || (isRed(h)  && !isRed(h.left) && !isRed(h.right));
        h.color = !h.color;
        h.left.color = !h.left.color;
        h.right.color = !h.right.color;
    }
    // insert the key-value pair in the subtree rooted at h
    private Node put(Node h, Key key, Value val) {
        // insert/put new node as left/right child of leaf node
        if (h == null) return new Node(key, val, RED, 1);
        int cmp = key.compareTo(h.key);
        if      (cmp < 0) h.left  = put(h.left,  key, val);
        else if (cmp > 0) h.right = put(h.right, key, val);
        else              h.val   = val;
        // fix-up any right-leaning links
        if (isRed(h.right) && !isRed(h.left))      h = rotateLeft(h);
        if (isRed(h.left)  &&  isRed(h.left.left)) h = rotateRight(h);
        if (isRed(h.left)  &&  isRed(h.right))     flipColors(h);
        h.size = size(h.left) + size(h.right) + 1;
        return h;
    }
    public void put(Key key, Value val) {
        if (key == null) {
            throw new IllegalArgumentException("first argument to put() is null");
        }
        if (val == null) {
            delete(key);
            return;
        }
        root = put(root, key, val);
        root.color = BLACK;
        // assert check();
    }
    // Assuming that h is red and both h.left and h.left.left
    // are black, make h.left or one of its children red.
    private Node moveRedLeft(Node h) {
        // assert (h != null);
        // assert isRed(h) && !isRed(h.left) && !isRed(h.left.left);
        flipColors(h);
        if (isRed(h.right.left)) {
            h.right = rotateRight(h.right);
            h = rotateLeft(h);
            flipColors(h);
        }
        return h;
    }
    // Assuming that h is red and both h.right and h.right.left
    // are black, make h.right or one of its children red.
    private Node moveRedRight(Node h) {
        // assert (h != null);
        // assert isRed(h) && !isRed(h.right) && !isRed(h.right.left);
        flipColors(h);
        if (isRed(h.left.left)) {
            h = rotateRight(h);
            flipColors(h);
        }
        return h;
    }
    // restore red-black tree invariant
    private Node balance(Node h) {
        // assert (h != null);
        if (isRed(h.right))                      h = rotateLeft(h);
        if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
        if (isRed(h.left) && isRed(h.right))     flipColors(h);
        h.size = size(h.left) + size(h.right) + 1;
        return h;
    }
    // delete the key-value pair with the minimum key rooted at h
    private Node deleteMin(Node h) {
        if (h.left == null)
            return null;
        if (!isRed(h.left) && !isRed(h.left.left))
            h = moveRedLeft(h);
        h.left = deleteMin(h.left);
        return balance(h);
    }
    /**
     * Removes the smallest key and associated value from the symbol table.
     * @throws NoSuchElementException if the symbol table is empty
     */
    public void deleteMin() {
        if (isEmpty()) throw new NoSuchElementException("BST underflow");
        // if both children of root are black, set root to red
        if (!isRed(root.left) && !isRed(root.right))
            root.color = RED;
        root = deleteMin(root);
        if (!isEmpty()) root.color = BLACK;
        // assert check();
    }
    // delete the key-value pair with the maximum key rooted at h
    private Node deleteMax(Node h) {
        if (isRed(h.left))
            h = rotateRight(h);
        if (h.right == null)
            return null;
        if (!isRed(h.right) && !isRed(h.right.left))
            h = moveRedRight(h);
        h.right = deleteMax(h.right);
        return balance(h);
    }
    /**
     * Removes the largest key and associated value from the symbol table.
     * @throws NoSuchElementException if the symbol table is empty
     */
    public void deleteMax() {
        if (isEmpty()) throw new NoSuchElementException("BST underflow");
        // if both children of root are black, set root to red
        if (!isRed(root.left) && !isRed(root.right))
            root.color = RED;
        root = deleteMax(root);
        if (!isEmpty()) root.color = BLACK;
        // assert check();
    }
    // delete the key-value pair with the given key rooted at h
    private Node delete(Node h, Key key) {
        // assert get(h, key) != null;
        if (key.compareTo(h.key) < 0)  {
            if (!isRed(h.left) && !isRed(h.left.left))
                h = moveRedLeft(h);
            h.left = delete(h.left, key);
        }
        else {
            if (isRed(h.left))
                h = rotateRight(h);
            if (key.compareTo(h.key) == 0 && (h.right == null))
                return null;
            if (!isRed(h.right) && !isRed(h.right.left))
                h = moveRedRight(h);
            if (key.compareTo(h.key) == 0) {
                Node x = min(h.right);
                h.key = x.key;
                h.val = x.val;
                // h.val = get(h.right, min(h.right).key);
                // h.key = min(h.right).key;
                h.right = deleteMin(h.right);
            }
            else h.right = delete(h.right, key);
        }
        return balance(h);
    }
    /**
     * Removes the specified key and its associated value from this symbol table
     * (if the key is in this symbol table).
     *
     * @param  key the key
     * @throws IllegalArgumentException if {@code key} is {@code null}
     */
    public void delete(Key key) {
        if (key == null) throw new IllegalArgumentException("argument to
        delete() is null");
        if (!contains(key)) return;
        // if both children of root are black, set root to red
        if (!isRed(root.left) && !isRed(root.right))
            root.color = RED;
        root = delete(root, key);
        if (!isEmpty()) root.color = BLACK;
        // assert check();
    }
基本性质
- 非红即黑
- 根黑
- 叶黑 e.g T.null 黑哨兵
- 红父孩子黑
- 简单路径同黑
- 右孩子不红 e.g 父黑两孩红 -> 父红两孩黑(flip); 父黑右红 -> 父左旋变红, 右孩子变黑(left-rotate)
基本操作
- 插入(插入红点, 旋转+重新着色(反色)保持红黑性质)
- 删除(删除红点, 旋转+重新着色(反色)保持红黑性质)
B Tree
t: 每个内部结点至少 t 个孩子(t-1 个 key), 至多 2t 个孩子(2t-1 个 key)
插入/删除
下溯的同时,分裂满结点
Fibonacci Heap
BST + 循环双向链表:
- 一个根树(根结点)循环双向链表
- n 个孩子循环双向链表: 每个根树的每层结点形成一个循环双向链表
K-Dimensional Tree
- 分隔空间数据
e.g 左子树:左下方 右子树:右上方
Search Algorithm
First Search
- DFS(深度优先):栈实现
- BFS(广度优先):队列实现

Cycle Detection
- 许多图论算法不适用于存在环路的复杂图,故使用循环检测剔除意外情况
处理方法:可将环路元素(如强联通分支)视作单一元素,忽视其内部结构
a = b+1;b = c+1;c = a+1;
//a extends b;b extends c;c extends a;
Dynamic Programming
- 最优解结构特征: 一个选择 + 子问题的最优解 - 所有(可重复求解)子问题的最优解可独立求解(不互相影响)
- 递归定义最优解: 列出递归表达式
- 自底向上求解最优解
- 构造最优解(额外信息数组)
子问题
- 子问题可映射为有向图, 并对其进行拓扑排序: 共有 O(n) 个子问题, 每个子问题最多 O(n) 种选择, 则算法时间复杂度为 O(n^2).其对应子问题图有 n 个顶点, 每个顶点最多有 n-1 条边.
- 递归生成可以重复求解的子问题,而不是不断生成新的子问题
范例
- 切割钢条问题: max{p[i], r[n-i]}
- 矩阵相乘链问题
- 最大公共子序列问题: r[i, j] = max{r[i, j-1], r[i-1, j]}
- 无权最短路径: path[i, j] = min{path[i, r], [r, j]}
Greedy Algorithm
- 最优解结构特征: 一个选择 + 子问题的最优解 - 所有(可重复求解)子问题的最优解可独立求解(不互相影响)
- 递归定义最优解: 列出递归表达式
- 自底向上求解最优解: 每次不进行多次选择, 只进行一次 贪心选择
- 构造最优解(额外信息数组)
Map Algorithm
图的表示
- 邻接链表法
- 邻接矩阵法
稀疏矩阵
unordered_map< int, unordered_map<int, int> > // => (row, (col, val))
广度优先遍历
BFS Node Color
- white: 未被发现/访问
- gray: 已被发现(进入队列), 邻接结点未全部发现
- black: 已被发现, 邻接结点全部发现
BFS Node Parent
广度优先树父结点
BFS Node Distance
距离 = v.pi.d + 1
利用队列实现广度优先遍历
深度优先遍历
利用 递归/栈 实现深度优先遍历
DFS Node Color
- white: 未被发现/访问
- gray: 已被发现, 未二次访问
- black: 已被发现, 二次访问(比其深的所有结点皆被发现)
当第一个访问 edge(u,v) 时:
- v.color == white: 树边
- v.color == gray : 后向边(v 为 深度优先森林的祖父结点)
- v.color == black: 前向边/横向边(v 为较深的结点/子结点)
- 无向图深度优先遍历不会出现 前向边/横向边
DFS Node Parent
比 v 浅的结点(比 v 更早被发现的结点)
DFS Node Distance
- v.d = ++time: 被发现的时间戳(入栈)
- v.f = ++time: 被二次访问的时间戳(出栈)
- time<v.d, white; v.d<time<v.f, gray: time>v.f, black
拓扑排序
目标集合: 拓扑排序后集合, 先入顶点高序, 后入顶点低序
Kahn 算法
不断将图中入度为 0 的点移入目标集合
DFS(深度优先)
当深度遍历至较深处, 并开始回溯时, 将此时访问的顶点加入目标集合(v.f 降序)
单源最短路径
void Relax(int u, int v, int w) {
    if v.d > u.d + w[u][v] {
        v.pi = u;
        v.d = v.pi.d + w[v.pi][v];
    }
}
DAG Shortest Paths
先将图进行拓扑排序(深度优先遍历), 再按照拓扑排序顺序, 依次对每个结点(拓扑排序)的邻接边进行 relax
a -> b -> c --> d, 且 a--b, a--c, b--d, c--d: relax(a, b), relax(a, c), relax(b, d), relax(c, d)
Bellman-Ford Algorithm
对每条边进行 n 次(结点总数) relax
Dijkstra Algorithm
贪心算法: 每次选取不属于 S 集合(white) 且 v.d 最小(gray)的结点, 对其所有邻接边进行 relax, 并将其加入 S 集合(black)
- white: 不属于 S 集合
- gray: 不属于 S 集合 且 v.d 最小
- black: 属于 S 集合
结点对最短路径
动态规划:l^m(i, j) = min(l^m-1(i, j), min(1<=k<=n){l^m-1(i, k)+w(k, j)})
m: 中间结点个数
Floyd-Warshall Algorithm
d^k(i, j) = w(i, j), k = 0 | min(d^k-1(i, j), d^k-1(i, k) + d^k-1(k, j)), k >= 1
pi^(i, j) = pi^k-1(i, j) or pi^k-1(k, j)
k: 中间结点个数
Matrix floyd_warshall(Matrix W) {
    int n = W.rows;
    Matrix D^0 = W;
    for (int k = 1;k < n+1; k++) {
        D^k = new Matrix(n);
        for (int i = 1; i < n+1; i++) {
            for (int j = 1; j < n+1; J++) {
                d^k[i][j] = min(d^k-1[i][j], d^k-1[i][k]+d^k-1[k][j]);
            }
        }
    }
    return D^n;
}
最大流问题
MaxFlow Problem:

最大流模型
最大流模型必须满足以下条件:
- 无双向边
- 唯一的源点 s 和 唯一的汇点 t
对于不符合该模型的问题可进行简单转化:
- 双向边: 添加额外结点, 切割双向边的其中一条, 使得双向边变成 3 条单向边
a --> b, b --> a: a --> c, c --> b, b --> a
- 多源点/汇点: 添加一个总源点/汇点
残存网络
- 若原图 u --> v 总容量 > 0, 则残存网络中 边 u --> v:剩余容量, 边 v --> u: 已用容量
- 增广路径: 残存网络中一条可行通路
最大流最小割定理
MaxFlow-MinCut Theorem:
- 切割的净流量: 流出-流入
- 切割的容量: 流出总容量(无需减流入总容量)
- 最小切割: 容量最小的切割
最大流最小割定理: 以下三个命题等价
- f 是 G 的一个最大流
- 残存网络 Gf 不含增广路径
- |f| = c(S, T)(切割的容量): |f| <= c(S, T)(流网络中任意流 f <= 任意切割容量 c(S, T))
Ford-Fulkerson Algorithm
不断寻找增广路径
Tree Edit Distance
Definition
Tree Edit Distance: 给定 Cost(edit operation) 时的最小编辑费用