一: 二叉堆,如下:
// 向下调整的方法很简单,就是把根和“较小”儿子做比较,如果根比较大,则交换它和儿子。 // 算法好比石沉大海,因此把它称为sink(下沉)过程 void sink(int p) { int child = p << 1, tmp = heap[p]; while(child <= n){ // 找出较小儿子 if(child < n && heap[child+1] < heap[child]) ++child; if(heap[child] >= tmp) break; heap[p] = heap[child]; p = child; child = p << 1; } // 放到合适位置 heap[p] = tmp; } // 先用最后一个元素代替根元素,之后执行下沉操作 int deleteMin() { int iret = heap[1]; heap[1] = heap[n--]; sink(1); return iret; } // 向上调整只需要比较结点和父亲,比向下调整更容易。 // 算法好比从海底游回水面,因此称为swim过程 void swim(int idx) { int tmp = heap[idx], p = idx >> 1; while(p && tmp < heap[idx]){ heap[idx] = heap[p]; idx = p; p = idx >> 1; } heap[idx] = tmp; } // 先插入到最后,然后执行上浮过程 void insert(int x) { heap[++n] = x; swim(n); } // 给n个整数,构造一个二叉堆可以一个一个插入,但是有更好的方法: // 从下往上一层一层向下调整。由于叶子无需调整,因此只需要从[n/2] // 开始递减到1,时间复杂度为O(n) void build() { for(int i = n/2; i > 0; i--) sink(i); }
二: 查并集,如下:
// 为了解释并查集的思想,假设:集合中的每个元素是不重合的,用p[i]表示i的父亲, // 如果p[i] == i 说明i本身是根,而rank[k]表示根节点为k的集合的某种性质(元素数量、 // 深度等) void makeSet(int x) { p[x] = x; } // 找到包含此元素的集合的根元素 ,并进行路径压缩优化 // 使今后的操作比较快 int findSet(int x) { int t = x, k; while(t != p[t]) t = p[t]; // find root while(x != t){ // path compression k = p[x]; p[x] = t; x = k; } return t; } void unionSet(int x, int y) { int Rx = findSet(x); int Ry = findSet(y); // 启发式合并优化 if(rank[Rx] > rank[Ry]) rank[Ry] = Rx; else rank[Rx] = Ry; }