目录
主要函数
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,可以不仅仅用数字
更多数据结构与算法实现:数据结构(严蔚敏版)与算法的实现(含全部代码)
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。