二叉搜索树
二叉搜索树的定义
二叉搜索树又被称为二叉排序树,它或者是一颗空树,或者是具备以下性质的二叉树
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
二叉搜索树的操作
它属于二叉树,所以它的初始化和创建销毁都和二叉树相同,不同的主要就是插入,查找和删除。
插入
在二叉树搜索树中插入一个新元素时,必须要检测该元素是否在树中已经存在。如果已经存在,则不进行插入;否则就将新元素加入到搜索挺值得地方,具体的操作可以看后面的代码注释。
查找
查找是按值进行查找,可以通过递归方法进行查找,非递归也可以但是没递归方法简单。由于二叉树搜索树的性质导致,查找只要比较需要查找的值后,就可以忽略一半的元素,因此二叉搜索树,就是为查找而生的。
删除
二叉搜索树的按值删除工作,是它最麻烦的操作,使用递归方法实现的具体思路如下:
首先查找元素是否在二叉搜索树种,如果不存在,则返回,否则删除的节点可能分下面四种情况:
- 要删除的节点无孩子节点
- 要删除的节点只有左孩子节点
- 要删除的节点只有右孩子节点
- 要删除的节点有左右孩子节点
情况1可以归类到2或者3
对于上述四种情况,相应的删除方法如下:
- 直接删除该节点
- 删除该节点且使被删除的节点的双亲节点指向被删除节点的左孩子节点
- 删除该节点且使被删除的节点的双亲节点指向被删除节点的右孩子节点
- 在它的右子树中寻找中序下的一个节点(关键码最小,也就是右子树的最小值或左子树的最大值),用他的值填补到被删除节点中,在来处理该节点的删除问题。
代码具体实现:
//search_tree.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef char SearchNodeType;
typedef struct SearchNode{
SearchNodeType data;
struct SearchNode* lchild;
struct SearchNode* rchild;
}SearchNode;
void SearchTreeInit(SearchNode** pRoot);
void SearchTreeDestroy(SearchNode** pRoot);
//有可能会对空树进行插入,从而就修改了root的指向
//此处不能指定往哪个位置插入,因此我们要保证不管怎么插,
//树始终都是一个二叉搜索树
void SearchTreeInsert(SearchNode** pRoot, SearchNodeType to_insert);
SearchNode* SearchTreeFind(SearchNode* root, SearchNodeType to_find);
//按照值来进行删除
void SearchTreeRemove(SearchNode** pRoot, SearchNodeType to_remove);
//search_tree.c
#include "search_tree.h"
void SearchTreeInit(SearchNode** pRoot){
if(pRoot == NULL){
return;//非法输入
}
*pRoot = NULL;
return;
}
SearchNode* CreateSearchNode(SearchNodeType value){
SearchNode* new_node = (SearchNode*)malloc(sizeof(SearchNode));
new_node -> data = value;
new_node -> lchild = NULL;
new_node -> rchild = NULL;
return new_node;
}
void DestroySearcNode(SearchNode* node){
free(node);
}
void SearchTreeDestroy(SearchNode** pRoot){
//此处的销毁和销毁一个普通二叉树没有什么区别
//后序遍历的方式来销毁这个树
if(pRoot == NULL){
return;//非法输入
}
if(*pRoot == NULL){
return;//空树
}
SearchNode* root = *pRoot;
SearchTreeDestroy(&root -> lchild);
SearchTreeDestroy(&root -> rchild);
DestroySearcNode(root);
*pRoot = NULL;
return;
}
#if 0
//递归方式插入
void SearchTreeInsert(SearchNode** pRoot, SearchNodeType to_insert){
if(pRoot == NULL){
return;//非法输入
}
if(*pRoot == NULL){
SearchNode* new_node = CreateSearchNode(to_insert);
*pRoot = new_node;
return;
}
//对于这个树非空的情况
//采用递归的方式来进行插入
SearchNode* cur = *pRoot;
if(to_insert < cur -> data){
SearchTreeInsert(&cur -> lchild, to_insert);//递归插入到左子树
}else if(to_insert > cur -> data){
SearchTreeInsert(&cur -> rchild, to_insert);//递归插入到右子树
}else{
//关于等于的处理有好多种,可以采用这么一种约定
//我们约定这个二叉搜索树中,所有的元素不能重复
//直接返回表示不做任何动作,插入失败
//我们的约定不允许出现重复元素
return;
}
return;
}
//递归查找
SearchNode* SearchTreeFind(SearchNode* root, SearchNodeType to_find){
if(root == NULL){
return NULL;
}
if(to_find < root -> data){
return SearchTreeFind(root -> lchild, to_find);//递归查找左子树
}else if(to_find > root -> data){
return SearchTreeFind(root -> rchild, to_find);//递归查找右子树
}else{
return root;
}
}
void SearchTreeRemove(SearchNode** pRoot, SearchNodeType to_remove){
if(pRoot == NULL){
return;//非法输入
}
if(*pRoot == NULL){
return;//空树 2.如果没找到,就直接返回
//按值删除有以下几种情况
//1.要删除的元素没有,直接返回
//2.如果要删除元素没有子树,直接让父节点对应的指针指向空,释放内存
//3.如果要删除元素只有右子树,让父节点只相当前节点的右子树,释放内存
//4.如果要删除元素只有左子树,让父节点指向当前节点的左子树,释放内存
//具体操作:
//1.找到to_remove所在的位置
SearchNode* root = *pRoot;
if(to_remove < root -> data){
SearchTreeRemove(&root -> lchild, to_remove);
return;
}else if(to_remove > root -> data){
SearchTreeRemove(&root -> rchild, to_remove);
return;
}else{
//3.如果找到了,分情况讨论
SearchNode* to_remove_node = root;
if(root -> lchild == NULL && root -> rchild == NULL){
//a)要删除的节点没有子树
*pRoot = NULL;
DestroySearcNode(to_remove_node);
return;
}else if(root -> lchild != NULL && root -> rchild == NULL){
//c)要删除的节点只有右子树
*pRoot = to_remove_node -> lchild;
DestroySearcNode(to_remove_node);
return;
}else if(root -> lchild == NULL && root -> lchild != NULL){
//c)要删除的节点只有右子树
*pRoot = to_remove_node -> rchild;
DestroySearcNode(to_remove_node);
return;
}else{
//d)要删除的节点有左右子树
// 需要先找到右子树中的最小节点
// 把要删除节点的值和最小节点的值进行交换
// 从当前节点的右子树出发,尝试递归的删除刚刚交换的值
SearchNode* min = to_remove_node -> rchild;
while(min -> lchild != NULL){
min = min -> lchild;
}
//min已经指向了右子树中的最小节点
to_remove_node -> data = min -> data;
//尝试递归删除 min -> data
SearchTreeRemove(&to_remove_node -> rchild, min -> data);
return;
}
}
return;
}
#endif
#if 1//实现非递归版本
void SearchTreeInsert(SearchNode** pRoot, SearchNodeType to_insert){
//1.单独处理空树问题
//2.对于非空树, 先找到插入位置,把要初入元素的父亲节点记录下来
if( pRoot == NULL){
return;//非法输入
}
if(*pRoot == NULL){
*pRoot = CreateSearchNode(to_insert);
return;
}
//新元素要放置的位置
SearchNode* cur = *pRoot;
//新元素的父节点是谁
SearchNode* pre = NULL;
//这个循环知识帮助我们找到插入的位置在哪
while(1){
if(cur == NULL){
break;//找到插入位置,跳出循环
}
if(to_insert < cur -> data){
pre = cur;
cur = cur -> lchild;
}else if(to_insert > cur -> data){
pre = cur;
cur = cur -> rchild;
}else{
//发现一个相同元素,此时按照约定直接插入失败
return;
}
}
SearchNode* new_node = CreateSearchNode(to_insert);
//pre不可能为NULL,如果pre为NULL,意味着前面的while循环一进来就命中了cur==NULL
//然而cur== NULL的情况已经被最开始的判定排除了
if(new_node -> data < pre -> data){
pre -> lchild = new_node;
}else{
pre -> rchild = new_node;
}
}
SearchNode* SearchTreeFind(SearchNode* root, SearchNodeType to_find){
if(root == NULL){
return NULL;
}
SearchNode* cur = root;
while(1){
if(to_find < cur -> data){
cur = cur -> lchild;
}else if(to_find > cur -> data){
cur = cur -> rchild;
}else{
break;
}
}
return cur;
}
void SearchTreeRemove(SearchNode** pRoot, SearchNodeType to_remove){
if(pRoot == NULL){
return;
}
if(*pRoot == NULL){
return;
}
//1.先找到要删除的节点是谁
SearchNode* to_remove_node = *pRoot;
SearchNode* parent = NULL;
while(1){
if(to_remove_node == NULL){
return;//没找到要删除的元素,直接函数终止
}
if(to_remove < to_remove_node -> data){
parent = to_remove_node;
to_remove_node = to_remove_node -> lchild;
}else if(to_remove > to_remove_node -> data){
parent = to_remove_node;
to_remove_node = to_remove_node -> rchild;
}else{
//找到了要删除的元素
break;
}
}
//2.如果找到了元素,分情况讨论
if(to_remove_node -> lchild == NULL && to_remove_node -> rchild ==NULL){
//无子树
//此处我们要先判定要删除节点是否是个根节点
if(to_remove_node == *pRoot){
*pRoot = NULL;
DestroySearcNode(to_remove_node);
}else{
//要删除元素不是根节点
//需要知道当前节点是parent的左子树还是右子树
if(to_remove_node -> data < parent -> data){
parent -> lchild = NULL;
}else{
parent -> rchild = NULL;
}
}
//统一释放节点内存;
DestroySearcNode(to_remove_node);
return;
}else if(to_remove_node -> lchild != NULL && to_remove_node -> rchild == NULL){
//只有左子树
if(to_remove_node == *pRoot){
*pRoot = to_remove_node -> lchild;
}else{
if(parent -> data < parent -> data){
parent-> lchild = to_remove_node -> lchild;
}else{
parent -> rchild = to_remove_node -> lchild;
}
}
DestroySearcNode(to_remove_node);
return;
}else if(to_remove_node -> lchild == NULL && to_remove_node -> rchild != NULL){
//只有右子树
if(to_remove_node == *pRoot){
*pRoot = to_remove_node -> rchild;
}else{
if(to_remove_node -> data < parent -> data){
parent -> lchild = to_remove_node -> rchild;
}else{
parent -> rchild = to_remove_node -> rchild;
}
}
DestroySearcNode(to_remove_node);
return;
}else{
//有左右子树
SearchNode* min = to_remove_node -> rchild;
SearchNode* min_parent = to_remove_node;
while(min -> lchild != NULL){
min_parent = min;
min = min -> lchild;
}
//循环结束后,min就指向to_remove_node右子树的最小值
to_remove_node -> data = min -> data;
if(min -> data < min_parent -> data){
//min是min_parent的左子树
//min一定没有左子树
min_parent -> lchild = min -> rchild;
}else{
//通常情况下min是min_parent的左子树,但初始情况下例外
min_parent -> rchild = min ->rchild;
}
DestroySearcNode(min);
return;
}
}
#endif
///
TEST//
/
#if 1
#define TESTHEARDER printf("\n=================%s================\n",__FUNCTION__)
void PreOrder(SearchNode* root){
if(root == NULL){
return;
}
printf("%c ",root -> data);
PreOrder(root -> lchild);
PreOrder(root -> rchild);
}
void InOrder(SearchNode* root){
if(root == NULL){
return;
}
InOrder(root -> lchild);
printf("%c ",root -> data);
InOrder(root -> rchild);
}
void SearchTreePrintChar(SearchNode* root, const char* msg){
printf("%s\n", msg);
printf("先序遍历");
PreOrder(root);
printf("\n");
printf("中序遍历");
InOrder(root);
printf("\n");
}
void TestInsert(){
TESTHEARDER;
SearchNode* root;
SearchTreeInit(&root);
SearchTreeInsert(&root, 'a');
SearchTreeInsert(&root, 'e');
SearchTreeInsert(&root, 'c');
SearchTreeInsert(&root, 'd');
SearchTreeInsert(&root, 'b');
SearchTreePrintChar(root, "插入五个元素");
}
void TestFind(){
TESTHEARDER;
SearchNode* root;
SearchTreeInit(&root);
SearchTreeInsert(&root, 'a');
SearchTreeInsert(&root, 'b');
SearchTreeInsert(&root, 'c');
SearchTreeInsert(&root, 'd');
SearchTreeInsert(&root, 'e');
SearchNode* cur;
cur = SearchTreeFind(root, 'c');
printf("cur expectef c, actual %c\n", cur -> data);
}
void TestRemove(){
TESTHEARDER;
SearchNode* root;
SearchTreeInit(&root);
SearchTreeInsert(&root, 'a');
SearchTreeInsert(&root, 'e');
SearchTreeInsert(&root, 'c');
SearchTreeInsert(&root, 'd');
SearchTreeInsert(&root, 'b');
SearchTreeRemove(&root, 'd');
SearchTreePrintChar(root, "删除一个元素");
}
int main(){
TestInsert();
TestFind();
TestRemove();
return 0;
}