每一个点开我的文章的小伙伴大家好!
在发这篇文章的时候,我是一名大一的学生,我将在博客记载我的学习之路!
这是我的第四篇博客文章,是关于C语言数据结构的BST树的操作。
接下来我会把各个操作给分解 文章末尾附上源码(Visual Studio2022完美运行)!
先给小伙伴们看一下页面:
在直接进入正题之前,我要给大家提个醒,就是在实现生成BST树的时候,大家的页面可能出现这种情况:
不过大家不要慌,可能是我限制了随机数的范围和time.h头文件的功能的限制,大家只需要多试几次就好了,我反正是试了五六次才出来以下界面:
接着大家就可以按这个顺序实现功能啦!!
一、首先是代码开头的各种定义:
#define _CRT_SECURE_NO_WARNINGS //加上这个,我们就不用scanf_s,可以用scanf了
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h> //在这题中这个头文件是用来生成各不相同的随机数,有兴趣了解它的用法的//可以去查查
typedef struct Node
{
int key; /* 关键字域 */
struct Node* Lchild, * Rchild; //左右孩子
}BSTNode;
BSTNode* q,*f; //这是我定义的两个全局变量,因为后续的while循环或者别的循环中会用到,
//为了避免在局部退出循环之后q,f不更新值,所以我直接定义成全局变量
二、接着是BST树的建立:
这里我用随机生成的50个互不相同的整数作为关键字来建立BST树
这里的非递归建立BST树思路大家可以参考一下,至于为什么没有放上来递归的思路,主要是因为我写出来的递归算法运行到后面有误,希望有看到这篇文章的大佬能提供些帮助(我也不知道哪个环节出了问题 = 。= )
void CreatBST(BSTNode* T)
{
system("cls");
int min;
int t;
int a[100];
srand(time(0)); //避免生成的随机数有重复的函数
//从a[1]开始存储 个人习惯从1存储开始
for (int i = 1; i <= 50; i++)
{
a[i] = 200 + rand() % (801); //Rand()函数 生成随机数
}
BSTNode* p, * r;
T->key = a[1]; T->Lchild = T->Rchild = NULL; //初始化,先为根节点赋值
for (int i = 2; i <= 50; i++)
{
p = T; //设置多一个指针,p为了代替根节点指针 T 进行移动
r = (BSTNode*)malloc(sizeof(BSTNode));
r->key = a[i]; r->Lchild = r->Rchild = NULL; //新建立待插入的结点
while (p != NULL) //当p移动到空的时候,就可以直接作为新结点插入
{
q = p; // q的作用是作为p的父母结点,在p指向空的时候与r进行比较大小来决定r插入在q的左孩子结点还是右孩子结点
if (p->key > r->key) p = p->Lchild;
else if (p->key < r->key) p = p->Rchild;
}//p到空的结点的时候就可以判断它的父母结点与待插入的结点的值的大小了
if (q->key > r->key)
q->Lchild = r;
else q->Rchild = r;
}
}
三、接着是中序遍历输出BST树:
这里推荐大家去看我主页的第二篇关于二叉树的文章,里面还有好几种关于二叉树的输出方式,这里的中序遍历算法就是跟第二篇文章差不多的
void InorderTraverse(BSTNode* T)
/*非递归中序遍历二叉树*/
{
BSTNode* s[1000], * p; //数组s是模拟栈的功能 存储BSTNode类型的结点
int top = 0;
int flag = 1;
p = T;
if (T == NULL) printf("该树为空树\n");
else {
do {
while (p)
{
s[++top] = p;
p = p->Lchild;
}
if (top == 0) flag = 0;
else {
p = s[top--];
printf("%d ", p->key);
p = p->Rchild;
}
} while (flag != 0);
}
}
四、接着就是查找关键字:
int SerachBST(BSTNode* T, int key) //查找关键字key
{
int count = 0; //记录查找的次数
BSTNode* a = T; //用指针a代替根节点在树中移动,避免对根节点指针的破坏
while (a != NULL && a->key != key)
{
if (a->key > key)
a = a->Lchild;
else if (a->key < key)
a = a->Rchild;
count++;
}
if (key == a->key) {
printf("查找了%d次", count);
return 1; //查找成功标志"1"
}
else return 0; //查找失败标志"0" 在主函数判断
}
五、接着是删除关键字:(建议大家删除BST树中已有的关键字,不然后续中序遍历输出可能出问题)
BSTNode* DeleteKey(BSTNode* T, int key)
{
BSTNode* a = T,*s; //用指针a代替根节点在树中移动,避免对根节点指针的破坏
int count = 0;
while (a != NULL && a->key != key)
{
if (a->key > key)
a = a->Lchild;
else if (a->key < key)
a = a->Rchild;
count++;
}
printf("查找%d次后查找到待删除的数据%d\n", count, key);
if (a == NULL) return T;
s = a; /* 找到了要删除的结点为p */
if (a->Lchild != NULL && a->Rchild != NULL)
{
f = a; s = a->Lchild; /* 从左子树开始找 */
while (s->Rchild != NULL)
{
f = s; s = s->Rchild;
}
/* 左、右子树都不空,找左子树中最右边的结点 */
a->key = s->key;
/* 用结点s的内容替换结点p内容 */
} /* 将第3种情况转换为第2种情况*/
if (s->Lchild != NULL) /* 若s有左子树,右子树为空 */
q = s->Lchild;
else q = s->Rchild;
if (f == NULL) T = q;
else if (f->Lchild == s) f->Lchild = q;
else f->Rchild = q;
free(s);
return T;
}
六、最后就是我们的主函数了:
void main()
{
int c, d, e;
BSTNode* T = (BSTNode*)malloc(sizeof(BSTNode));
char ch[100];
while (1)
{
printf("\n==================================================================\n");
printf("| 1.生成50个在[200, 1000]之间互不相同的整数并且建立BST树 |\n");
printf("| 2.中序遍历的方式输出BST树 |\n");
printf("| 3.在BST树中查找从键盘上输入的关键字的结点 |\n");
printf("| 4.在BST树中删除从键盘上输入的关键字的结点 |\n");
printf("| 5.中序遍历的方式输出删除数据后的BST树 |\n");
printf("| 6.退出程序 |\n");
printf("====================================================================\n");
printf("请输入要进行的操作:");
scanf("%s", ch);
if (strcmp(ch, "1") == 0)
{
CreatBST(T);
printf("成功创建BST树!\n");
}
else if (strcmp(ch, "2") == 0)
{
system("cls");
printf("输出中序遍历后的BST树:\n");
InorderTraverse(T);
printf("输出完成!\n");
}
else if (strcmp(ch, "3") == 0)
{
printf("\n输入你要查找的整数:\n");
scanf("%d", &d);
c = SerachBST(T, d);
if (c == 1)
printf("查找成功!\n");
else printf("查找失败!\n");
}
else if (strcmp(ch, "4") == 0)
{
system("cls");
printf("\n输入你要删除的整数:\n");
scanf("%d", &e);
DeleteKey(T, e);
printf("成功删除BST树中数据%d!\n", e);
}
else if (strcmp(ch, "5") == 0)
{
printf("输出删除%d之后中序遍历后的BST树:\n",e);
InorderTraverse(T);
}
else if (strcmp(ch, "6") == 0)
{
system("cls");
printf("谢谢使用!\n");
exit(0);
}
}
}
最后就附上我们的源代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
typedef struct Node
{
int key; /* 关键字域 */
struct Node* Lchild, * Rchild; //左右孩子
}BSTNode;
BSTNode* q,*f;
void CreatBST(BSTNode* T)
{
system("cls");
int min;
int t;
int a[100];
srand(time(0)); //避免生成的随机数有重复的
//从a[1]开始存储
for (int i = 1; i <= 50; i++)
{
a[i] = 200 + rand() % (801); //Rand()函数 生成随机数
}
BSTNode* p, * r;
T->key = a[1]; T->Lchild = T->Rchild = NULL; //初始化,先为根节点赋值
for (int i = 2; i <= 50; i++)
{
p = T; //设置多一个指针,p为了代替根节点指针 T 进行移动
r = (BSTNode*)malloc(sizeof(BSTNode));
r->key = a[i]; r->Lchild = r->Rchild = NULL; //新建立待插入的结点
while (p != NULL) //当p移动到空的时候,就可以直接作为新结点插入
{
q = p; // q的作用是作为p的父母结点,在p指向空的时候与r进行比较大小来决定r插入在q的左孩子结点还是右孩子结点
if (p->key > r->key) p = p->Lchild;
else if (p->key < r->key) p = p->Rchild;
}//p到空的结点的时候就可以判断它的父母结点与待插入的结点的值的大小了
if (q->key > r->key)
q->Lchild = r;
else q->Rchild = r;
}
}
void Inoder(BSTNode* T)
{
if (T != NULL)
{
Inoder(T->Lchild);
printf("%d ", T->key);
Inoder(T->Rchild);
}
}
void InorderTraverse(BSTNode* T)
/*非递归中序遍历二叉树*/
{
BSTNode* s[1000], * p;
int top = 0;
int flag = 1;
p = T;
if (T == NULL) printf("该树为空树\n");
else {
do {
while (p)
{
s[++top] = p;
p = p->Lchild;
}
if (top == 0) flag = 0;
else {
p = s[top--];
printf("%d ", p->key);
p = p->Rchild;
}
} while (flag != 0);
}
}
int SerachBST(BSTNode* T, int key) //查找关键字key
{
int count = 0; //记录查找的次数
BSTNode* a = T; //用指针a代替根节点在树中移动,避免对根节点指针的破坏
while (a != NULL && a->key != key)
{
if (a->key > key)
a = a->Lchild;
else if (a->key < key)
a = a->Rchild;
count++;
}
if (key == a->key) {
printf("查找了%d次", count);
return 1; //查找成功标志"1"
}
else return 0; //查找失败标志"0" 在主函数判断
}
BSTNode* DeleteKey(BSTNode* T, int key)
{
BSTNode* a = T,*s; //用指针a代替根节点在树中移动,避免对根节点指针的破坏
int count = 0;
while (a != NULL && a->key != key)
{
if (a->key > key)
a = a->Lchild;
else if (a->key < key)
a = a->Rchild;
count++;
}
printf("查找%d次后查找到待删除的数据%d\n", count, key);
if (a == NULL) return T;
s = a; /* 找到了要删除的结点为p */
if (a->Lchild != NULL && a->Rchild != NULL)
{
f = a; s = a->Lchild; /* 从左子树开始找 */
while (s->Rchild != NULL)
{
f = s; s = s->Rchild;
}
/* 左、右子树都不空,找左子树中最右边的结点 */
a->key = s->key;
/* 用结点s的内容替换结点p内容 */
} /* 将第3种情况转换为第2种情况*/
if (s->Lchild != NULL) /* 若s有左子树,右子树为空 */
q = s->Lchild;
else q = s->Rchild;
if (f == NULL) T = q;
else if (f->Lchild == s) f->Lchild = q;
else f->Rchild = q;
free(s);
return T;
}
void main()
{
int c, d, e;
BSTNode* T = (BSTNode*)malloc(sizeof(BSTNode));
char ch[100];
while (1)
{
printf("\n==================================================================\n");
printf("| 1.生成50个在[200, 1000]之间互不相同的整数并且建立BST树 |\n");
printf("| 2.中序遍历的方式输出BST树 |\n");
printf("| 3.在BST树中查找从键盘上输入的关键字的结点 |\n");
printf("| 4.在BST树中删除从键盘上输入的关键字的结点 |\n");
printf("| 5.中序遍历的方式输出删除数据后的BST树 |\n");
printf("| 6.退出程序 |\n");
printf("====================================================================\n");
printf("请输入要进行的操作:");
scanf("%s", ch);
if (strcmp(ch, "1") == 0)
{
CreatBST(T);
printf("成功创建BST树!\n");
}
else if (strcmp(ch, "2") == 0)
{
system("cls");
printf("输出中序遍历后的BST树:\n");
InorderTraverse(T);
printf("输出完成!\n");
}
else if (strcmp(ch, "3") == 0)
{
printf("\n输入你要查找的整数:\n");
scanf("%d", &d);
c = SerachBST(T, d);
if (c == 1)
printf("查找成功!\n");
else printf("查找失败!\n");
}
else if (strcmp(ch, "4") == 0)
{
system("cls");
printf("\n输入你要删除的整数:\n");
scanf("%d", &e);
DeleteKey(T, e);
printf("成功删除BST树中数据%d!\n", e);
}
else if (strcmp(ch, "5") == 0)
{
printf("输出删除%d之后中序遍历后的BST树:\n",e);
InorderTraverse(T);
}
else if (strcmp(ch, "6") == 0)
{
system("cls");
printf("谢谢使用!\n");
exit(0);
}
}
}