数据结构-二叉查找(排序)树的插入,创建,查找,删除(含全部代码)

目录

主要函数

插入操作部分

遍历操作部分

查找操作部分

删除操作部分(重点)

全部代码

运行结果截图


主要函数

BST_Insert(BSTree &T, TElemType data)  参数T,二叉查找树根节点 作用:插入数据data,保证中序非严格递增(即可重复)
PreOrder(BSTree T)  参数T,二叉查找树根节点 作用:先序遍历二叉查找树,递归方式
InOrder(BSTree T)   参数T,二叉查找树根节点 作用:中序遍历二叉查找树,递归方式
PostOrder(BSTree T) 参数T,二叉查找树根节点 作用:后序遍历二叉查找树,递归方式
LevelOrder(BSTree T)参数T,二叉查找树根节点 作用:层序遍历二叉查找树,递归方式
BST_Search(BSTree T, TElemType key) 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,返回指向该结点的指针,否则,返回空
BST_Delete(BSTree &T, TElemType key) 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,删除,返回true,否则,返回false

CreateBSTree(BSTree &T) 参数T,二叉查找树根节点 作用:调用InitTree,创建二叉查找树
Traverse(BSTree T)      参数T,二叉查找树根节点 作用:PreOrder InOrder PostOrder LevelOrder遍历二叉查找树
InsertBSTree(BSTree &T) 参数T,二叉查找树根节点 作用:插入结点,调用BST_Insert
DeleteBSTree(BSTree &T) 参数T,二叉查找树根节点 作用:删除结点,调用BST_Delete
SearchBSTree(BSTree T)  参数T,二叉查找树根节点 作用:查找结点,调用BST_Search

插入操作部分

分析:stl里面的set和map都有对应的multi,这里的插入就把重复的插在了右边,没有跳过,读者若想要不可重复,可改下面代码,也可以在输入后进行剔除再调用BST_Insert函数。

代码:

//二叉查找树插入 参数T,二叉查找树根节点 作用:插入数据data,保证中序非严格递增(即可重复)
int BST_Insert(BSTree &T, TElemType data)
{
	if (T == NULL)//树为空
	{
		T = (BSTree)malloc(sizeof(BSTNode));//申请结点空间
		T->data = data;                     //赋值
		T->lchild = NULL;                   //左右孩子为空
		T->rchild = NULL;
		return 1;
	}
	else if (data < T->data)               //比当前节点数据小,插入左子树
	{
		return BST_Insert(T->lchild,data);
	}
	else                                   //这里规定二叉查找树可以重复,等于及大于当前结点时,插入右子树
	{
		return BST_Insert(T->rchild,data);
	}
}

遍历操作部分

分析:这里写了先序遍历、中序遍历、后序遍历、层次遍历。前三个都是递归形式,比较简单,就不从重复粘贴代码了。层次遍历借助队列,队列有先进先出的特点,可以访问根,先左孩子入队,再右孩子。

代码:

//层序遍历 借助队列
void LevelOrder(BSTree T)
{
	queue<BSTNode> q;//借助队列
	if (T != NULL)
	{
		BSTNode temp;//暂存要出队的结点
		q.push(*T);//根结点入队
		while (!q.empty())//队列非空
		{
			temp = q.front();
			q.pop();
			cout << temp.data << " ";
			if (temp.lchild != NULL) q.push(*temp.lchild);//队列先进先出,先入左孩子
			if (temp.rchild != NULL) q.push(*temp.rchild);
		}
	}
}

查找操作部分

分析:根据插入时的特点,进行查找,将key值与当前结点数据进行比较。若找到,返回指向该值的指针。

代码:

//二叉查找树查找 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,返回指向该结点的指针,否则,返回空
BSTNode * BST_Search(BSTree T, TElemType key)
{
   BSTNode *cur = T; //当前结点指针cur
   while (cur&&key!=cur->data)
   {
	   if (key<cur->data)     //比当前结点数据小
	   {
		   cur = cur->lchild;
	   }
	   else                  //比当前结点数据大
	   {
		   cur = cur->rchild;
	   }
   }
   return cur;
}

删除操作部分(重点

分析:删除操作基于查找,找到才能删除。由于需要保存待删除结点的双亲结点,这里没有调用查找函数。

待删除结点有三种情况:叶子结点,单支结点,双支结点。每种情况又进行细分。用cur表示实际上待删除的结点。

叶子结点(直接删除)

  • 根结点:根结点又为叶子结点,则树只有一个结点,T=NULL即可。
  • 双亲的左孩子:双亲的左孩子为空即可。
  • 双亲的右孩子:双亲的右孩子为空即可。

根节点的删除就不画了,另外两个以右孩子为例。

待删除结点为叶子结点且为右孩子

单支结点(孩子给双亲)

  • 根结点:判断有哪个孩子即可。共两种情况。
  • 非根结点:待删除结点可能是双亲结点的左孩子或右孩子。待删除结点可能有左孩子或右孩子。共4种情况。   

根结点的比较简单,就不画了。非根结点的,以待删除结点为双亲的右孩子,待删除结点有左孩子为例。

待删除结点为双亲的右孩子,待删除结点有左孩子

双支结点(转化为删除直接后继):

temp为逻辑上需要删除的结点,由于双亲结点不好删除。转化为删除叶子结点或单支结点。最终实际删除结点为cur所指结点。

思路:找到直接后继,交换数据域,删除直接后继(直接后继没有左孩子,不可能为双支结点)。

  • 右子树没有左子树:右子树根节点是待删除结点的直接后继,相当于待删除结点为右孩子,待删除结点有右孩子的单支删除。
  • 右子树有左子树:相当于待删除结点为左孩子,待删除结点有右孩子的单支删除。
右子树没有左子树

 

右子树有左子树

代码:

//二叉查找树删除 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,删除,返回true,否则,返回false
bool BST_Delete(BSTree &T, TElemType key)
{
	BSTNode*  cur = T;
	BSTNode*  cur_par = NULL;//当前结点的双亲结点
	while (cur&&key != cur->data)
	{
		if (key<cur->data)     //比当前结点数据小
		{
			cur_par = cur;    //记录双亲结点
			cur = cur->lchild;
		}
		else                  //比当前结点数据大
		{
			cur_par = cur;    //记录双亲结点
			cur = cur->rchild;
		}
	}
	if (cur)//找到啦,分情况删除:叶子结点(度为0) 单支结点(度为1) 双支结点(度为2)
	{
		if (cur->lchild==NULL&&cur->rchild==NULL)//叶子结点
		{
			if (cur == T)//根节点
			{
				T = NULL;
			}
			else if (cur_par->lchild == cur)//要删除的是cur_par的左孩子
			{
				cur_par->lchild = NULL;
			}
			else
			{
				cur_par->rchild = NULL;
			}
		}
		else if (cur->lchild == NULL || cur->rchild == NULL)//单支结点
		{
			if (cur == T)//根节点
			{
				if (cur->lchild)//有左孩子
				{
					T = cur->lchild;
				}
				else
				{
					T = cur->rchild;
				}
			}
			else //非根结点,双亲结点指向要删除结点的子结点即可
			{
				if (cur_par->lchild == cur&&cur->lchild)//cur为cur的双亲结点的左孩子,且有左孩子
				{
					cur_par->lchild = cur->lchild;
				}
				else if (cur_par->lchild == cur&&cur->rchild)
				{
					cur_par->lchild = cur->rchild;
				}
				else if (cur_par->rchild == cur&&cur->lchild)
				{
					cur_par->rchild = cur->lchild;
				} 
				else{
					cur_par->rchild = cur->rchild;
				}
			}
		}
		else //双支结点  可以选择与直接前驱交换数据域,然后删除直接前驱。
			//或者,与直接后继交换数据域,删除直接后继。这里选择后者。
		{
			BSTNode* temp = cur;//记录需要删除的结点,接下来要与直接后继交换数据域
			cur_par = cur;      //用cur找到temp的直接后继 则cur为temp右子树的最左孩子
			cur = cur->rchild;  //右子树
			while (cur->lchild)//找到直接后继,即右子树的最左孩子(最小值)
			{
				cur_par = cur;
				cur=cur->lchild;
			}
			temp->data = cur->data;//交换数据域
			if (cur_par == temp)//待删除结点的右子树没有左子树,即右子树根节点即为待删除结点后继
			{
				cur_par->rchild = cur->rchild;
			}
			else               //待删除结点的右子树有左子树
			{
				cur_par->lchild = cur->rchild;//将cur的右子树给双亲结点
			}		
		}
		free(cur);
		return true;
	}//if
	return false;
}

全部代码

/*
Project: 二叉查找树(又称二叉排序树,简称BST)
Date:    2019/01/13
Author:  Frank Yu
BST_Insert(BSTree &T, TElemType data)  参数T,二叉查找树根节点 作用:插入数据data,保证中序非严格递增(即可重复)
PreOrder(BSTree T)  参数T,二叉查找树根节点 作用:先序遍历二叉查找树,递归方式
InOrder(BSTree T)   参数T,二叉查找树根节点 作用:中序遍历二叉查找树,递归方式
PostOrder(BSTree T) 参数T,二叉查找树根节点 作用:后序遍历二叉查找树,递归方式
LevelOrder(BSTree T)参数T,二叉查找树根节点 作用:层序遍历二叉查找树,递归方式
BST_Search(BSTree T, TElemType key) 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,返回指向该结点的指针,否则,返回空
BST_Delete(BSTree &T, TElemType key) 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,删除,返回true,否则,返回false

CreateBSTree(BSTree &T) 参数T,二叉查找树根节点 作用:调用InitTree,创建二叉查找树
Traverse(BSTree T)      参数T,二叉查找树根节点 作用:PreOrder InOrder PostOrder LevelOrder遍历二叉查找树
InsertBSTree(BSTree &T) 参数T,二叉查找树根节点 作用:插入结点,调用BST_Insert
DeleteBSTree(BSTree &T) 参数T,二叉查找树根节点 作用:删除结点,调用BST_Delete
SearchBSTree(BSTree T)  参数T,二叉查找树根节点 作用:查找结点,调用BST_Search
*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<stack>
#include<queue>
#include<algorithm>
#include<iostream>
#define TElemType char
using namespace std;
//链式二叉查找树数据结构
typedef struct BSTNode
{
	TElemType data;//数据域
	struct BSTNode *lchild, *rchild;//左右孩子
}BSTNode, *BSTree;
//********************************基本操作函数********************************//
//二叉查找树插入 参数T,二叉查找树根节点 作用:插入数据data,保证中序非严格递增(即可重复)
int BST_Insert(BSTree &T, TElemType data)
{
	if (T == NULL)//树为空
	{
		T = (BSTree)malloc(sizeof(BSTNode));//申请结点空间
		T->data = data;                     //赋值
		T->lchild = NULL;                   //左右孩子为空
		T->rchild = NULL;
		return 1;
	}
	else if (data < T->data)               //比当前节点数据小,插入左子树
	{
		return BST_Insert(T->lchild,data);
	}
	else                                   //这里规定二叉查找树可以重复,等于及大于当前结点时,插入右子树
	{
		return BST_Insert(T->rchild,data);
	}
}
//先序遍历-递归
void PreOrder(BSTree T)
{
	if (T != NULL)
	{
		cout<<T->data<<" ";
		PreOrder(T->lchild);//递归先序遍历左右子树
		PreOrder(T->rchild);
	}
}
//中序遍历-递归
void InOrder(BSTree T)
{
	if (T != NULL)
	{
		InOrder(T->lchild);//递归中序遍历左右子树
		cout << T->data << " ";
		InOrder(T->rchild);
	}
}
//后序遍历-递归
void PostOrder(BSTree T)
{
	if (T != NULL)
	{
		PostOrder(T->lchild);//递归后序遍历左右子树
		PostOrder(T->rchild);
		cout << T->data << " ";
	}
}
//层序遍历 借助队列
void LevelOrder(BSTree T)
{
	queue<BSTNode> q;//借助队列
	if (T != NULL)
	{
		BSTNode temp;//暂存要出队的结点
		q.push(*T);//根结点入队
		while (!q.empty())//队列非空
		{
			temp = q.front();
			q.pop();
			cout << temp.data << " ";
			if (temp.lchild != NULL) q.push(*temp.lchild);//队列先进先出,先入左孩子
			if (temp.rchild != NULL) q.push(*temp.rchild);
		}
	}
}
//二叉查找树查找 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,返回指向该结点的指针,否则,返回空
BSTNode * BST_Search(BSTree T, TElemType key)
{
   BSTNode *cur = T; //当前结点指针cur
   while (cur&&key!=cur->data)
   {
	   if (key<cur->data)     //比当前结点数据小
	   {
		   cur = cur->lchild;
	   }
	   else                  //比当前结点数据大
	   {
		   cur = cur->rchild;
	   }
   }
   return cur;
}
//二叉查找树删除 参数 BSTree T, TElemType key,作用查找key是否存在于二叉查找树中,若存在,删除,返回true,否则,返回false
bool BST_Delete(BSTree &T, TElemType key)
{
	BSTNode*  cur = T;
	BSTNode*  cur_par = NULL;//当前结点的双亲结点
	while (cur&&key != cur->data)
	{
		if (key<cur->data)     //比当前结点数据小
		{
			cur_par = cur;    //记录双亲结点
			cur = cur->lchild;
		}
		else                  //比当前结点数据大
		{
			cur_par = cur;    //记录双亲结点
			cur = cur->rchild;
		}
	}
	if (cur)//找到啦,分情况删除:叶子结点(度为0) 单支结点(度为1) 双支结点(度为2)
	{
		if (cur->lchild==NULL&&cur->rchild==NULL)//叶子结点
		{
			if (cur == T)//根节点
			{
				T = NULL;
			}
			else if (cur_par->lchild == cur)//要删除的是cur_par的左孩子
			{
				cur_par->lchild = NULL;
			}
			else
			{
				cur_par->rchild = NULL;
			}
		}
		else if (cur->lchild == NULL || cur->rchild == NULL)//单支结点
		{
			if (cur == T)//根节点
			{
				if (cur->lchild)//有左孩子
				{
					T = cur->lchild;
				}
				else
				{
					T = cur->rchild;
				}
			}
			else //非根结点,双亲结点指向要删除结点的子结点即可
			{
				if (cur_par->lchild == cur&&cur->lchild)//cur为cur的双亲结点的左孩子,且有左孩子
				{
					cur_par->lchild = cur->lchild;
				}
				else if (cur_par->lchild == cur&&cur->rchild)
				{
					cur_par->lchild = cur->rchild;
				}
				else if (cur_par->rchild == cur&&cur->lchild)
				{
					cur_par->rchild = cur->lchild;
				} 
				else{
					cur_par->rchild = cur->rchild;
				}
			}
		}
		else //双支结点  可以选择与直接前驱交换数据域,然后删除直接前驱。
			//或者,与直接后继交换数据域,删除直接后继。这里选择后者。
		{
			BSTNode* temp = cur;//记录需要删除的结点,接下来要与直接后继交换数据域
			cur_par = cur;      //用cur找到temp的直接后继 则cur为temp右子树的最左孩子
			cur = cur->rchild;  //右子树
			while (cur->lchild)//找到直接后继,即右子树的最左孩子(最小值)
			{
				cur_par = cur;
				cur=cur->lchild;
			}
			temp->data = cur->data;//交换数据域
			if (cur_par == temp)//待删除结点的右子树没有左子树,即右子树根节点即为待删除结点后继
			{
				cur_par->rchild = cur->rchild;
			}
			else               //待删除结点的右子树有左子树
			{
				cur_par->lchild = cur->rchild;//将cur的右子树给双亲结点
			}		
		}
		free(cur);
		return true;
	}//if
	return false;
}
//**********************************功能实现函数*****************************//
//调用BST_Insert进行二叉查找树的创建
void CreateBSTree(BSTree &T)
{
	string s;
	printf("请输入二叉树结点数据创建二叉查找树(结点字符串):");
	cin >> s;
	for (int i=0;i<s.length();i++)
	{
		BST_Insert(T,s[i]);
	}
	printf("二叉查找树先序遍历序列:");
	PreOrder(T);
	printf("\n");
}
//遍历功能函数 调用PreOrder InOrder PostOrder LevelOrder
void Traverse(BSTree T)
{
	int choice;
	while (1)
	{
		printf("********1.先序遍历    2.中序遍历*********\n");
		printf("********3.后序遍历    4.层次遍历*********\n");
		printf("********5.返回上一单元\n");
		printf("请输入菜单序号:\n");
		scanf("%d", &choice);
		if (5 == choice) break;                  
		switch (choice)
		{
		case 1: {printf("二叉查找树先序遍历序列:");PreOrder(T);printf("\n");}break;
		case 2: {printf("二叉查找树中序遍历序列:");InOrder(T);printf("\n");}break;
		case 3: {printf("二叉查找树后序遍历序列:");PostOrder(T);printf("\n");}break;
		case 4: {printf("二叉查找树层次遍历序列:");LevelOrder(T);printf("\n");}break;
		default:printf("输入错误!!!\n");break;
		}
	}
}
//插入 调用BST_Insert
void InsertBSTree(BSTree &T)
{
	TElemType data;
	printf("请输入要插入的数据:\n");
	cin >> data;
	BST_Insert(T,data);
	printf("插入成功!!!\n");
}
//删除 调用BST_Delete
void DeleteBSTree(BSTree &T)
{
	TElemType key;
	printf("请输入要删除的数据:\n");
	cin >> key;
	if (BST_Delete(T, key))
	{
		printf("删除成功!\n");
	}
	else
	{
		printf("未找到!\n");
	}
}
//查找 调用BST_Search
void SearchBSTree(BSTree T)
{
	TElemType data;
	BSTNode *p;
	printf("请输入要查找的数据:\n");
	cin >> data;
	p = BST_Search(T, data);
	if (p) 
	{
		printf("该数据存在!!!\n");
	}
	else
		printf("该数据不存在!!!\n");
}
//菜单
void menu()
{
	printf("********1.创建    2.遍历*********\n");
	printf("********3.插入    4.查找*********\n");
	printf("********5.删除    6.退出*********\n");
}
//主函数
int main()
{
	BSTree T = NULL;int choice = 0;
	while (1)
	{
		menu();
		printf("请输入菜单序号:\n");
		scanf("%d", &choice);
		if (6 == choice) break;
		switch (choice)
		{
		case 1:CreateBSTree(T);break;
		case 2:Traverse(T);break;
		case 3:InsertBSTree(T);break;
		case 4:SearchBSTree(T);break;
		case 5:DeleteBSTree(T);break;
		default:printf("输入错误!!!\n");break;
		}
	}
	return 0;
}

运行结果截图

创建及遍历部分运行截图
其他功能部分截图

 

截图所用二叉树及变换

数据类型定义为char,可以不仅仅用数字

更多数据结构与算法实现:数据结构(严蔚敏版)与算法的实现(含全部代码)

有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lady_killer9

感谢您的打赏,我会加倍努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值