二叉排序树是一种动态排序的数据结构。支持插入、删除、查找等操作。且平均时间复杂度为O(log(N)),可是普通二叉排序树不能保证树退化为一颗分支的情况,此时最坏情况下的时间复杂度为O(N)。
此时,平衡二叉树的产生了。平衡二叉树是一种动态调整平衡的数据结构,但理想的平衡二叉树非常难,于是人们使用AVL、红黑树、Treap、伸展树等来替代平衡二叉树,这些数据结构能够非常好地改善最坏情况。但实现起来并非非常easy的事。
Treap是Heap+Tree,通俗来讲就是堆与树的结合,每一个节点除了keywordkey以及节点之间的连接关系外,还要保存一个优先级priority,注意这里的优先级不同于其它节点的随机数字,随机才干较好的期望平衡度,保证能够非常好维护二叉树的性质。当中在Treap中keywordkey遵循二叉查找树的性质,即:
(1)、空树是一颗二叉查找树。
(2)、若左子树不为空,则左子树中的所有keyword均小于该节点keyword;
(3)、若右子树不为空,则右子树中的所有keyword均不小于该节点keyword。
(4)、若左右子树非空。则左右子树也是一颗二叉查找树。
Treap中的优先级priority遵循Heap的性质,此处的堆已不再是棵全然二叉树,而是用指针取代数组下标实现的树结构,满足全部根节点优先级小于(或大于)左右孩子节点的优先级(在左右孩子存在的情况下,小于为小顶堆,大于为大顶堆)。
因为每一个节点上的优先级是随机产生的,所以期望高度能够达到非常好的程度。
在算法导论的12.4节中,其证明了随机构造的二叉查找树的期望高度为O(lgn)。因而treap的期望高度亦是O(lgn)。
Treap支持二叉树的全部操作,且平摊时间复杂度为O(log(N)),便于实现,性能好,克服了二叉查找树最坏情况时间复杂度为O(N)的弊端,有不存在AVL、红黑树等编码和理解难度,与伸展树有类似优点。
以下简要谈谈Treap的几种基本操作。
左旋:
右旋:
以上截图来自http://www.cnblogs.com/huangxincheng/archive/2012/07/30/2614484.html。在此表示感谢!
查找:
查找直接用二叉树的性质,參考源码。
插入:
先找到插入位置,在运行插入操作,然后维护堆的性质,即依据优先级调整以维护小顶堆或大顶堆的性质。关于维护队的性质,參考我前面的关于对的博客。
删除:
类比二叉查找树一样,删除结点存在三种情况:
(1)、叶子结点,跟二叉查找树一样,直接释放本节点就可以。
(2)、仅仅有一个还自己节点,跟二叉查找树一样操作。
(3)、有两个孩子节点,能够像二叉查找树一样转换为删除右子树最小节点或左子树最大节点,然后将仅仅赋值到本应删除的节点处,此时能够一起复制优先级,然后向该分支调整,也可不赋值优先级保留原来的优先级,我的程序保留原来优先级。
更新:
先删除原节点,然后插入新节点。
因为期望高度为O(log(N))。所以以上全部操作的平潭时间复杂度为O(log(N))。
代码:
之前写了个带父亲指针的。感觉有点复杂,于是写了以下这段代码。查看带父亲指针的代码点击此处,感觉那个封装的比較好。
#include<iostream> #include<ctime> using namespace std; class Treap { private: int num,key,pri; Treap *child[2]; public: int compare(int); Treap(int); Treap* rotate(Treap*,int); Treap* erase(Treap*,int); Treap* leafNode(); bool find(int); void setNum(); Treap* insert(Treap*,int); void rotatePrint(Treap*,int); }; Treap::Treap(int tKey) { num=0; key=tKey; pri=rand()%1000; child[0]=child[1]=NULL; } int Treap::compare(int tKey) { return key<tKey?1:0; } void Treap::setNum() { num=1; if(child[0]!=NULL) num+=child[0]->num; if(child[1]!=NULL) num+=child[1]->num; } Treap*Treap::rotate(Treap* tRoot,int dir) { Treap* ch=tRoot->child[dir]; tRoot->child[dir]=ch->child[dir^1]; ch->child[dir^1]=tRoot; tRoot->setNum(); ch->setNum(); return ch; } Treap* Treap::insert(Treap *tRoot,int tKey) { if(NULL==tRoot) return new Treap(tKey); int dir=this->compare(tKey); tRoot->child[dir]=tRoot->child[dir]->insert(tRoot->child[dir],tKey); if(tRoot->pri>tRoot->child[dir]->pri) tRoot=rotate(tRoot,dir); else tRoot->setNum(); return tRoot; } Treap* Treap::leafNode() { if(NULL==child[0]&&NULL==child[1]) { delete this; return NULL; } else if(NULL==child[0]) { Treap *p=this->child[1]; delete this; return p; } else if(NULL==child[1]) { Treap *p=this->child[0]; delete this; return p; } } Treap* Treap::erase(Treap* tRoot,int tKey) { if(NULL==tRoot) return NULL; if(key==tKey) { if(child[0]==NULL||child[1]==NULL) return leafNode(); Treap *q=this,*p=this->child[1]; bool left=false; while(p->child[0]!=NULL) { left=true; q=p; p=p->child[0]; } this->key=p->key; if(!left) q->child[1]=p->leafNode(); else q->child[0]=p->leafNode(); return this; } int dir=this->compare(tKey); tRoot->child[dir]=tRoot->child[dir]->erase(tRoot->child[dir],tKey); return tRoot; } bool Treap::find(int tKey) { if(NULL==this) return false; if(key==tKey) return true; int dir=this->compare(tKey); return child[dir]->find(tKey); } void Treap::rotatePrint(Treap* tRoot,int dep) { if(NULL==tRoot) return ; rotatePrint(tRoot->child[0],dep+1); for(int i=0;i<dep;i++) cout<<" "; cout<<"("<<tRoot->key<<"。"<<tRoot->pri<<")"<<endl; rotatePrint(tRoot->child[1],dep+1); } void menu() { //Treap *root=new Treap(10); Treap *root=NULL; srand(unsigned(clock())); int val,choice; while(true) { cout<<"***************菜单*************"<<endl; cout<<" 1.插入"<<endl; cout<<" 2.删除"<<endl; cout<<" 3.查找"<<endl; cout<<" 4.退出"<<endl; cout<<"您的选择:[ ]"; cin>>choice; cout<<"请输入数:"; cin>>val; if(1==choice) root=root->insert(root,val); else if(2==choice) root=root->erase(root,val); else if(3==choice) { if(root->find(val)) cout<<"查找成功!"<<endl; else cout<<"查找失败!"<<endl; } else if(4==choice) exit(0); cout<<"==================="<<endl; root->rotatePrint(root,0); } } int main() { while(true) menu(); }