一: 二叉堆,如下:
// 向下调整的方法很简单,就是把根和“较小”儿子做比较,如果根比较大,则交换它和儿子。
// 算法好比石沉大海,因此把它称为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;
}