二叉搜索树
二叉搜索树的特点
-
根节点的值大于左结点的值,小于右结点的值
-
根节点的左、右子树也是一个二叉搜索树
-
没有重复值
-
中序遍历得到的序列是从小到大排列的
-
二叉树的存储结构
typedef struct tree
{
int data;
struct tree *leftchild;
struct tree *rightchild;
} BiNode, *BiTree;
typedef struct Binary_Search_Tree
{
BiTree root;
} BST,*pBST;
这里我多定义了一个结构,来操纵根节点,这个结构看个人习惯,可有可无。
二叉搜索树的算法
以下建立的二叉搜索树如图
二叉搜索树的建立/插入
插入只有在位置为空的时候才插入,并不会在位置不为空的时候抢占其他结点的位置。
void insert_Binode(pBST pB, int val)
{
BiTree pNew = (BiTree)malloc(sizeof(BiNode));//创建一个新的树结点同时初始化
pNew->data = val;
pNew->leftchild = NULL;
pNew->rightchild = NULL;
if (pB->root == NULL) //树根节点为空
{
pB->root= pNew;
}else
{
BiTree temp = pB->root; //创建一个临时结点来遍历二叉搜索树中的结点来进行值比较
while(temp!=NULL)
{
if(val>temp->data) //大于根节点,说明要插入右子树
{
if(temp->rightchild == NULL)//根节点的右子树结点为空,说明可以插入
{
temp->rightchild = pNew;
return;
}
else
temp=temp->rightchild; //不为空接着遍历比较
}
else
{
if (temp->leftchild == NULL)
{
temp->leftchild = pNew;//小于根节点,说明要插入右子树
return;
}
else
temp=temp->leftchild;
}
}
}
}
二叉搜索树的查找
用跟插入一样的迭代方法可能更好理解,我第一次用的是递归方法.
int search_Binode(BiTree pBC,int val)
{
if(pBC==NULL)
{
return 0;
}
if(pBC->data==val)
{
return 1;
}else if(pBC->data<val) //val更大在右子树,
{
return search_Binode(pBC->rightchild, val);//接着在右子树看是否=val
}else
{
return search_Binode(pBC->leftchild, val);
}
}
int search_Binode(BiTree pBC,int val)
{
BiTree p=pBC;
if(pBC==NULL) //根节点为空
{
return 0;
}
while(p!=NULL)
{
if(p->data==val) //找到了返回1
{
return 1;
}else if(pBC->data<val) //val更大在右子树
{
p=p->rightchild //更新p
}else
{
p=p->leftchild
}
}
}
二叉搜索树删除结点
删除结点更复杂一点,需要讨论三种情况
-
删除节点为叶子节点
-
删除节点只有左子树或者右子树
-
删除节点既有右子树又有左子树
从图里可以看出第一种、第二种情况都是将待删除结点的父节点指向待删除结点的子结点(待删除结点为叶子节点可以认为它子结点为NULL)
代码书写上我一般把第一个和第二个情况一起写(只考虑了第一种第二种情况)
void delete_Binode(pBST pB, int val)
{
if(pB->root==NULL)
{
return;
}else{
BiTree temp = pB->root;
BiTree temp_father = NULL;
BiTree temp_child = NULL;
while (temp->data != val && temp != NULL)
{
if(val>temp->data)
{
temp_father = temp;
temp = temp->rightchild;
}else{
temp_father = temp;
temp = temp->leftchild;
}
}
if(temp==NULL)
{
return;
}else
{
if (temp->leftchild != NULL) //判断要删除结点是只有一个左子结点还是只有一个右子结点 还是没有左右子节点
temp_child = temp->leftchild;
else if (temp->rightchild != NULL)
temp_child = temp->rightchild;
else
temp_child = NULL; //待删除结点为叶子节点则其子结点为空
if(temp_father->leftchild==temp)//把要删除结点的父节点指向要删除结点的子结点,先判断是 父节点的哪个结点
temp_father->leftchild = temp_child;
else
temp_father->rightchild = NULL;
}
}
}
第三种情况需要做的是
左子树最大的结点或者右子树最小的结点覆盖待删除结点。然后再把左子树最大节点或者右子树最小节点删除
void delete_Binode(pBST pB, int val)
{
if(pB->root==NULL)
{
return;
}else{
BiTree temp = pB->root;
BiTree temp_father;
BiTree temp_child;
while (temp->data != val && temp != NULL)
{
if(val>temp->data)
{
temp_father = temp;
temp = temp->rightchild;
}else{
temp_father = temp;
temp = temp->leftchild;
}
}
if(temp==NULL)
{
return;
}
if (temp->rightchild != NULL && temp->leftchild != NULL)//要删除结点有两个子结点
{
temp_father = temp; //准备好两个节点用来找要删除结点的左子树最大节点
temp_child = temp->leftchild; //定位到左子树
while(temp_child->rightchild!=NULL) //找左子树的最大结点——>一直取右子树结点
{
temp_father = temp_child;
temp_child = temp_child->rightchild;
}
temp->data = temp_child->data;
if(temp_child->leftchild!=NULL) //找到最大结点的时候要考虑两种情况,看下图更好理解
{
temp_father->leftchild = temp_child->leftchild;
}else{
temp_father->rightchild = NULL;}
}
(v表示是左子树的最大节点,树的前部分结构没有画出,就画出了左子树最大节点那一小部分)
如果左子树最大结点是第二类,就非常方便此时child指向的就是左子树最大节点,可以直接覆盖删除
但是如果是第一类,覆盖之后直接删除就会导致有一个结点丢失,所以就需要判断是否左子树最大结点的左节点是否为空,如果不为空需要把结点接在最大节点位置
如果是第二类,直接把父节点的右孩子指针域置空就删除了。
完整代码
#include <stdio.h>
#include <stdbool.h>
#include <windows.h>
#include <malloc.h>
typedef struct tree
{
int data;
struct tree *leftchild;
struct tree *rightchild;
} BiNode, *BiTree;
typedef struct Binary_Search_Tree
{
BiTree root;
} BST,*pBST;
void create_searchtree(BiTree *);
void insert_Binode(pBST pB, int val);
int search_Binode(BiTree pBC, int val);
void delete_Binode(pBST pBC, int val);
void Midtraverse_tree(BiTree pBC);
int main()
{
pBST pB=(pBST)malloc(sizeof(BST)); //要记得初始化,这里踩了坑,不初始化地址只读,程序无法运行
pB->root = NULL;
int i;
int a[7] = {6, 3, 8, 2, 5, 1, 7};
for (i = 0; i<7;i++)
{
insert_Binode(pB, a[i]);
}
Midtraverse_tree(pB->root);
printf("\n");
delete_Binode(pB,3);
Midtraverse_tree(pB->root);
}
void Midtraverse_tree(BiTree pBC)
{
if (pBC == NULL)
{
return;
}
else
{
Midtraverse_tree(pBC->leftchild);
printf("%d", pBC->data);
Midtraverse_tree(pBC->rightchild);
}
}
void insert_Binode(pBST pB, int val)
{
BiTree pNew = (BiTree)malloc(sizeof(BiNode));
pNew->data = val;
pNew->leftchild = NULL;
pNew->rightchild = NULL;
if (pB->root == NULL)
{
pB->root= pNew;
}else
{
BiTree temp = pB->root;
while(temp!=NULL)
{
if(val>temp->data)
{
if(temp->rightchild == NULL)
{
temp->rightchild = pNew;
return;
}
else
temp=temp->rightchild;
}
else
{
if (temp->leftchild == NULL)
{
temp->leftchild = pNew;
return;
}
else
temp=temp->leftchild;
}
}
}
}
int search_Binode(BiTree pBC,int val)
{
if(pBC==NULL)
{
return 0;
}
if(pBC->data==val)
{
return 1;
}else if(pBC->data<val)
{
return search_Binode(pBC->rightchild, val);
}else
{
return search_Binode(pBC->leftchild, val);
}
}
void delete_Binode(pBST pB, int val)
{
if(pB->root==NULL)
{
return;
}else{
BiTree temp = pB->root;
BiTree temp_father;
BiTree temp_child;
while (temp->data != val && temp != NULL)
{
if(val>temp->data)
{
temp_father = temp;
temp = temp->rightchild;
}else{
temp_father = temp;
temp = temp->leftchild;
}
}
if(temp==NULL)
{
return;
}
if (temp->rightchild != NULL && temp->leftchild != NULL)//要删除结点有两个子结点
{
temp_father = temp;
temp_child = temp->leftchild;
while(temp_child->rightchild!=NULL) //找左子树的最大结点
{
temp_father = temp_child;
temp_child = temp_child->rightchild;
}
temp->data = temp_child->data;
if(temp_child->leftchild!=NULL)
{
temp_father->leftchild = temp_child->leftchild;
}else{
temp_father->rightchild = NULL;}
}
else
{
if (temp->leftchild != NULL) //判断要删除结点是只有一个左子结点还是只有一个右子结点还是没有左右子节点
temp_child = temp->leftchild;
else if (temp->rightchild != NULL)
temp_child = temp->rightchild;
else
temp_child = NULL;
if(temp_father->leftchild==temp)//把要删除结点的父节点指向要删除结点的子结点,先判断是父节点的哪个结点
temp_father->leftchild = temp_child;
else if (temp_father->rightchild == temp)
temp_father->rightchild = temp_child;
}
}
}