二叉树及其应用(增删改查)
一、树的概念
树(tree)是n(n>0)个结点的有限集.在任何一个非空树中
1)有且仅有一个特定的称为根的结点
2)当n>1时,其余结点可分为m个互不相交的有限集T1、T2,,,Tm
其中每个集合本身也是一颗树,叫根的子树
树的结点包含一个数据元素以及若干个指向其子树的分支
结点的度:结点拥有的子树的数量,称之为结点的度。度为0的结点称之为叶子结点或者终端结点。
度不为0的结点,称之为分支结点或者非终端结点
结点的层次:从根开始定义起,根为第一层,根的孩子为第二层,树中结点的最大层次叫树的深度或者高度
二、二叉树
二叉树是一种树状结构,它的特点是每个结点最多只有两颗子树,也就是二叉树不存在度大于2的结点。
并且二叉树的子树有左右之分,不能随意颠倒次序
二叉树的五种形态
1、空二叉树(一个结点都没有的二叉树)
2、只有一个根结点的二叉树
3、只有左子树
4、只有右子树
5、完全二叉树
三、二叉树的性质
1、在二叉树的第i层上至多有2^(i-1)个结点(i>=1)
2、深度为K的二叉树最多有(2^k)-1个结点
3、对于任意一颗二叉树,如果其终端节点数为n0,度为2的结点为n2,则有n2=n0-1
1)满二叉树:一颗深度为K且具有(2^K)-1个结点的二叉树,满二叉树
2)完全二叉树:满足以下条件的二叉树
a:除去最后一层为满二叉树
b:最后一层的结点必须依次从左往右紧密排列
练习:结点数699的完全二叉树的叶子结点(终端结点)的数量为多少?
2^9-1=511
699-511=188
K=9+1=10层
10层最多为1024
4、具有n个结点的完全二叉树的深度为(log2N)+1
四、二叉树的存储结构
1、顺序存储结构
把一颗普通二叉树,填补成完全二叉树,然后按自上向下,从左往右进行编号(可能造成浪费内存空间)
2、链式存储结构
typedef int TElemType_t
struct BiTNode
{
TElemType_t data;//数据域
struct BiTNode *lchild;//left 存储当前结点的左子结点的地址
struct BiTNode *rchild;//right 存储当前结点的右子结点的地址
};
五、二叉树的遍历
如何按照某条搜索路径访问树中的每个结点,使每个结点都被访问一次,而且仅被访问一次
先序遍历:根 左 右
中序遍历:左 根 右
后序遍历:左 右 根
1)先序 中序 后序都是针对“根”的位置来讲的
2)若左右不是终端结点,则进去递归遍历
六、二叉排序树也叫二叉查找树
1、概念
二叉排序树,要么是空树,要么具有以下的性质的二叉树
1)若它的左子树不为空,则左子树上所有的结点的值都小于它的根结点
2)若它的右子树不为空,则右子树上所有的结点的值都大于它的根结点
3)它的左右子树也分别为二叉排序树
2、编程实现
1)设计
typedef int TElemType_t
typedef struct BiTNode
{
TElemType_t data;//数据域
struct BiTNode *lchild;//left 存储当前结点的左子结点的地址
struct BiTNode *rchild;//right 存储当前结点的右子结点的地址
}BiTNode_t;
//在r为根结点的二叉排序树中,插入值为inputdata的结点
BiTNode_t *insert_nodeToTree(BiTNode_t *r,TElemType_t inputdata)
{
//1.新建结点
BiTNode_t *newNode=malloc(sizeof(BiTNode_t));
if(newNode==NULL)
{
printf("malloc newNode error\n");
return r;
}
//2.初始化新结点
newNode->data=inputdata;
newNode->lchild=newNode->rchild=NULL;
//3.遍历二叉排序树,找到合适的位置插入新结点
//1)从无到有----新结点就是树的根节点
if(r==NULL)
{
r=newNode;
}
//2)从少到多
else
{
BiTNode_t *p=r;
while(p)
{
//插入的数据比当前遍历结点的数据小,那么就去当前结点左子树中查找
if(inputdata<p->data)
{
//如果当前结点的左子结点为NULL,直接插入
if(p->lchild==NULL)
{
p->lchild=newNode;
break;//插入成功后退出
}
else//如果不为空,往下面遍历
{
p=p->lchild;
}
}
//插入的数据比当前遍历结点的数据大,那么就去当前结点右子树中查找
else if(inputdata>p->data)
{
//如果当前结点的右子结点为NULL,直接插入
if(p->rchild==NULL)
{
p->rchild=newNode;
break;//插入成功后退出
}
else//如果不为空,往下面遍历
{
p=p->rchild;
}
}
//如果是相等的就直接退出
else
{
printf("此时结点是相等的,冲突\n");
free(newNode);
break;
}
}
}
//4.返回根结点
return r;
}
//2、创建二叉树排序树
BiTNode_t *create_sort_tree()
{
BiTNode_t *r=NULL;
//1、从键盘获取数据,将这个数据作为新结点的值,接着将新结点插入二叉树中
printf("please inputData\n");
while(1)
{
TElemType_t inputData;
scanf("%d",&inputData);
if(inputData==-1)
break;
//插入到二叉排序中
r=insert_nodeToTree(r,inputData);
}
//返回根结点
return r;
}
//先序遍历:根 左 右
void pre_allToTree(BiTNode_t *r)
{
if(r==NULL)
return;
printf("%d",r->data);
pre_allToTree(r->lchild);
pre_allToTree(r->rchild);
}
//中序遍历:左 根 右
void mid_allToTree(BiTNode_t *r)
{
if(r==NULL)
return;
pre_allToTree(r->lchild);
printf("%d",r->data);
pre_allToTree(r->rchild);
}
//后序遍历:左 右 根
void post_allToTree(BiTNode_t *r)
{
if(r==NULL)
return;
pre_allToTree(r->lchild);
pre_allToTree(r->rchild);
printf("%d",r->data);
}
//查找
bool find_nodeToTree(BiTNode_t *r,TElemType_t findData)
{
//1.先做判断
if(r==NULL)
return false;
//遍历
BiTNode_t *P=r;
while(p)
{
if(findData==p->data)
return true;
else if(findData<p->data)
{
p=p->lchild;
}
else if(findData>p->data)
{
p=p->rchild;
}
}
return true;
}
七、二叉树增删改查例子---demo.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int TElemType_t;
typedef struct BiTNode
{
TElemType_t data;
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode_t;
BiTNode_t *insert_nodeToTree(BiTNode_t *r,TElemType_t inputdata)
{
BiTNode_t *newNode = malloc(sizeof(BiTNode_t));
if(newNode == NULL)
{
printf("malloc newNode errror!\n");
return r;
}
newNode->data = inputdata;
newNode->lchild = NULL;
newNode->rchild = NULL;
if(r == NULL)
{
r = newNode;
}
else
{
BiTNode_t *p = r;
while(p)
{
if(inputdata < p->data)
{
//如果当前结点的左子节点为NULL,直接插入
if(p->lchild == NULL)
{
p->lchild = newNode;
break;
}
else//如果不为NULL,往下遍历,找到为止
{
p = p->lchild;
}
}
else if(inputdata > p->data)
{
//如果当前结点的右子节点为NULL,直接插入
if(p->rchild == NULL)
{
p->rchild = newNode;
break;
}
else//如果不为NULL,往下遍历,找到为止
{
p = p->rchild;
}
}
else//相等,直接退出
{
printf("此时结点的值是相等的,直接退出!\n");
free(newNode);
break;
}
}
}
return r;//返回新结点
}
BiTNode_t *creat_sort_tree()
{
BiTNode_t *r = NULL;
printf("请输入你的数据:\n");
while(1)
{
TElemType_t inputdata;
scanf("%d",&inputdata);
if(inputdata == -1)
break;
r = insert_nodeToTree(r,inputdata);
}
return r;
}
void pre_allToTree(BiTNode_t *r)//先序遍历:根->左->右
{
if(r == NULL)
return;
printf("%d->",r->data);
pre_allToTree(r->lchild);
pre_allToTree(r->rchild);
}
void mid_allToTree(BiTNode_t *r)//中序遍历:左->根->右
{
if(r == NULL)
return;
mid_allToTree(r->lchild);
printf("%d->",r->data);
mid_allToTree(r->rchild);
}
void last_allToTree(BiTNode_t *r)//后序遍历:左->右->根
{
if(r == NULL)
return;
last_allToTree(r->lchild);
last_allToTree(r->rchild);
printf("%d->",r->data);
}
/*查找*/
bool find_nodeToTree(BiTNode_t *r,TElemType_t findData)
{
if(r == NULL)
return false;
//遍历
BiTNode_t *p = r;
while(p)
{
if(findData == p->data)
return true;
else if(findData < p->data)
{
p = p->lchild;
}
else if(findData > p->data)
{
p = p->rchild;
}
}
return false;
}
/*从根为r的二叉排序树中删除值为delData的结点*/
BiTNode_t *delete_nodeToTree(BiTNode_t *r, TElemType_t delData)
{
printf("请输入要删除的数据:\n");
scanf("%d",&delData);
if(r == NULL)
return r;
//找到要删除的结点P和删除节点的上一个结点pre
BiTNode_t *p = r;
BiTNode_t *pre = NULL;
while(p)
{
if(p->data == delData)
break;
else if(delData < p->data)
{
pre = p;//先记录父节点
p = p->lchild;
}
else if(delData > p->data)
{
pre = p;//先记录父节点
p = p->rchild;
}
}
if(p == NULL)
{
printf("傻逼!没有这个数据找不到,无法删除!\n");
return r;
}
else//找到这个待删除的节点
{
//不同情况不同的处理
//1、(终端结点)删除节点没有左、右孩子
if(p->lchild == NULL && p->rchild == NULL)
{
//(1)p是根节点,直接释放
if(p == r)
{
free(p);
return NULL;
}
//(2)p是父结点的左孩子,则p为NULL
else if(p == pre->lchild)
{
pre->lchild = NULL;
free(p);
}
//(3)p是父结点的左孩子,则p为NULL
else if(p == pre->rchild)
{
pre->rchild = NULL;
free(p);
}
}
//2、删除结点有左、右孩子
else if(p->lchild != NULL && p->rchild != NULL)
{
//方法:找到删除节点p的左子树中最大的结点p2,p2与删除节点交换数据,删除p2结点
BiTNode_t *p2 = p->lchild;
BiTNode_t *pre2 = p;
while(p2->rchild)
{
pre2 = p2;
p2 = p2->rchild;
}
//对p2位置不同的处理
if(p2 == p->lchild)
{
//交换值
TElemType_t temp = p->data;
p->data = p2->data;
p2->data = temp;
p->lchild = p2->lchild;
p2->lchild = NULL;//断开链接
free(p2);
}
else
{
TElemType_t temp = p->data;
p->data = p2->data;
p2->data = temp;
pre2->rchild = p2->lchild;
p2->lchild = NULL;
free(p2);
}
}
//删除节点只有一个孩子
else
{
//只有左孩子
if(p->lchild)//p->lchild!=NULL
{
if(p == r)//情况1
{
r = p->lchild;
p->lchild = NULL;//断开链接
free(p);
}
else if(p == pre->lchild)//情况2
{
pre->lchild = p->lchild;
p->lchild = NULL;
free(p);
}
else if(p == pre->rchild)//情况3
{
pre->rchild = p->lchild;
p->lchild = NULL;
free(p);
}
}
//只有右孩子
else if(p->lchild)//p->rchild!=NULL
{
if(p == r)//情况1
{
r = p->lchild;
p->lchild = NULL;//断开链接
free(p);
}
if(p == pre->rchild)//情况2
{
pre->rchild = p->rchild;
p->rchild = NULL;
free(p);
}
else if(p == pre->lchild)//情况3
{
pre->lchild = p->rchild;
p->rchild = NULL;
free(p);
}
}
}
}
return r;
}
int main(int argc,char *argv[])
{
BiTNode_t *r = creat_sort_tree();
printf("\n");
pre_allToTree(r);
printf("\n");
TElemType_t delData;
r = delete_nodeToTree(r,delData);
pre_allToTree(r);
printf("\n");
// mid_allToTree(r);
// printf("\n");
// last_allToTree(r);
// printf("\n");
// if(find_nodeToTree(r,5))
// printf("找到!\n");
// else
// printf("找不到!\n");
return 0;
}
在gcc编译器下编译该代码,一次输入二叉树顺序数据下的运行结果: