从头来过之数据结构二叉查找树
二叉查找树(二叉搜索树)
可能是空树
或者满足以下条件
1、若左子树存在,则左子树结点值小于根结点值
2、若右子树存在,则右子树结点值大于根结点值
3、且左右子树皆是二叉查找树
创建二叉查找树
使用的结点值为54、20、66、40、28、79、58
将这些数据依次插入,创建二叉查找树
可以点击此处动态实现一下 依次输入上述的值 进行插入建树
创建出的查找二叉树
开始建树
定义树结点的结构体
//20230104 二叉排序树(二叉查找树)
//创建二叉查找树
typedef int ElemType;
//树的根节点的结构体
typedef struct STNode {
ElemType data;//用来存放数据域
STNode* Lchild, * Rchild;//用来指向该结点的左右结点
}STNode,*STree;
建树
//创建二叉树的函数
void creat_STree(STree& tree, ElemType str[], int len) {
//因为要对树进行改变,所以使用引用
tree = NULL;
int i=0;//方便遍历数组 取出数据 进行插树
while (i <len) {
insert(tree, str[i]);//调用插入树元素函数
i++;
}
}
像树中插入元素的函数insert
此处使用递归思想:
当像树中插入元素时
若为空树,则创建根节点,并将值存入即可
若不为空树,则要进行比较大小
若存入数据元素比根结点元素小,则须将该元素与根结点的左结点进行比较(左结点为空存入 不为空 在与左结点比较 大于存左结点的右结点,小于存左结点的左结点)递归思想
若存入数据元素比根结点元素大,则须将该元素与根结点的右结点进行比较(右结点为空存入 不为空 在与右结点比较 大于存右结点的右结点,小于存右结点的左结点)递归思想
//在树中进行插入的函数(使用递归思想)
void insert(STree& p, ElemType i) {
if (NULL == p) {
p = (STree)malloc(sizeof(STNode));//为根节点申请空间
//根为空,将元素放入该结点
p->data = i;
p->Lchild = NULL;
p->Rchild=NULL;
}
else if (i < p->data) {
//插入元素小于根节点元素 将其插入左子树
insert(p->Lchild, i);
}
else {
//插入元素大于根节点元素,将其插入右子树
insert(p->Rchild, i);
}
}
对新建的二叉查找树进行中序遍历
//使用中序遍历(左根右) 进行打印(得出的二叉查找树结果应为从小到大)
void inOrder(STree tree) {
if (tree != NULL) {
inOrder(tree->Lchild);
printf("%3d", tree->data);
inOrder(tree->Rchild);
}
}
主函数代码
STree tree = (STree)malloc(sizeof(STNode));//为树结点指针向内存申请指向空间(地址)
ElemType str[] = { 54,20,66,40,28,79,58 };//要创建的树的值
int len = 7;//加入的总结点个数
creat_STree(tree, str, len);//创建二叉排序树的函数
inOrder(tree);//中序遍历二叉排序树
中序遍历二叉查找树结果
将二叉查找树从小到大输出 因为中序遍历是 左根右
二叉查找树也满足 左子树<根<右子树
二叉搜索数的查找
先将要查找元素与根结点进行比较 相等返回根结点
不等 比根结点大 和根结点的右结点进行比较 (重复相等返回 不等 在按大小 与左右结点比较)比根节点小 与根结点的左结点比较
只找到被查找元素的代码
//查找对应的元素并传回
int search_elem_in_STree(STree tree, ElemType elem, STree& parent) {
//parent使用引用是因为如果查找到对应的元素则将结点地址赋予parent
STree p = tree;
while (NULL != p) {
if (elem == p->data) {
parent = p;
return 1;//表示查找到当前元素
}
else if (elem > p->data) {
//当前元素比根结点元素大 将该元素与根结点的右结点进行比较查找
p = p->Rchild;
}
else {
//当前元素比根结点元素小,将该元素与根结点的左结点进行比较查找
p = p->Lchild;
}
}
return 0;//表示没有查到指定元素
}
查找结果时同时返回该元素的父节点值(有助于删除操作)
函数返回值为查找到的元素所在结点
元素所在结点的父结点值通过引用的方式在函数内部进行了传递
//查找对应的结点返回 并传回其父结点
STree search_elem_in_STree(STree tree, ElemType elem, STree& parent) {
//parent使用引用是因为如果查找到对应的元素则将结点的父结点地址赋予parent
parent = NULL;
while (NULL != tree && elem != tree->data) {
parent = tree;
if (elem < tree->data)
tree = tree->Lchild;
else tree = tree->Rchild;
}
return tree;//返回查找到的对应元素结点
}
主函数代码
STree parent;//用来接查找到的元素的父亲结点
STree search=search_elem_in_STree(tree, 40, parent);//查找对应元素并传回
if (search) {
printf("找到对应结点,其值为%d\n", search->data);
}
else {
printf("没有找到对应元素\n");
}
二叉查找树查找结果
二叉查找树的删除
首先判断该树是否为空 空 则直接返回 不用删除
不为空,则先在树中查找到对应的元素结点
当该结点的左子树为空时,直接将右子树移动到该位置即可 释放要删除的元素结点所占空间
当该结点的右子树为空时,则直接左子树移动到该位置即可 释放要删除元素结点所占空间
左右子树皆不为空时
可以将左子树的最大值结点赋值给要删除元素的结点 再将左子树中的最大值结点删除
或者将右子树的最小值结点赋值给要删除的元素的结点 再将右子树中的最小值结点删除
//二叉查找树 根据元素 删除对应结点
void DeleteNode(STree& tree, ElemType elem) {
//如果树为空树 则直接返回不用进行删除操作
if (NULL == tree) {
return;
}
//树不为空
STree parent;//用于接收查找到的元素结点的父结点
STree search;//用于接收查找到的元素的元素结点
search=search_elem_in_STree(tree, elem, parent);//调用查找元素的函数 上面有定义
STree freeDelete;//用来存储要释放的内存空间地址
if (search) {
//此时查找到要删除的元素
if (search->Lchild == NULL) {
//如果要删除元素的左结点为空 直接将要删除元素的右结点接到要删除的位置 释放要删除的元素的空间
freeDelete = search;//指向要删除元素在内存的空间地址
search = search->Rchild;//将删除元素的右结点移至要删除的位置
free(freeDelete);//释放掉该指针指向的内存空间 防止别的指针指向该内存空间时 有数据
}
else if (search->Rchild == NULL) {
//如果要删除元素的右结点为空 直接将要删除元素的左结点接到要删除的位置 释放要删除的元素的空间
freeDelete = search;//指向要删除元素在内存的空间地址
search = search->Lchild;//将删除元素的左结点移至要删除的位置
free(freeDelete);//释放掉该指针指向的内存空间 防止别的指针指向该内存空间时 有数据
}
else {
//要删除元素的左右结点都不为空时,可以将左子树的最大结点 或者 右子树的最小结点 移到要删除元素的位置
//找到要删除结点的左子树中最大值的结点-----------从左子树的根结点起 找其右结点 当右结点为空时 即是
freeDelete = search->Lchild;
while (freeDelete->Rchild != NULL) {
freeDelete = freeDelete->Rchild;
}
//循环结束后 此时freeDelete指向的就是要删除数值的结点的左子树中的最大结点
search->data = freeDelete->data;//将左子树的最大值赋给要删除的数值所在结点
DeleteNode(search->Lchild, freeDelete->data);//将左子树中的最大值从左子树中删除
}
}
}
主函数代码
DeleteNode(tree, 66);
//inOrder(tree);//再将二叉查找树进行一次中序遍历(从小到大)
全部代码展示
//20230104 二叉排序树(二叉查找树)
//创建二叉查找树
typedef int ElemType;
//树的根节点的结构体
typedef struct STNode {
ElemType data;//用来存放数据域
STNode* Lchild, * Rchild;//用来指向该结点的左右结点
}STNode,*STree;
//在树中进行插入的函数(使用递归思想)
void insert(STree& p, ElemType i) {
if (NULL == p) {
p = (STree)malloc(sizeof(STNode));//为根节点申请空间
//根为空,将元素放入该结点
p->data = i;
p->Lchild = NULL;
p->Rchild=NULL;
}
else if (i < p->data) {
//插入元素小于根节点元素 将其插入左子树
insert(p->Lchild, i);
}
else {
//插入元素大于根节点元素,将其插入右子树
insert(p->Rchild, i);
}
}
//创建二叉树的函数
void creat_STree(STree& tree, ElemType str[], int len) {
//因为要对树进行改变,所以使用引用
tree = NULL;
int i=0;//方便遍历数组 取出数据 进行插树
while (i <= len - 1) {
insert(tree, str[i]);//调用插入树元素函数
i++;
}
}
//使用中序遍历(左根右) 进行打印(得出的二叉查找树结果应为从小到大)
void inOrder(STree tree) {
if (tree != NULL) {
inOrder(tree->Lchild);
printf("%3d", tree->data);
inOrder(tree->Rchild);
}
}
//查找对应的元素并传回
//
//int search_elem_in_STree(STree tree, ElemType elem, STree& parent) {
// //parent使用引用是因为如果查找到对应的元素则将结点地址赋予parent
// STree p = tree;
// while (NULL != p) {
// if (elem == p->data) {
// parent = p;
// return 1;//表示查找到当前元素
// }
// else if (elem > p->data) {
// //当前元素比根结点元素大 将该元素与根结点的右结点进行比较查找
// p = p->Rchild;
// }
// else {
// //当前元素比根结点元素小,将该元素与根结点的左结点进行比较查找
// p = p->Lchild;
// }
// }
// return 0;//表示没有查到指定元素
//}
//查找对应的结点返回 并传回其父结点
STree search_elem_in_STree(STree tree, ElemType elem, STree& parent) {
//parent使用引用是因为如果查找到对应的元素则将结点的父结点地址赋予parent
parent = NULL;
while (NULL != tree && elem != tree->data) {
parent = tree;
if (elem < tree->data)
tree = tree->Lchild;
else tree = tree->Rchild;
}
return tree;//返回查找到的对应元素结点
}
//二叉查找树 根据元素 删除对应结点
void DeleteNode(STree& tree, ElemType elem) {
//如果树为空树 则直接返回不用进行删除操作
if (NULL == tree) {
return;
}
//树不为空
STree parent;//用于接收查找到的元素结点的父结点
STree search;//用于接收查找到的元素的元素结点
search=search_elem_in_STree(tree, elem, parent);//调用查找元素的函数 上面有定义
STree freeDelete;//用来存储要释放的内存空间地址
if (search) {
//此时查找到要删除的元素
if (search->Lchild == NULL) {
//如果要删除元素的左结点为空 直接将要删除元素的右结点接到要删除的位置 释放要删除的元素的空间
freeDelete = search;//指向要删除元素在内存的空间地址
search = search->Rchild;//将删除元素的右结点移至要删除的位置
free(freeDelete);//释放掉该指针指向的内存空间 防止别的指针指向该内存空间时 有数据
}
else if (search->Rchild == NULL) {
//如果要删除元素的右结点为空 直接将要删除元素的左结点接到要删除的位置 释放要删除的元素的空间
freeDelete = search;//指向要删除元素在内存的空间地址
search = search->Lchild;//将删除元素的左结点移至要删除的位置
free(freeDelete);//释放掉该指针指向的内存空间 防止别的指针指向该内存空间时 有数据
}
else {
//要删除元素的左右结点都不为空时,可以将左子树的最大结点 或者 右子树的最小结点 移到要删除元素的位置
//找到要删除结点的左子树中最大值的结点-----------从左子树的根结点起 找其右结点 当右结点为空时 即是
freeDelete = search->Lchild;
while (freeDelete->Rchild != NULL) {
freeDelete = freeDelete->Rchild;
}
//循环结束后 此时freeDelete指向的就是要删除数值的结点的左子树中的最大结点
search->data = freeDelete->data;//将左子树的最大值赋给要删除的数值所在结点
DeleteNode(search->Lchild, freeDelete->data);//将左子树中的最大值从左子树中删除
}
}
}
int main() {
//20230104 新年快乐 💪
//二叉排序树(二叉查找树)
//二叉排序树可能是一颗空树
//也可能满足以下条件
//1、若左子树为空,则左子树所有结点的值都小于根节点的值
//2、若右子树为空,则右子树的所有结点的值都大于根节点的值
//3、左右子树也分别是一颗二叉排序树(即:需要满足上述要求)
//创建一颗二叉排序树
//存入数据为 54、20、66、40、28、79、58
STree tree = (STree)malloc(sizeof(STNode));//为树结点指针向内存申请指向空间(地址)
ElemType str[] = { 54,20,66,40,28,79,58 };//要创建的树的值
int len = 7;//加入的总结点个数
//大体逻辑: 插入元素
//插入第一个元素 判断此时树根是不是为空 ,为空将该元素设置为树根
//插入第二个元素,判断此时树根是否为空,不为空,判断与树根的大小
//大于树根 判断其树根的右结点是否为空,为空存入右结点,不为空 继续与树根的右结点比较大小
//小于树根 判断其树根的左结点是否为空,为空存入左结点,不为空,继续与树根的左结点比较大小
creat_STree(tree, str, len);//创建二叉排序树的函数
inOrder(tree);//中序遍历二叉排序树
printf("\n");
//大体逻辑:查找元素
//将查找元素与根元素进行对比 相等返回
//比根元素大 则继续与根的右结点元素进行比较 (相等返回 大与右结点比较 小与左结点比较)
//比根元素小 则继续与根的左结点元素进行比较 (相等返回 大与右结点比较 小与左结点比较)
//对二叉查找树进行查找时 最多查找次数为树的层数
STree parent;//用来接查找到的元素的父亲结点
STree search=search_elem_in_STree(tree, 40, parent);//查找对应元素并传回
if (search) {
printf("找到对应结点,其值为%d\n", search->data);
}
else {
printf("没有找到对应元素\n");
}
//删除元素
//删除元素时,需要先找到对应元素
//当要删除元素左子树为空时,直接将删除元素的右子树接到要删除的位置 释放到要删除的空间
//当要删除元素右子树为空时,直接将删除元素的左子树接到要删除的位置 释放到要删除的空间
//当要删除元素的左右子树都存在时,将左子树的最大值,或右子树的最小值移到要删除的位置 再将对应的左子树或右子树中的最大值删除
DeleteNode(tree, 66);
//inOrder(tree);//再将二叉查找树进行一次中序遍历(从小到大)
return 0;
}