随机构造的二叉搜索树是趋向于平衡的.因此,一般来说,要为一组固定的元素建立平衡二叉树,可以先随机排列这些元素,然后按照排列的顺序将它们插入倒树中.Treap树先按照结点的优先级将结点排序,然后再逐一插入二叉树中.Treap树中结点的关键字遵循二叉搜索树的性质,且优先级遵循最小堆的性质.
编程实现:
#include<iostream> using namespace std; class Node{ public: char key; int priority; Node* p = nullptr; Node* left = nullptr; Node* right = nullptr; Node() = default; Node(char k, int w):key(k),priority(w){} }; void left_rotate(Node* &x){//结点指针要传递引用 Node* y =x->right; x->right = y->left; y->left = x; x = y; } void right_rotate(Node* &x){//结点指针要传递引用 Node* y =x->left; x->left = y->right; y->right = x; x = y; } void insert(char key,int priority, Node* &tree){//结点指针要传递引用 if(tree == nullptr){//找到位置,建立结点 tree = new Node(key, priority); } else if(key < tree->key){ insert(key, priority, tree->left); if(tree->left->priority < tree->priority){//左子结点优先级小于当前结点优先级,右旋当前结点 right_rotate(tree); } } else if(key > tree->key){ insert(key, priority, tree->right); if(tree->right->priority < tree->priority){//右子结点优先级小于当前结点优先级,左旋当前结点 left_rotate(tree); } } } void remove(char key, Node* &tree){//结点指针要传递引用 if(tree != nullptr){ if(key < tree->key){ remove(key, tree->left); } else if(key > tree->key){ remove(key, tree->right); } else{ if(tree->left != nullptr && tree->right != nullptr){ if(tree->left->priority < tree->right->priority){ //左子结点优先级较小,右旋 right_rotate(tree); remove(key,tree->right);//1 } else{//右子结点优先级较小,左旋 left_rotate(tree); remove(key,tree->left);//2 } //remove(key,tree);1和2可以由这句代替 } else{ Node* t = tree; tree = (tree->left == nullptr) ? tree->right : tree->left; delete t; } } } } void inorder_traverse(Node* tree){ if(tree != nullptr){ inorder_traverse(tree->left); cout<<tree->key<<" , "; inorder_traverse(tree->right); } } int main(){ Node* tree = nullptr; insert('G',4, tree); insert('B',7, tree); insert('H',5, tree); insert('K',65, tree); insert('A',10, tree); insert('E',23, tree); inorder_traverse(tree); cout<<endl; remove('B', tree); inorder_traverse(tree); cout<<endl; return 0; }
运行结果:
思考:为什么结点指针要传递引用?
因为这里改变的是结点指针变量本身而不是结点指针指向的对象.
受交换两个变量的值的影响,我一直以为只要指针作为参数时传递的是地址,函数对指针的操作在函数调用结束后仍然有效.其实不然,这里要分三种情况:
1.函数操作对象本身//函数调用结束后函数对对象的操作无效 2.函数操作指针指向的对象//函数调用结束后函数对指针指向的对象的操作仍然有效,和操作引用是同样的效果 3.函数操作指针本身//函数调用结束后函数对指针指向的对象的操作无效,对指针的操作也无效,回到1了,指针在这里就是一个普通变量 4.函数操作指针的引用//函数调用结束后函数对指针指向的对象的操作无效,对指针的操作有效,和操作指针的指针是同样的效果
#include<iostream> using namespace std; void swap(int *vp1, int *vp2) { int a = *vp1; *vp1 = *vp2; *vp2 = a; } void swap2(int *vp1, int *vp2) { int* a = vp1; vp1 = vp2; vp2 = a; } void swap3(int* &vp1, int* &vp2){ int* a = vp1; vp1 = vp2; vp2 = a; } int main(){ int a=1,b=2; swap(&a,&b); cout<<"swap :"<<a<<","<<b<<endl; swap2(&a,&b); cout<<"swap2:"<<a<<","<<b<<endl; int* p1 = &a; int* p2 = &b; //swap3(&a,&b);invalid initialization of non-const reference of type ‘int*&’ from an rvalue of type ‘int*’ swap3(p1,p2); cout<<"swap3:"<<*p1<<","<<*p2<<endl; return 0; }
运行结果:
参考:https://www.byvoid.com/upload/wp/2010/12/treap-analysis-and-application.pdf