随机产生一组关键字,利用二叉排序树的插入算法建立二叉排序树,然后删除某一指定关键字元素。
目录
1、二叉排序树的结构
2、插入结点
3、查找结点
4、创建二叉树
5、删除结点
6、完整测试源码
就是一般二叉树的结构。
//二叉排序树
typedef struct BST {
int data; //值域
struct BST* left; //左孩子
struct BST* right; //右孩子
}*BST;
递归方式,找到要插入值的位置,然后进行插入。
//插入结点
void InsertBSTree(BST* root, int k) {//root是一个二级指针,是二叉树根节点的地址,k是要插入的值
if (!(*root)) {//当节点为空值时,这就是要插入的位置
BST p = (BST)malloc(sizeof(struct BST));
p->data = k;
p->left = p->right = NULL;
*root = p;
}
else if ((*root)->data >= k)//当前结点值大于要插入的值,那就进入左子树
InsertBSTree(&(*root)->left, k);
else //当前结点值小于要插入的值,那就进入右子树
InsertBSTree(&(*root)->right, k);
}
还是用递归,一直比较结点值与要查找的值。
//查找结点
bool FindBSTree(BST root, int k) {
//找到了返回真,没找到返回假
if (!root)
return false;//空树返回假
if (root->data == k)
return true;//找到了返回真
if (root->data >= k)
return FindBSTree(root->left, k);//去左子树里面找
if (root->data < k)
return FindBSTree(root->right, k);//去右子树里面找
}
用随机数函数生成随机数,把生成的随机数当成关键字次次调用插入函数,完成树的创建。
因为rand函数每一次产生的随机数是相同的,所以要用srand函数设置一个随机数种子,保证每一次运行程序生成的随机数不是一样的,所以我们一般用系统时间作随机数种子,因为时间一直在改变;
因为srand取得是系统时间,并且是以秒为单位,但是for循环一次远远小于1秒,这样就会导致种子没有变化,产生的随机数也不会有变化,所以要让两次rand函数之间间隔1秒以上,所以用以恶搞sleep函数。
//创建二叉树
//循环调用插入函数来完成创建
void CreateBSTree(BST* root,int num) {
int a;//a是随机数作结点值
for (int i = 0; i < num; i++) {
srand((unsigned)time(0));//随机数种子
a = rand() % 100;//0到100范围内的随机数
Sleep(1000);//停留1s
if ((*root)&&FindBSTree(*root, a))
//不为空树并且树里面没有该值,如果树里有这个值就会执行continue语句从而重新生成一个数字
continue;
//因为srand((unsigned)time(NULL));取的是系统时间,也就是距离1970.1.1午夜有多少秒。而for循环一次远远小于1s,所以随机值种子不会变化,所以加一个sleep间隔一秒
//调用插入函数
InsertBSTree(root, a);
}
}
删除结点有四种情况:
中序遍历下的二叉排序树是有序的。
//删除结点
//先用函数判断要删除结点的位置
bool DeleteBSTree(BST* root, int k) {
if ((*root) == NULL)//空树或找不到目标值
return false;
if ((*root)->data == k)//找到目标值
return Delete(root);
if ((*root)->data > k)//当前结点值大于目标值,进入左子树找
return DeleteBSTree(&(*root)->left, k);
if ((*root)->data < k)//当前结点值小于目标值,进入右子树找
return DeleteBSTree(&(*root)->right, k);
}
//删除的核心操作
bool Delete(BST* root) {
BST pre, s;//pre作目标节点的前一个,s作要删除的结点
//1、右子树为空或叶子节点
if ((*root)->right == NULL) {
pre = *root; //保存好要删除的结点
*root = (*root)->left; //用他的左儿子代替这个位置
free(pre); //再释放掉要删除的结点
}
//2、左子树为空
else if ((*root)->left == NULL) {
pre = *root; //保存好要删除的结点
*root = (*root)->right; //用他的右儿子代替这个位置
free(pre); //再释放掉要删除的结点
}
//3、左右子树均不为空
//用要删除的结点的前驱节点的值来代替要删除的结点的值,然后再把这个前驱节点删除掉就可以了
else {
pre = *root; //保存好要删除的结点
s = (*root)->left; //s是他左子树的根节点
while (s->right) { //循环找到他左子树的最右的结点,也就是找到要删除结点的前驱节点
pre = s; //两个指针依次向下
s = s->right; //
}
//退出循环,s是被删结点的前驱节点,pre是s的前驱节点
(*root)->data = s->data;//这里是赋值,用前驱的值代替被删除的结点的值
if (pre != (*root)) //这里不等于代表着经过了循环,
pre->right = s->left;//因为前面已经把值赋过去了,后面会有删除,又因为s是(*root)的直接前驱说明s没有右儿子,所以这里把s可能有的左儿子连接到pre上
else //这里代表着没有经过循环,也就是说,s直接是(*root)的直接前驱
pre->left = s->left;
free(s); //这里是删除,释放掉这个前驱结点,因为前驱结点的值和儿子都交代完了就可以不要了
}
return true;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
/*
3.随机产生一组关键字,利用二叉排序树的插入算法建立二叉排序树,然后删除某一指定关键字元素。
*/
//二叉排序树
typedef struct BST {
int data;
struct BST* left;
struct BST* right;
}*BST;
//插入结点
void InsertBSTree(BST* root, int k) {
if (!(*root)) {//当节点为空值时
BST p = (BST)malloc(sizeof(struct BST));
p->data = k;
p->left = p->right = NULL;
*root = p;
}
else if ((*root)->data >= k)//当前结点值大于要插入的值,那就进入左子树
InsertBSTree(&(*root)->left, k);
else //当前结点值小于要插入的值,那就进入右子树
InsertBSTree(&(*root)->right, k);
}
//查找结点
bool FindBSTree(BST root, int k) {
//找到了返回真,没找到返回假
if (!root)
return false;//空树返回假
if (root->data == k)
return true;//找到了返回真
if (root->data >= k)
return FindBSTree(root->left, k);//去左子树里面找
if (root->data < k)
return FindBSTree(root->right, k);//去右子树里面找
}
//中序遍历打印二叉树
void Inoder(BST root) {
if (!root)
return;
Inoder(root->left);
printf("%d ", root->data);
Inoder(root->right);
}
//创建二叉树
//循环调用插入函数来完成创建,如果
void CreateBSTree(BST* root,int num) {
int a;//a是随机数作结点值
for (int i = 0; i < num; i++) {
srand((unsigned)time(0));//随机数种子
a = rand() % 100;
Sleep(1000);//停留1s
if ((*root)&&FindBSTree(*root, a))//不为空树并且树里面没有该值,如果树里有这个值就会执行continue语句从而重新生成一个数字
continue;
//因为srand((unsigned)time(NULL));取的是系统时间,也就是距离1970.1.1午夜有多少秒。而for循环一次远远小于1s,所以随机值种子不会变化,所以加一个sleep间隔一秒
InsertBSTree(root, a);
}
}
//删除结点
bool Delete(BST* root) {
BST pre, s;//pre作目标节点的前一个,s作要删除的结点
//1、右子树为空或叶子节点
if ((*root)->right == NULL) {
pre = *root; //保存好要删除的结点
*root = (*root)->left; //用他的左儿子代替这个位置
free(pre); //再释放掉要删除的结点
}
//2、左子树为空
else if ((*root)->left == NULL) {
pre = *root; //保存好要删除的结点
*root = (*root)->right; //用他的右儿子代替这个位置
free(pre); //再释放掉要删除的结点
}
//3、左右子树均不为空
//用要删除的结点的前驱节点的值来代替要删除的结点的值,然后再把这个前驱节点删除掉就可以了
else {
pre = *root; //保存好要删除的结点
s = (*root)->left; //s是他左子树的根节点
while (s->right) { //循环找到他左子树的最右的结点,也就是找到要删除结点的前驱节点
pre = s; //两个指针依次向下
s = s->right; //
}
//退出循环,s是被删结点的前驱节点,pre是s的前驱节点
(*root)->data = s->data;//这里是赋值,用前驱的值代替被删除的结点的值
if (pre != (*root)) //这里不等于代表着经过了循环,
pre->right = s->left;//因为前面已经把值赋过去了,后面会有删除,又因为s是(*root)的直接前驱说明s没有右儿子,所以这里把s可能有的左儿子连接到pre上
else //这里代表着没有经过循环,也就是说,s直接是(*root)的直接前驱
pre->left = s->left;
free(s); //这里是删除,释放掉这个前驱结点,因为前驱结点的值和儿子都交代完了就可以不要了
}
return true;
}
bool DeleteBSTree(BST* root, int k) {
if ((*root) == NULL)//空树或找不到目标值
return false;
if ((*root)->data == k)//找到目标值
return Delete(root);
if ((*root)->data > k)//当前结点值大于目标值,进入左子树找
return DeleteBSTree(&(*root)->left, k);
if ((*root)->data < k)//当前结点值小于目标值,进入右子树找
return DeleteBSTree(&(*root)->right, k);
}
void menu() {
printf("---------\n1、插入\n");
printf("2、删除\n---------\n");
}
void main() {
BST bstree=NULL;
int chose;
int num;//num作插入值
int num1;//num1作结点个数,
printf("输入结点个数");
scanf("%d", &num1);
printf("请稍等...\n");
CreateBSTree(&bstree,num1);
printf("二叉排序树:");
Inoder(bstree);//先遍历输出一次
printf("\n");
while (1) {
menu();
scanf("%d", &chose);
switch (chose) {
case 1:
printf("请输入要插入的值:");
scanf("%d", &num);
InsertBSTree(&bstree,num);
printf("新树:");
Inoder(bstree);
printf("\n");
break;
case 2:
printf("请输入要删除的值:");
scanf("%d", &num);
if (DeleteBSTree(&bstree, num)) {
printf("新树:");
Inoder(bstree);
printf("\n");
}
else
printf("没有这个值!\n");
break;
default:return 0;
}
}
}