堆之左偏树

左偏树(左偏堆),优势:两个左偏树合并效率高O(logn)

外结点:一个结点的孩子数不满两个,就称之为外结点。

距离(dist):每个节点上有个距离(dist)的属性,dist的值为该结点到最近的外结点所经过的边的个数。外结点的dist=0。空结点的dist=-1。

左偏树定义:

1.是一颗二叉树。

2.是个堆。

3.每个节点的左节点的dist >=右结点dist

 

根据定义,易得

左偏树性质:

  1. 节点的键值小于或等于它的左右子节点的键值。(堆性质)
  2. 节点的左子节点的距离不小于右子节点的距离。(左偏性质)
  3. 节点的距离等于它的右子节点的距离加1。
  4. 一棵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.左偏树实现十分简单。

点赞