数据结构之 二叉树(C语言实现)

时间:2022-07-12 10:09:48

数据结构之 二叉树(C语言实现)

1. 二叉树的定义

==二叉树(Binary Tree)是n(n ≥ 0)个节点有限集合。==当n=0时,称为空二叉树,当n>0时,该集合有一个根节点及互不可交的,分别被称为左子树和右子树的二叉树组成。

二叉树可以被理解为一下两个条件的树型结构。

  • 每个节点度不大于2。
  • 节点没棵子树时明确区分左右的,不能随意改变。

2. 二叉树的性质

  1. 在二叉树的第i层之多有 2i1 个节点(i≥1)。
  2. 深度为k的二叉树之多有 2k1 个节点(k≥1)。
  3. 对任意一个二叉树T,若终端节点为n度为2的节点数为m,则n = m + 1。
  4. 具有n个节点的完全二叉树的深度为 log2n+1
  5. 对于具有n个节点的完全二叉树,如果按照对满二叉树节点进行连续编号的方式,对所有节点从1开始顺序编号,则对于任意序号为i的节点有:
    • 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
    • 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
    • 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

3. 二叉树的链式实现

3.1二叉树的抽象数据类型

typedef int myType;

typedef struct treeNode
{
myType element; //值域元素
struct treeNode *lchild; //左孩子
struct treeNode *rchild; //右孩子
}Btree;

3.2 创建一个二叉树

递归创建一个二叉树

void createTree(Btree **T)  //传入一个Btree的指针的地址
{
myType data;
scanf("%d", &data);

if(data == -1) { //-1代表终端节点值
*T = NULL;
} else {
*T = (Btree *)malloc(sizeof(struct treeNode));
(*T)->element = data;
printf("请输入%d的左孩子节点:", data);
createTree(&((*T)->lchild));
printf("请输入%d的右孩子节点:", data);
createTree(&((*T)->rchild));
}
}

3.3 递归先根序遍历

void preOrder(Btree *T)
{
if(T != NULL) {
printf("%d ", T->element); //访问根节点
preOrder(T->lchild); //先根序遍历左子树
preOrder(T->rchild); //先根序遍历右子树
}
}

3.4 递归中根序遍历

void inOrder(Btree *T)
{
if(T != NULL) {
inOrder(T->lchild); //中根序遍历左子树
printf("%d ", T->element); //访问根节点
inOrder(T->rchild); //中根序遍历右子树
}
}

3.5 递归后根序遍历

void postOrder(Btree *T)
{
if(T != NULL) {
postOrder(T->lchild); //后根序遍历左子树
postOrder(T->rchild); //后根序遍历右子树
printf("%d ", T->element); //访问根节点
}
}

==对于二叉树递归遍历的时间复杂度均为O(n)。==

3.6 层次遍历

层次遍历的特点是:先访问的节点其孩子也将先访问,后访问的节点其孩子也将后访问,这与队列操作的特点类似,因此应用队列进行节点的访问次序控制。下面层次遍历的代码会引用到这篇文章中的队列数据结构的相关接口:队列的实现

利用队列遍历的层次思想是:
首先根节点入队,当队列非空时,重复如下两部操作:

  1. 队头节点出队,并访问出队的节点。
  2. 出队节点的非空左、右孩子依次入队。
void levelOrder(Btree *T)
{
QUEUE *q = createQueue(100); //创建一个容量为100的队列

while(T != NULL) {
printf("%d ", T->element); //访问根节点
if(T->lchild != NULL)
enQueue(T->lchild, q); //如果左孩子存在,左孩子入队
if(T->rchild != NULL)
enQueue(T->rchild, q); //如果左孩子存在,左孩子入队
if(!isEmpty(q)) //当队列非空时
T = frontAndDequeue(q); //队头元素出队,并且访问出队元素
else
T = NULL; //队列为空,遍历完成
}

disposeQueue(q); //销毁队列
}

3.7 销毁二叉树

void distroyTree(Btree **T)
{
Btree *pl, *pr;
if((*T) == NULL) {
return ;
} else {
pl = (*T)->lchild; //保存左孩子的地址
pr = (*T)->rchild; //保存右孩子的地址
(*T)->lchild = NULL;
(*T)->rchild = NULL;
free(*T); //释放根节点
(*T) = NULL;
distroyTree(&pl); //递归销毁
distroyTree(&pr);
}
}

3.8 二叉树的深度

int tree_deep(Btree *T)
{
int deep = 0;
if(T) {
int leftdeep = tree_deep(T->lchild);
int rightdeep = tree_deep(T->rchild);
deep = leftdeep > rightdeep ? leftdeep+1 : rightdeep+1;
}

return deep;
}

3.9 终端节点数目

int leafnode_count(Btree *T, int num)
{
if(T) {
if(T->lchild == NULL && T->rchild == NULL)
num++;
leafnode_count(T->lchild, num);
leafnode_count(T->rchild, num);
}

return num;
}