二叉搜索树的相关操作
操作包括二叉搜索树的创建,插入,搜索,寻找前驱后继,删除,左右旋转,插入元素为根结点,以及两棵二叉树的合并。
二叉树的创建很简单,只需要设置 value, left child, right child 即可。
插入的时候递归插入树中合适的位置,通过比较插入元素的值与根结点元素的值,如果小于则递归插入到左子树中,否则递归插入到右子树中。
搜索的时候与插入类似,比较要搜索的值和根结点元素值的大小,若小于则递归到左子树中去查找,否则递归到右子树中去查找。
寻找前驱的方式是在左子树的右结点中去递归寻找,一直找到右结点的右子结点为空,则该右结点即是前驱,寻找后继是在右子树的左结点中去递归寻找,一直找到左结点的左子结点为空,则该左结点即是后继。
结点的删除有些复杂,如果删除的叶结点,则直接删除,否则就要用该结点的前驱中的值来代替该结点中的值,然后递归删除前驱,若不存在前驱,则使用该结点的后继来代替该结点中的值,然后递归删除后继,如果后继也不存在,证明该结点是个叶子结点,直接删除即可。
左旋的方式是让根结点的右结点变成新的根结点,再让新的根结点的左结点指向原来的根结点,再让原来根结点的右结点指向新的根结点原来的左结点,这样操作之后就好像原来的根结点向左旋转了一样。
右旋的操作正好和左旋是对称的,将根结点的左结点变成新的根结点,再让新的根结点的右结点指向原来的根结点,再让原来根结点的左结点指向新的根结点原来的右结点,这样操作之后就好像根结点右旋了一样。
原来的插入操作是直接插入到叶子结点上的,而插入一个元素使其变为根结点就需要旋转操作来实现,如果插入元素大于根结点值,则递归插入到右子树中让其成为右子树的根结点,再将树左旋即可实现,若插入元素小于根结点值,则递归插入到左子树中让其成为左子树中的根结点,再将树右旋即可,递归到最后必然到叶子结点上,此时用插入元素的值来创建一个新的结点,之后依次回程旋转即可实现。
要合并两棵二叉树,基本的思路是遍历其中一棵树,然后递归插入到另一棵树中,优化方式是先将其中一棵树A的根结点插入到另外一棵树B中,此时两棵树的根结点相同,此后树A的左子树插入到树B中必然也是在左子树中,右子树也是同样。所以就可以递归将A的左结点插入到B的左子树中,将A的右结点递归插入到B的右子树中,注意因为这种方式依赖于左右子树和根结点的大小关系,所以只能采用前序递归方式,否则合并之后会破坏树中元素的先后顺序。
具体详见代码:
//BST.cpp #include <malloc.h> #include <iostream> #include "binary_tree.h"//defined BST struct #include "print_binary_tree.h" //the node of a tree, included in binary_tree.h #include <stdlib.h> #include <time.h> using namespace std; //create node with value, left child and right child struct bst_node* create_bst_node(int value, struct bst_node* l, struct bst_node* r){ struct bst_node* t =(struct bst_node*) malloc(sizeof *t);//should call free() when finished t->value = value; t->l = l; t->r = r; return t; } //bst insert struct bst_node* bst_insert(struct bst_node* root, int value){ if(root == NULL) return create_bst_node(value,NULL,NULL);//tree is empty //if root is not NULL if(value < root->value) root->l = bst_insert(root->l,value);//insert into left child else if (value > root->value) root->r = bst_insert(root->r,value);// return root; } //bst search, the keyword "struct" can be omitted struct bst_node* bst_search(struct bst_node* bst_root, int value){ if(bst_root == NULL) return NULL; if(value < bst_root->value){ return bst_search(bst_root->l, value); }else if(value > bst_root->value){ return bst_search(bst_root->r, value); } return bst_root;//neither greater nor less, must be equal } //predecessor bst_node* predecessor(bst_node* t){ t = t->l; while(t->r != NULL){ t = t->r; } return t; } //successor bst_node* successor(bst_node* t){ t = t->r; while(t->l != NULL){ t = t->l; } return t; } //bst remove bst_node* bst_remove(bst_node* t, int value){ if(t == NULL) return NULL; if(value < t->value){ t->l = bst_remove(t->l,value);//return a new left subtree which has removed the value }else if(value > t->value){ t->r = bst_remove(t->r,value);//return a new right subtree which has removed the value }else{//have found the value if(t->l != NULL){//get the predecessor first, then remove predecessor in left subtree t->value = predecessor(t)->value; t->l = bst_remove(t->l,t->value); }else if(t->r != NULL){//get the successor first, then remove successor in right subtree t->value = successor(t)->value; t->r = bst_remove(t->r,t->value); }else{ //now t is a leaf node,and remove it directly free(t); t = NULL; } } return t; } //bst rotate //bst rotate right, return left child bst_node* bst_rot_r(bst_node* t){ if(t == NULL || t->l ==NULL) return t; else{ bst_node* tl = t->l; t->l = tl->r; tl->r = t; return tl; } } //bst rotate left, return right child bst_node* bst_rot_l(bst_node* t){ if(t == NULL || t->r ==NULL) return t; else{ bst_node* tr = t->r; t->r = tr->l; tr->l = t; return tr; } } //insert node to root bst_node* bst_insert_to_root(bst_node* t, int value){ if(t == NULL)//insert to leaf return create_bst_node(value,NULL,NULL); if(value > t->value){ t->r = bst_insert_to_root(t->r,value); return bst_rot_l(t); }else if(value < t->value){ t->l = bst_insert_to_root(t->l,value); return bst_rot_r(t); }else{ return t; } } //merge two trees bst_node* bst_merge_mine(bst_node* a, bst_node* b) { if (b==NULL) return a; if (a==NULL) return b; //get the value from a, and insert the value into b //我是直接从根结点开始插入 //我的算法前序递归和后序递归都是可以的 //中序递归会生成一颗倾斜的树,应为中序遍历结点的值往后依次是增大的 //最后的一个元素是最大的,又成为了根结点,所以合成树是向右上方倾斜的 b = bst_merge_mine(a->l,b); b = bst_merge_mine(a->r,b); b = bst_insert_to_root(b,a->value);//let root of a tobe the root of merged tree return b; } //merge two trees //基本思想还是遍历a中的元素,递归插入到b中 //但是这个比我的算法稍微好的地方在于将a和b的根结点设置为一样之后 //每次插入可以不从b的根结点开始,因为此时a的左树插入之后必然在合并之树的左边, //a的右子树插入之后也必然在合并之树的右边,所以插的时候a的左子树可以直接插到b的左子结点去, //同理a的右结点可以插到b的右子树中去 bst_node* bst_merge(bst_node* a, bst_node* b) {//整个递归过程是前序递归:root->left child->right child //从一开始想到这两个条件并不困难, //但是在递归的时候这两个条件却起到了很大作用 //这两个条件配合下面的左右子树赋值语句b->l=... b->r=...发挥了很妙的作用 if (a==NULL) return b;// if (b==NULL) return a;//这个条件很微妙,在递归的时候表现的很巧妙 //注意,因为此处利用了左右子树和根的相互关系,所以只能是前序递归 //中序递归和后序递归都会破坏树中元素的先后关系 b = bst_insert_to_root(b,a->value);//set a to root b->l = bst_merge(a->l, b->l);//将a的左结点取出,插到b的左子树中去 b->r = bst_merge(a->r, b->r);//将a的右结点取出,插到b的右子树中去 free(a);//每次合并完后将a的子树释放 a = NULL; return b; } int main(){ //create_bst_node(); //srand((unsigned)time(NULL));//使用系统时间初试化随机种子 int value = rand()%123; bst_node* bst_root = create_bst_node(value, NULL, NULL); //for(int k=0; k<39; k++){ for(int k=0; k<9; k++){ value = rand()%123; bst_insert(bst_root, value); } print_binary_tree(bst_root); value = rand()%123; bst_node* bst_root2 = create_bst_node(value, NULL, NULL); //for(int k=0; k<39; k++){ for(int k=0; k<9; k++){ value = rand()%123; bst_insert(bst_root2, value); } print_binary_tree(bst_root2); bst_node* m = bst_merge(bst_root,bst_root2); print_binary_tree(m); return 0; }
去吧,去吧,到彼岸去吧,彼岸是光明的世界!