左偏树(左偏堆),优势:两个左偏树合并效率高O(logn)
外结点:一个结点的孩子数不满两个,就称之为外结点。
距离(dist):每个节点上有个距离(dist)的属性,dist的值为该结点到最近的外结点所经过的边的个数。外结点的dist=0。空结点的dist=-1。
左偏树定义:
1.是一颗二叉树。
2.是个堆。
3.每个节点的左节点的dist >=右结点dist
根据定义,易得
左偏树性质:
- 节点的键值小于或等于它的左右子节点的键值。(堆性质)
- 节点的左子节点的距离不小于右子节点的距离。(左偏性质)
- 节点的距离等于它的右子节点的距离加1。
- 一棵N个节点的左偏树root节点的距离最多为floor(log(N+1))-1。(完全二叉树时最多)
左偏树合并:复杂度是O(logn)
//左偏树合并
struct node {//左偏树结点
int dist;
int val;
node *father;
node *left;
node *right;
}
//大致思路如下,写起来十分简单。
node *LTreeMerge(node *A,node *B){
if(A == NULL)
return B;
if(B == NULL);
return A;
if(A->dist>B->dist)//算法复杂度取决于merge函数第一个参数的dist值
retrun merge(B,A);
return merge(A,B);
}
node *merge(node *A,node *B) {
if(A == NULL)
return B;
if(B == NULL);
return A;
node *p;
p = merge(A->right,B);
if(p != NULL){
A->right = p;
if(A->left==NULL ||p->dist>A->left->dist)
swp(A->lift,A->right);//保证定义3
A->dist = A->right +1;//依据性质2
}
return A;
}
左偏树的构建:
1.通用方法,节点一个个插入。
时间复杂度 = log1+log2+log3…logn = O(nlogn)
2.和构建霍夫曼树很像,将候选节点视为n个左偏树,尽量选取dist最小的两个左偏树合并。可以通过队列实现。
时间复杂度 = O(n) 证明略了,之后会专门写一篇博客,讲一下时间复杂度的计算。
左偏树的插入,删除:
都可以看作是左偏树合并,此处不在赘述。
简述一下左偏树的意义:
左偏树合并的时候其实就是遍历根到最近的外部节点的过程。因为左偏树的结构保证了右节点dist比左结点,所以不会出现普通堆合并的极端情况:右子树极长,合并耗时接近O(n)。
每次合并的时间复杂度在O(1)~O(logn)的区间内,O(1)时,是根结点dist为0时出现。O(logn)时,是完全二叉树时出现。
优势:
1.左偏树的作用就是相对快速的堆合并,当然,它并不是最快的堆合并的数据结构,也有合并复杂度为O(1)的。
2.左偏树实现十分简单。