左偏树,也可以称之为左式堆。称其为树,是因为其存储结构通常采用二叉树,所以可以认为是一种特殊的二叉树。称其为堆,是因为在逻辑结构上,它属于可合并堆的一种。其实数据结构中最欣欣向荣的两个分支就是:平衡树 和可合并堆。高级树结构的核心都是围绕如何使树到达平衡而展开,高级堆结构的核心就是如何有效地进行合并。
这里先介绍可合并堆中一种十分好用的数据结构:左式堆。人们熟悉或者常听说的堆主要有:(二叉)堆、左式堆、斜堆、二项堆、斐波纳契堆。其中二叉堆是最为常用的,一般的优先队列都可以考虑用二叉堆实现,而STL中优先队列,更是方便了大家的使用,如果自己编程主要注意sift down 和 sift up 这两个核心操作的实现。
二叉堆 | 左式堆 | 斜堆 | 二项堆 | 斐波纳契堆 | |
Insert | O(log n) | O(log n) | O(log n) | O(log n) | O(1) |
Extract-Min | O(log n) | O(log n) | O(log n) | O(log n) | O(log n) |
merge | O(m + n) | O(log n) | O(log n) | O(log n) | O(1) |
编程复杂度 | 简单 | 较简单 | 较简单 | 较复杂 | 复杂 |
/*基本的存储结构*/ struct LTNode { int data; int dist; struct LTNode *lchild; struct LTNode *rchild; }; /*交换操作,为了方便后面的实现*/ template<typename T> inline void swap(T &a,T &b) { T tmp = a; a = b; b = tmp; } /*简单的中序遍历操作,方便调试用的*/ void InOrder(LTNode *t) { if(t) { InOrder(t->lchild); printf("%d\n",t->data); InOrder(t->rchild); } } /*核心操作,一定要小心实现*/ LTNode* merge(LTNode* &A,LTNode* &B) { if(A==NULL || B==NULL) return A==NULL?B:A; if(A->data > B->data) /*确保B->data >= A->data*/ { swap<LTNode*>(A,B); } A->rchild = merge(A->rchild,B); /*新来个左偏树始终合并到右侧*/ /*由于新结点合并到右侧,右侧结点现在一定存在了,但左侧不一定*/ if(A->lchild==NULL || /*左侧为空,一定小于右侧*/ A->rchild->dist > A->lchild->dist)/*右侧大于了左侧*/ swap<LTNode*>(A->lchild,A->rchild); if(A->rchild==NULL){A->dist = 0;} /*右子树为空*/ else {A->dist = A->rchild->dist + 1;} return A; } void insert(LTNode *&t,int x) { /*initiate the node*/ LTNode *p = new LTNode; p->data = x; p->dist = 0; p->lchild = NULL; p->rchild = NULL; /*merge into a leftist tree*/ t = merge(t,p); printf("insert %d success\n",x); } LTNode* ExtractMin(LTNode* &t) { /*根元素始终都是最小的元素*/ LTNode *p = t; /*合并根的左右子树*/ t = merge(t->lchild,t->rchild); /*返回指向最小节点的指针*/ return p; }