可以参见《左偏树的特点及其应用》这篇论文讲的很详细.
定义堆的左偏性质:
若dis(i)表示i节点到叶子节点的最短距离,对于左偏树上的任意节点,都有:
dis(i)=dis(right_son (i)), dis(left_son(i)) >= dis(right_son(i)).显然左偏树的
dis(i)就是根节点最右路径的长度.(定性看就是一棵任意一个节点左边都比右边
看上去大的树)
左偏树是一种堆,满足堆的性质和左偏的性质,并且能够在O(lgn)时间内完成
两棵左偏树的合并.
左偏树的操作有:合并(merge),删除堆顶(pop),插入一个key值(insert),取
堆顶key值(top).所有操作的关键是merge.
merge操作的算法很简单:
node *merge (node *a, node *b) {//合并两棵左偏树
if (a == NULL) return b;
if (b == NULL) return a;
if (a->key < b->key) swap (a, b);
a->r = merge (a->r, b);
if (dis (a->r) > dis (a->l)) swap (a->l, a->r);
a->dis = dis (a->r) + 1;
return a;
}
其他操作可以转化为merge:
pop:将堆顶的左右子树merge后删除堆顶;
insert:将key值建立一个单节点左偏树后merge;
top:直接返回堆顶的key值.
重载一下<比较符就可以随便改成大顶堆或者小顶堆或者key值为其他
数据结构的左偏树了.
模板:
struct node {//左偏树节点
int key, dis;
node *l, *r;
bool operator < (const node &b) const {
return key > b.key;
}
};
node *tree[maxn];
void init (node *&t, int key) {
t = new node;
t->l = t->r = NULL;
t->key = key;
t->dis = 0;
}
int dis (node *a) {//计算左偏树的dis值
if (a == NULL)
return -1;
return a->dis;
}
node *merge (node *a, node *b) {//合并两棵左偏树
if (a == NULL) return b;
if (b == NULL) return a;
if (*b < *a) swap (a, b);
a->r = merge (a->r, b);
if (dis (a->r) > dis (a->l)) swap (a->l, a->r);
a->dis = dis (a->r) + 1;
return a;
}
node *pop (node *a) {//删除左偏树key值最小的节点
if (!a) return NULL;
return merge (a->l, a->r);
}
node *insert (node *root, int num) {//左偏树中插入key值为num的节点
node *p;
init (p, num);
return merge (root, p);
}
int top (node *a) {//去取左偏树key值最大的元素
return a->key;
}