学习Treap
其实很久以前就有写这样一篇文章的想法,由于时间问题没有实现。停课集训马上就要开始,时间稍稍充裕,终于有机会写本文。本文不打算介绍Treap的各种细节,你可以在这里找到详细的教程。而本文仅仅列出一个大纲,以日记的方式简单地介绍Treap,作为自己的一个备忘,也希望能给初学者提供一个参考。
Treap=Tree+Heap。Treap本身是一棵二叉搜索树,它的左子树和右子树也分别是一个Treap,和一般的二叉搜索树不同的是,Treap纪录一个额外的数据,就是优先级。Treap在以关键码构成二叉搜索树的同时,还满足堆的性质(在这里我们假设节点的优先级大于该节点的孩子的优先级)。
如果您是一个初学者,在读下面的文字的时候,请同时参阅NOCOW上的Treap介绍。
简单的说,Treap通过给予每个节点一个附加的随机优先级,在保证Treap数据域的排序二叉树性质的前提下,使节点的优先级同时满足堆的性质。经过种种的分析证明,在一般情况下这样做可以让Treap维持平衡。
和大多数平衡树一样,Treap的操作用到了旋转。从图(参见NOCOW上的Treap介绍)上看出,右旋即将该节点的左子节点提升到根,左旋则相反。你可以用这样的方法来记住。
插入方法:如果当前节点为空,则新建节点,加入数据,设置特征值。否则若插入数小于当前节点,插入其左子树;如果不是,就插入右子树。插入后,要根据情况旋转,来保证堆的性质。
删除方法:这是比其他平衡树树好写得多的操作。首先找到要删除的节点。如果它没有子节点,则将自己删除。若只有一个子树,那么用这个子树来代替自己。如果有两个子树,那么选择优先级比较大的,将其转到根,然后才删除要删的节点所在的子树。举例来说,如果要删除当前节点,它有两个子树,其中左子树优先级比较大,那么右旋,递归地删除其右子树。
查找:和普通的排序二叉树一样。
分离合并等,都是通过虚拟节点来实现的。由于我没有实践过,所以不敢乱讲。
NOI2004的郁闷的出纳员和2006年的生日快乐是练习平衡树的很好的题目。当然如果您才刚入门,不妨用它来写一个排序,来检验其正确性。
看到有人求代码,我来贴一个吧。很丑很长,不过还算比较好写。
#include <iostream> #include <cassert> using namespace std; struct Treap { Treap *lSon , *rSon; int key; unsigned rank; }; void leftRoutate( Treap*& cur ) { assert( cur -> rSon ); Treap* t = cur -> rSon; cur -> rSon = t -> lSon; t -> lSon = cur; cur = t; } void rightRoutate( Treap*& cur ) { assert( cur -> lSon ); Treap* t = cur -> lSon; cur -> lSon = t -> rSon; t -> rSon = cur; cur = t; } void ins( Treap*& cur , int key ) { if ( cur == NULL ) { cur = new Treap; cur -> lSon = cur -> rSon = NULL; cur -> key = key; cur -> rank = rand(); } else { if ( key <= cur -> key ) { ins( cur -> lSon , key ); if ( cur -> lSon -> rank > cur -> rank ) { rightRoutate( cur ); } } else { ins( cur -> rSon , key ); if ( cur -> rSon -> rank > cur -> rank ) { leftRoutate( cur ); } } // 维护附加域 } } bool findKey( Treap*& cur , int key ) { if ( cur == NULL ) { return false; } else { if ( key == cur -> key ) return true; if ( key < cur -> key ) { return findKey( cur -> lSon , key ); } else { return findKey( cur -> rSon , key ); } } } void del( Treap*& cur , int key ) { if ( cur == NULL ) return; if ( key < cur -> key ) { del( cur -> lSon , key ); return; } if ( key > cur -> key ) { del( cur -> rSon , key ); return; } if ( cur -> lSon == NULL && cur -> rSon == NULL ) { delete cur; cur = NULL; return; } if ( cur -> lSon == NULL && cur -> rSon != NULL ) { Treap* t = cur; cur = cur -> rSon; delete t; return; } if ( cur -> lSon != NULL && cur -> rSon == NULL ) { Treap* t = cur; cur = cur -> lSon; delete t; return; } if ( cur -> lSon != NULL && cur -> rSon != NULL ) { if ( cur -> lSon -> rank > cur -> rSon -> rank ) { rightRoutate( cur ); del( cur -> rSon , key ); } else { leftRoutate( cur ); del( cur -> lSon , key ); } return; } } void midWalk( Treap* cur ) { if ( cur ) { midWalk( cur -> lSon ); cout << cur -> key << " "; midWalk( cur -> rSon ); } } int minimum( Treap* cur ) { while( cur -> lSon ) cur = cur -> lSon; return cur -> key; } int maximun( Treap* cur ) { while( cur -> rSon ) cur = cur -> rSon; return cur -> key; } Treap* root; int main() { int opt; cout << "1.Insert\n2.midWalk\n3.Delete\n4.Find\n5.getMin\n6.getMax\n0.Exit\n" << endl; cin >> opt; while ( opt != 0 ) { if ( opt == 1 ) { int t; cin >> t; ins( root , t ); } if ( opt == 2 ) { midWalk( root ); cout << endl; } if ( opt == 3 ) { int t; cin >> t; del( root , t ); } if ( opt == 4 ) { int x; cin >> x; cout << ( findKey( root , x ) ? "Yes" : "No" ) << endl; } if ( opt == 5 ) { cout << minimum( root ) << endl; } if ( opt == 6 ) { cout << maximun( root ) << endl; } cin >> opt; } } |

评论
一条评论 到 “学习Treap”引用
看看别人是怎么评论这个日志的...[...] 平衡树,Treap,见这里。 [...]