左偏树是一种常用的优先队列(堆)结构。与二叉堆相比,左偏树可以高效的实现两个堆的合并操作。

左偏树实现方便,编程复杂度低,而且有着不俗的效率表现。

它的一个常见应用就是与并查集结合使用。利用并查集确定两个元素是否在同一集合,利用左偏树确定某个集合中优先级最高的元素。

 #include <cstdio>
 #include <cstring>
 #include <algorithm>

 template <class T>
 struct HeapNode
 {
     typedef HeapNode<T> Node;
     Node* lch;
     Node* rch;
     T val;
     int dist;

     HeapNode(),rch(),val(_val),dist() {}

     void clear()
     {
         if(lch) lch->clear();
         if(rch) rch->clear();
         delete this;
     }
 };

 template <class T,class Comp>
 struct LeftistHeap
 {
     typedef HeapNode<T> Node;
     typedef LeftistHeap<T,Comp> Heap;

     Node* root;
     Comp cmp;

     LeftistHeap():root() {}
     ~LeftistHeap()
     {
         clear();
     }

     void clear()
     {
         if(root) root->clear();
         root=;
     }

     Node* merge(Node* A,Node* B)
     {
         if(!A) return B;
         if(!B) return A;

         if(cmp(B->val,A->val)) std::swap(A,B);
         A->rch=merge(B,A->rch);

         if(!A->lch || A->rch->dist > A->lch->dist)
             std::swap(A->lch,A->rch);
         A->dist = (A->rch) ? A->rch->dist +  :  ;

         return A;
     }

     void push(const T& _val)
     {
         Node* nNode=new Node(_val);
         root=merge(root,nNode);
     }

     Heap& operator << (const T& _val)
     {
         push(_val);
         return *this;
     }

     T top()
     {
         return root->val;
     }

     void pop()
     {
         Node* temp=root;
         root=merge(temp->lch,temp->rch);
         delete temp;
     }

     Heap& operator >> (T& _dest)
     {
         _dest=top();
         pop();
         return *this;
     }

     void merge(Heap& _other)
     {
         this->root=merge(this->root,_other.root);
         _other.root=;
     }

     bool empty()
     {
         ;
     }
 };

Leftist Heap

定义左偏树节点的“距离”(dist)为从其右子树开始,一直向右走的路径总长。特别地,若某个节点没有右孩子,其dist值为0。

树中的每个节点都必须满足左孩子的dist值不小于右孩子(如果有的话)的dist值。

和大多数可并堆一样,左偏树的核心操作就是合并(Merge)操作。

(以下伪代码以小根堆为例,节点的数据域记为val)

function merge(Node* A,Node* B)

if(A和B中某一个为空) return 另一个   //特判,同时也是递归终止的条件

交换A和B(如果需要的话),使得A的val小于B的val

A->rch = merge(B,A->rch)

if(A的左孩子的dist小于右孩子的dist或A的左孩子不存在) 交换A的左、右孩子

根据A的右孩子更新A的dist

return A

实现细节详见代码。

有了合并操作,其他的也就水到渠成了:

插入(push):建立一个新节点,然后把它视为一个左偏树,将其与已有的合并。

删除(pop):删除其根节点,合并原先根节点的左右孩子。

附一道左偏树+并查集的练习题:

 #include <cstdio>
 #include <cstring>
 #include <algorithm>

 template <class T>
 struct HeapNode
 {
     typedef HeapNode<T> Node;
     Node* lch;
     Node* rch;
     T val;
     int dist;

     HeapNode(),rch(),val(_val),dist() {}

     void clear()
     {
         if(lch) lch->clear();
         if(rch) rch->clear();
         delete this;
     }
 };

 template <class T,class Comp>
 struct LeftistHeap
 {
     typedef HeapNode<T> Node;
     typedef LeftistHeap<T,Comp> Heap;

     Node* root;
     Comp cmp;

     LeftistHeap():root() {}
     ~LeftistHeap()
     {
         clear();
     }

     void clear()
     {
         if(root) root->clear();
         root=;
     }

     Node* merge(Node* A,Node* B)
     {
         if(!A) return B;
         if(!B) return A;

         if(cmp(B->val,A->val)) std::swap(A,B);
         A->rch=merge(B,A->rch);

         if(!A->lch || A->rch->dist > A->lch->dist)
             std::swap(A->lch,A->rch);
         A->dist = (A->rch) ? A->rch->dist +  :  ;

         return A;
     }

     void push(const T& _val)
     {
         Node* nNode=new Node(_val);
         root=merge(root,nNode);
     }

     Heap& operator << (const T& _val)
     {
         push(_val);
         return *this;
     }

     T top()
     {
         return root->val;
     }

     void pop()
     {
         Node* temp=root;
         root=merge(temp->lch,temp->rch);
         delete temp;
     }

     Heap& operator >> (T& _dest)
     {
         _dest=top();
         pop();
         return *this;
     }

     void merge(Heap& _other)
     {
         this->root=merge(this->root,_other.root);
         _other.root=;
     }

     bool empty()
     {
         ;
     }
 };

 #include <functional>

 ;

 int N,M;
 int idx[maxN];

 int father(int x)
 {
     return idx[x]==x ? x : idx[x]=father(idx[x]) ;
 }

 LeftistHeap<int,std::greater<int> > heap[maxN];

 void init()
 {
     ;i<maxN;i++) heap[i].clear();
     ;i<maxN;i++) idx[i]=i;
 }

 bool solve()
 {
     init();

     if(scanf("%d",&N)==EOF) return false;
     ;i<=N;i++)
     {
         int s; scanf("%d",&s);
         heap[i].push(s);
     }

     scanf("%d\n",&M);
     while(M--)
     {
         int mk1,mk2;
         scanf("%d%d",&mk1,&mk2);

         int f1=father(mk1);
         int f2=father(mk2);
         if(f1==f2)
         {
             printf("-1\n");
             continue;
         }

         int s1,s2;
         heap[f1]>>s1;
         heap[f2]>>s2;

         if(f1<f2)
         {
             idx[f2]=f1;
             heap[f1].merge(heap[f2]);
             );
             ));
             heap[f1] << (s1>>) << (s2>>);
         }
         else
         {
             idx[f1]=f2;
             heap[f2].merge(heap[f1]);
             );
             ));
             heap[f2] << (s1>>) << (s2>>);
         }
     }

     return true;
 }

 int main()
 {
     while(solve());
     ;
 }

Problem:ZOJ P2334

左偏树(Leftist Heap/Tree)简介及代码的更多相关文章

  1. 『左偏树 Leftist Tree』

    新增一道例题 左偏树 Leftist Tree 这是一个由堆(优先队列)推广而来的神奇数据结构,我们先来了解一下它. 简单的来说,左偏树可以实现一般堆的所有功能,如查询最值,删除堆顶元素,加入新元素等 ...

  2. 【BZOJ 1367】 1367: [Baltic2004]sequence (可并堆-左偏树)

    1367: [Baltic2004]sequence Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Ou ...

  3. 左偏树 / 非旋转treap学习笔记

    背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...

  4. 浅谈左偏树在OI中的应用

    Preface 可并堆,一个听起来很NB的数据结构,实际上比一般的堆就多了一个合并的操作. 考虑一般的堆合并时,当我们合并时只能暴力把一个堆里的元素一个一个插入另一个堆里,这样复杂度将达到\(\log ...

  5. [note]左偏树(可并堆)

    左偏树(可并堆)https://www.luogu.org/problemnew/show/P3377 题目描述 一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 ...

  6. Monkey King(左偏树 可并堆)

    我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...

  7. P3378 【模板】堆 (内含左偏树实现)

    P3378 [模板]堆 题解 其实就是一个小根堆啦,STL就可以解决,但是拥有闲情雅致的我学习了Jelly_Goat的左偏树,增加了代码长度,妙啊 Solution 1 STL STL 里面prior ...

  8. 左偏树初步 bzoj2809 & bzoj4003

    看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...

  9. k短路模板(洛谷P2483 [SDOI2010]魔法猪学院)(k短路,最短路,左偏树,priority_queue)

    你谷数据够强了,以前的A*应该差不多死掉了. 所以,小伙伴们快来一起把YL顶上去把!戳这里! 俞鼎力的课件 需要掌握的内容: Dijkstra构建最短路径树. 可持久化堆(使用左偏树,因其有二叉树结构 ...

随机推荐

  1. Java基础(二十三)GUI图形界面编程(Java基础完)

    这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...

  2. Android学习笔记(十三)SharedPreference必须掌握的基础

    我们在开发中,应用程序会保存少量数据,例如一些字符串.一些标记或者一些配置文件,这时候如果去使用SQLite保存这些数据的话,难免会显得大材小用,用起来也不方便,对于这种信息,保存在SharedPre ...

  3. NIR相机

    近红外(NIR)相机——专为低照度环境而设计的高灵敏度相机 近红外光”(Near-Infrared) 是介于可见光和中红外光间的电磁波,因此是不能被人眼所察觉到的.近红外优化工业相机广泛适用于交通监控 ...

  4. 微信、QQ这类IM App怎么做——谈谈Websocket

    前言 关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作.直到最近换了工作,到了一家是含有IM社交聊天功能的app的时候,我觉得我现在可以谈谈 ...

  5. PL/SQL练习

    简单结构 set serveroutput on; DECLARE v_name ); BEGIN v_name := 'hello'; dbms_output.put_line(v_name); E ...

  6. String+,StringBuilder,String.format运行效率比较

    实现String字符串相加的方法有很多,常见的有直接相加,StringBuilder.append和String.format,这三者的运行效率是有差异的,String是final类型的,每次相加都会 ...

  7. 如何使用VIM的Help

    很多时候在用到vim的命令的时候,都会去网上搜索,殊不知,如果熟练使用VIM的help,可以达到事半功倍的效果. 下面介绍如何使用VIM的help: 1.      在vim的一般模式中输入:help ...

  8. ASP.NET多线程下使用HttpContext.Current为null解决方案

    多线程或者异步调用中如何访问HttpContext? 前面我还提到在APM模式下的异步完成回调时,访问HttpContext.Current也会返回null,那么此时该怎么办呢? 答案有二种:1. 在 ...

  9. Making the Elephant Dance: Strategic Enterprise Analysis

    http://www.modernanalyst.com/Resources/Articles/tabid/115/ID/2934/categoryId/23/Making-the-Elephant- ...

  10. Java-SSI框架学习

    框架简介: 相信大家对于mvc的三层架构已经灰常熟悉了,在这就不细讲了,个人感觉ssi的框架结构还是比较典型的mvc三层架构,还是比较容易上手的.关于这块的入门我想特别感谢下FrankHui童鞋,在他 ...