操作包括二叉搜索树的创建,插入,搜索,寻找前驱后继,删除,左右旋转,插入元素为根结点,以及两棵二叉树的合并。
二叉树的创建很简单,只需要设置 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;
}