左偏树介绍
2016-06-27 23:05
260 查看
左偏树是一种优先队列,虽然有些简陋,但它可以比较高效的实现队列的合并操作,所以在一些涉及到最值,以及合并的问题中,不妨考虑下这种数据结构。
就开门见山了,下方是左偏树的结构体代码(我自己喜欢这种把数据放在结构体,并用一个数组提前开好内存的做法啦,当然你也可以用指针,也可以把这些数据分开放在不同的数组里,一个数据结构,只要可以实现就好了,实现的方法就是看使用者的习惯了)
结合图片:(图片来自百度百科,侵删)
![](http://img.blog.csdn.net/20160627232956546)
可以看出这是一棵二叉树,且根据它优先级越大,越接近根节点的性质(优先级最大的点就是根节点),可看出这也是一个堆。
图中的小圆内的数字就是结构体里的v(可以看出这颗二叉树的节点v值越小,优先级越大)。
小圆旁蓝色的数字就是h(貌似大家都喜欢叫这个d,距离的意思,但是自己就是觉得这个数字当做高度更好理解额),h的定义是这样的:
如果一个节点没有右节点,那么这个节点的高度是0,(一棵树下面没有节点了当然就在地面上,就没有高度了,真是矛盾百出的比喻,=_=)
如果有右节点,那么这个右节点的高度是右节点的高度加一。
好了,我们知道这个数据结构了,所以来造一棵树吧……
造树代码:
恩恩,当我们想找到一棵树的根时,可以用这个代码:
接下来,就是合并两棵左偏树了
先放图好了:(图片来源:http://www.cnblogs.com/yc_sunniwell/archive/2010/06/28/1766756.html,这也是一篇介绍左偏树的博客,侵删啦)
![](http://img.blog.csdn.net/20160628000637062)
![](http://img.blog.csdn.net/20160628000651478)
![](http://img.blog.csdn.net/20160628000705885)
从图中可看出合并两个左偏树的大致过程是这样的:
先比较顶点的优先级,优先级大的作为被插入树,小的作为插入树。
把被插入树的右边拿出,与插入树进行一个完整的插入过程,这个过程结束后,得到一个新的左偏树,作为被插入树的右子树(新树的顶点作为被插入树顶点的右顶点)。(可以看出这个插入是一个不断迭代的过程,就像dfs一样)
比较新树的左右顶点的高度(左右子树的高度),使高度较大的顶点(子树)作为左顶点(左子树)
根据右子树重新确定根节点的高度(其实自己一直想吐槽自己硬把别人叫做距离的东西叫做高度,没办法,自己改的东西,跪着也要叫完!)
毕竟图是别人的(懒惰且恶劣的自己……),可能你看到这里没怎么看懂,所以上代码吧:
[b]从代码里我们可以看出来,这棵树有着一种左偏的性质(喂喂,说什么啊),不妨看看图吧,一般情况下,树的左边要比右边重(顶点更多),右边的顶点更少,为什么呢?由于插入都是往右插入的,而且右边的顶点数不会超过n/2,那么插入的时间复杂度不会超过O(log n/2),而且,子树的右顶点为空的概率也很大,这样就可以保证比较高效的完成两个优先队列的合并。
接下来说一下pop操作
额,至少这个比std::priority_queue的pop好一点
(其实这个代码不是很严谨,因为被pop顶点的上一个顶点还存有这个顶点的信息,但是如果我们使用了前面说到的第二个find()代码,查找顶点速度可以很快,上一个顶点的信息却丢失了,这个信息是无法被修改的。不过一道题目可能并不会要求我们实现如此严谨的功能,毕竟不是内置的数据结构,所以仔细分析题目的要求,确定需要实现的功能才是关键)
接下来放两道题,一题很水,一题一点也不水……
HDU 1512 Monkey King 题解
HDU 5575 Discover Water Tanky 题解
就开门见山了,下方是左偏树的结构体代码(我自己喜欢这种把数据放在结构体,并用一个数组提前开好内存的做法啦,当然你也可以用指针,也可以把这些数据分开放在不同的数组里,一个数据结构,只要可以实现就好了,实现的方法就是看使用者的习惯了)
struct Heap{ int l,r,f,v,h; //l:左节点的编号 //r:右节点的编号 //f:父节点的编号 //v:优先级(这个数值作为优先队列的排序依据) //h:这个节点的高度(下面会有说明) };
结合图片:(图片来自百度百科,侵删)
可以看出这是一棵二叉树,且根据它优先级越大,越接近根节点的性质(优先级最大的点就是根节点),可看出这也是一个堆。
图中的小圆内的数字就是结构体里的v(可以看出这颗二叉树的节点v值越小,优先级越大)。
小圆旁蓝色的数字就是h(貌似大家都喜欢叫这个d,距离的意思,但是自己就是觉得这个数字当做高度更好理解额),h的定义是这样的:
如果一个节点没有右节点,那么这个节点的高度是0,(一棵树下面没有节点了当然就在地面上,就没有高度了,真是矛盾百出的比喻,=_=)
如果有右节点,那么这个右节点的高度是右节点的高度加一。
好了,我们知道这个数据结构了,所以来造一棵树吧……
造树代码:
void init(int v){ heap[++tot].v=v; heap[tot].l=heap[tot].r=heap[tot].h=0; heap[tot].f=tot; //其实自己感觉这代码想怎么写怎么写啊,自己写一个init还可能误导读者 //反正大家明白大概是这么回事就好了,世间没有绝对的事,别拿这个代码当标准 }看到f值的初始化方法,是不是有一点并查集的感觉?
恩恩,当我们想找到一棵树的根时,可以用这个代码:
int find(int x){ heap[x].f==x?x:find(heap[x].f); } //为了让多次查找的时间复杂度变为O(1),可以这样写 int find(int x){ heap[x].f==x?x:heap[x].f=find(heap[x].f); } //完全就是并查集的写法额极快地返回优先级最高的数据,这就是一个优先队列了。
接下来,就是合并两棵左偏树了
先放图好了:(图片来源:http://www.cnblogs.com/yc_sunniwell/archive/2010/06/28/1766756.html,这也是一篇介绍左偏树的博客,侵删啦)
从图中可看出合并两个左偏树的大致过程是这样的:
先比较顶点的优先级,优先级大的作为被插入树,小的作为插入树。
把被插入树的右边拿出,与插入树进行一个完整的插入过程,这个过程结束后,得到一个新的左偏树,作为被插入树的右子树(新树的顶点作为被插入树顶点的右顶点)。(可以看出这个插入是一个不断迭代的过程,就像dfs一样)
比较新树的左右顶点的高度(左右子树的高度),使高度较大的顶点(子树)作为左顶点(左子树)
根据右子树重新确定根节点的高度(其实自己一直想吐槽自己硬把别人叫做距离的东西叫做高度,没办法,自己改的东西,跪着也要叫完!)
毕竟图是别人的(懒惰且恶劣的自己……),可能你看到这里没怎么看懂,所以上代码吧:
int merge(int a,int b){ if(a==0) return b; if(b==0) return a; if(heap[a].v<heap.v) swap(a,b); heap[a].r=merge(b,heap[a].r); heap[heap[a].r].f=a; if(heap[heap[a].l].h<heap[heap[a].r].h) swap(heap[a].l,heap[a].r); if(heap[a].r==0) heap[a].h=0; else heap[a].h=heap[heap[a].r].h+1; return a; }
[b]从代码里我们可以看出来,这棵树有着一种左偏的性质(喂喂,说什么啊),不妨看看图吧,一般情况下,树的左边要比右边重(顶点更多),右边的顶点更少,为什么呢?由于插入都是往右插入的,而且右边的顶点数不会超过n/2,那么插入的时间复杂度不会超过O(log n/2),而且,子树的右顶点为空的概率也很大,这样就可以保证比较高效的完成两个优先队列的合并。
接下来说一下pop操作
额,至少这个比std::priority_queue的pop好一点
int pop(int x){ int l=node[x].l; int r=node[x].r; node[l].f=l; node[r].f=r; node[x].h=node[x].l=node[x].r=0; return merge(x,merge(l,r)); }返回的是被pop顶点和它的子节点组成的左偏树。
(其实这个代码不是很严谨,因为被pop顶点的上一个顶点还存有这个顶点的信息,但是如果我们使用了前面说到的第二个find()代码,查找顶点速度可以很快,上一个顶点的信息却丢失了,这个信息是无法被修改的。不过一道题目可能并不会要求我们实现如此严谨的功能,毕竟不是内置的数据结构,所以仔细分析题目的要求,确定需要实现的功能才是关键)
接下来放两道题,一题很水,一题一点也不水……
HDU 1512 Monkey King 题解
HDU 5575 Discover Water Tanky 题解
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)