一、二叉树的基本定义
1.1 基本概念
二叉树的每个节点都有一个节点和指向两个其他节点的指针
(1)完全二叉树
二叉树的最后一排(定义为n)只有一部分有子节点,剩余的(1 ~ n-1)二叉树的全部都有左右节点,我们把这样的二叉树叫为完全二叉树。
如图:
(2)满二叉树(是二叉树的一种特列)
国际定义:除了叶结点外每一个结点都有左右子结点的二叉树。(每个节点都只有0个或者2个子节点)。
国内的定义是:除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。从csdn来看,这种国内的被称作满二叉树的较多。
(3)扩充二叉树
扩充二叉树是对已有二叉树的扩充,扩充后的二叉树的节点都变为度数为2的分支节点。也就是说,如果原节点的度数为2,则不变,度数为1,则增加一个分支,度数为0的叶子节点则增加两个分支。
(4)平衡二叉树。
是一棵空树或它的任意节点的左右两个子树的高度差的绝对值不超过1。
1.2 二叉树的基本性质
1.在二叉树的第 i 层上最多有
2
i
−
1
2^{i-1}
2i−1个结点.
2.深度为 k 的二叉树至最有
2
k
−
1
2^{k}-1
2k−1个结点
3.对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为
2
i
2i
2i ( 1 、2、4、8),其右孩子编号必为
2
i
+
1
2i+1
2i+1(1、3、7、15);其双亲的编号必为
i
/
2
i/2
i/2.
如图:
二、二叉树的构建以及遍历
2.1 遍历方式
1)前序遍历:根-> 左子树-> 右子树
2)中序遍历:左子树 ->根 -> 右子树
3)后序遍历:左子树 ->右子树 ->根
4)层序遍历:按照层次遍历
如图:
按照前序遍历: A BCD EFGHK
按照中序遍历:BDC A EHGKF
按照后序遍历:DCB HKGFE A
按照层序遍历:A B E C F D G H K
前序遍历方法:按照从上到下 从左到右依次遍历。
先遍历根:A。
再看左子树,第一节点是B,而且B没有左子节点,所以继续往右遍历,是C,C有左子节点,是D,D没有节点,所以左子树的遍历完成是BCD,
再看右子树,第一节点是E,E没有左子节点,所以遍历右子节点,是F。F有左子节点,所以先遍历左子节点,是G,G有左子节点,是H,H没有节点,H停止遍历,然后遍历G的右子节点,是K。K没有节点,K停止遍历,再回到F,遍历F的右子节点,F没有右子节点,遍历结束,所以右子树的EFGHK。
中序遍历方法:比较繁琐,如图(主要是不想做表格这个图很清楚):
后续遍历法:从左到右,从下到上依次遍历;
先看左子树:B没有左子节点,所以第一个遍历的就是B,B有右子节点C,C有左子节点D,所以再遍历D,D完成后再遍历C,左子树遍历完成BDC。
再看右子树:E有右子节点F,在看F,F有左子节点G,G有左子节点H,H没有左子节点,所以再遍历H,G有右子节点K。所以下一个遍历K,再遍历G,回到F,最后遍历E,右子树遍历完成HGKFE.
最后遍历根:A。
层层遍历:从上到下,从左到右,按层遍历不看子节点。
先遍历A,下一层左边第一个B,第二个E,再下一层左边第一个C,第二个F,再下一层左边第一个D 第二个G,再下一层左边第一个H第二个K,没有下一层,遍历结束。
三、程序的实现
#include<stdlib.h>
#include<stdio.h>
#include<ctype.h>
struct node //二叉树的节点定义
{
int item;
int count;
struct node *pleft;
struct node *pright;
};
struct node * creatnode(int value) //开辟动态内存(为节点开辟空间)放置节点数据(赋值)同时定义左节点和右节点
{
struct node *pnode=(struct node *)malloc(sizeof(struct node));
pnode->item=value;
pnode->count=1;
pnode->pleft=pnode->pright=NULL;
return pnode;
}
struct node * addnode(struct node *pnode,int value)//根据二叉树的根节点(显然是要查询的那个二叉树)和数值(需要查询的那个数)将二叉树中具有相同数值的那个节点的地址返回
{
if (pnode==NULL) //如果调用本函数的根节点地址为零,则说明该二叉树不存在,需要重新创建一个
{
return creatnode(value);
}
if (value==pnode->item) //如果该节点的数据和查询的数据数值相等,则该节点就是要查询的节点,返回该节点的地址
{
++pnode->count;
return pnode;
}
if (value<pnode->item) //如果要查询的数据小于该节点则查询该节点的左子节点
{
if (pnode->pleft==NULL) //如果该节点没有左子节点,则创建该节点的左子节点,并将查询的数据复制给该节点
{
pnode->pleft = creatnode(value);
return pnode->pleft;
}
else{ //该节点有左子节点,则迭代查询该左子节点
return addnode(pnode->pleft,value);
}
}
else //如果要查询的数据小于该节点则查询该节点的右子节点
{
if (pnode->pright==NULL) //如果该节点没有右子节点,则创建该节点的右子节点,并将查询的数据复制给该节点
{
pnode->pright = creatnode(value);
return pnode->pright;
}
else{
return addnode(pnode->pright,value);
}
}
}
void listnode(struct node * pnode) //以升序的方式迭代遍历该根节点二叉树的整个数据
{
if (pnode->pleft!=NULL) //按顺序从左到右,先遍历左子节点,再打印本节点,最后遍历右子节点,然后就迭代回上一节点,在循环同一步骤,直到根节点为止。
{
listnode(pnode->pleft);
}
for (int i = 0; i < pnode->count; i++)
{
printf("%d\n",pnode->item);
}
if (pnode->pright!=NULL)
{
listnode(pnode->pright);
}
}
int main(int argc, char const *argv[])
{
struct node *proot; //创建一个根节点的地址
proot = NULL;
int value=0; //要查询或者添加到节点上的数据
char test='\0'; //yes or no
do{
printf("enter the node value:\n");
scanf("%d",&value);
getchar();
if (proot==NULL) //刚开始根节点没有创建,需要创建
{
proot=creatnode(value); //将创键的根节点地址给指针
}
else
{
addnode(proot,value); //将键盘上输入的数据添加在根节点上创建的二叉树上,或者查询二叉树上已有的数据
}
printf("Do you want to enter anther:Y/N");
scanf("%c",&test);
getchar();
}while(test!='n');
printf("开始升序排列\n");
listnode(proot);
return 0;
}