一、定义
二叉树 是每个节点最多拥有两个子节点 的树形结构,子节点区分左孩子、右孩子,顺序不能互换。
节点结构通用形式(C 语言):
typedef struct BiTNode {
int data; // 数据域
struct BiTNode *lchild; // 左孩子指针
struct BiTNode *rchild; // 右孩子指针
} BiTNode, *BiTree;
二、基础概念
- 根节点:最顶层,无父节点
- 叶子节点:左、右孩子都为空
- 度:节点拥有的子节点数量;二叉树节点度 ≤ 2
- 深度 / 层数:根一般记第 1 层
- 高度:从当前节点向下到最远叶子的层数
三、特殊二叉树
满二叉树 所有层节点全部填满;叶子都在最后一层。 高度 h,结点总数:(2^n-1)

完全二叉树 按层序依次填充,空缺只能出现在最下层左侧。

二叉树的数据访问方式:



二叉树的完整创建和数据存储都需要依靠拓展序列进行,普通二叉树有些结点缺少左孩子或右孩子。拓展二叉树:把原二叉树所有空缺的左、右子树全部补上空结点,一般用 #表示。
练习:

根据前序和中序的序列,可以区分哪些节点是位于根节点的左侧,哪些节点位于根节点的右侧。主要看前序的第一个,视作为根节点,而在中序访问中,根节点的位置位于正中间,以此即可区分左右部分的子节点。对比前序和后续中,不同节点的位置,可以看出,在子节点中,谁是谁的根,便可以推算出树的结构,这主要得益于前序和中序的访问顺序差异,从而进行相互映照。
二叉树算法:
二叉树的创建:
拓展前序序列递归创建二叉树,# 代表空分支。 序列规则:拓展前序遍历序列,顺序:根 → 左子树 → 右子树。
typedef int data_t;
typedef struct btnode
{
data_t data;
struct btnode *pl;
struct btnode *pr;
}btree_t; //二叉树节点类型
char tree_seq[] = "ABDG##H###CE#I##F##";
//char tree_seq[] = "AB#CD##E##F#GH###";
int idx = 0;
btree_t *create_btree(void)
{
char data = tree_seq[idx++];//从数组中取得数据
if (data == '#') //判断是否为空节点
{
return NULL; //表示结束
}
btree_t *new = malloc(sizeof(btree_t)); //申请堆区空间创建新节点
if (new == NULL)
{
printf("malloc fail!\n");
return NULL;
}
new->data = data; //新节点写入数据
new->pl = create_btree(); //前序创建,先进行做分支递归创建
new->pr = create_btree(); //左分支递归完成后进行右分支递归创建
return new;
}
二叉树的深度访问:
前序访问:
接收二叉树根节点指针作为参数,首先判断节点是否为空,若为空直接返回终止当前递归;若节点有效,则先输出当前节点数据,接着递归遍历其左子树,左子树全部遍历完成后再递归遍历右子树,遵循根、左、右的访问顺序完成整棵树前序遍历。
int pre_order_traverse(btree_t *t) //传根节点的地址
{
// 结束条件 t == NULL
if (t == NULL)
return 0;
printf("%c ",t->data); //根
pre_order_traverse(t->pl); //左子树 //左
pre_order_traverse(t->pr); //右子树 //右
return 0;
}
中序访问:
接收二叉树根节点指针,先判断节点是否为空,为空则结束本次递归;若节点有效,先递归遍历左子树,左子树遍历完毕后输出当前节点数据,最后递归遍历右子树,按照左、根、右的顺序完成整棵树的中序遍历。
int in_order_traverse(btree_t *t) //传根节点的地址
{
// 结束条件 t == NULL
if (t == NULL)
return 0;
in_order_traverse(t->pl); //左子树 //左
printf("%c ",t->data); //根
in_order_traverse(t->pr); //右子树 //右
return 0;
}
后序访问:
接收二叉树根节点指针,先判断节点是否为空,为空则终止本次递归;若节点有效,先递归遍历左子树,再递归遍历右子树,左右子树遍历完成后输出当前节点数据,按照左、右、根的顺序完成整棵树的后序遍历。
int post_order_traverse(btree_t *t) //传根节点的地址
{
// 结束条件 t == NULL
if (t == NULL)
return 0;
post_order_traverse(t->pl); //左子树 //左
post_order_traverse(t->pr); //右子树 //右
printf("%c ",t->data); //根
return 0;
}
销毁二叉树:
销毁二叉树必须使用后序遍历方式:销毁节点时,要先释放当前节点的左、右子树空间,再释放自身节点。如果先释放根节点,会直接丢失左右孩子指针,造成子树内存无法回收、内存泄漏。而后序遍历顺序为左→右→根,正好匹配先销毁左右子节点,最后销毁当前节点的执行逻辑。
int btree_destroy(btree_t *t) // 销毁二叉树
{
if (t == NULL)
return -1;
//递归 --- 后序方式
//左右根
btree_destroy(t->pl);//左
btree_destroy(t->pr);//右边
free(t);
return 0;
}
二叉树的层序遍历:
按照从上到下、同一层从左往右依次访问节点,必须借助队列实现。 队列作用:保存当前层节点,取出节点访问时,依次把它的左孩子、右孩子入队。
-
根节点不为空,先将根节点入队列;
-
循环判断队列是否非空:
- 队首节点出队并访问;
- 如果存在左孩子,左孩子入队;
- 如果存在右孩子,右孩子入队;
-
队列为空,遍历结束。
typedef int data_t;
typedef struct btnode
{
data_t data;
struct btnode *pl;
struct btnode *pr;
}btree_t; //二叉树节点类型/定义队列里的数据类型为二叉树节点的指针类型/
typedef btree_t* pdata_t;typedef struct Node
{
pdata_t d; //数据域
struct Node *pnext; //指针域
}node_t; //节点int layer_order_traverse(btree_t *t)
{
if (t == NULL)
return -1;
//s1.创建队列
node_t *pq = linkqueue_create();enqueue(pq,t); while (is_empty(pq) != 1) { btree_t *data = NULL; dequeue(pq,&data); printf("%c ",data->data); if (data->pl != NULL) enqueue(pq,data->pl); if (data->pr != NULL) enqueue(pq,data->pr); } linkqueue_destroy(&pq); return 0;}
//创建链表队列
node_t * linkqueue_create(void)
{
node_t *p = malloc(sizeof(node_t));
if (p == NULL)
{
printf("malloc fail");
return NULL;
}//表示空链表的头节点 p->pnext = NULL; return p;}
//入列
int enqueue(node_t *head, pdata_t data)
{
if (head==NULL)
{
return -1;
}
node_t *p = head;
//1.找到尾节点
while (p->pnext)
{
p = p->pnext;
}
//2.创建新节点
node_t *pNew = malloc(sizeof(node_t));
if (pNew == NULL)
{
printf("malloc fail");
return -1;
}
pNew->d = data;
pNew->pnext = NULL;//3.连接 p->pnext = pNew; return 0;}
//出队
int dequeue(node_t *head,pdata_t *data)
{
if (NULL==head || is_empty(head) == 1)
{
return -1;
}node_t *p = head->pnext; *data = p->d; //拿到出队的数据 head->pnext = p->pnext; free(p); return 0;}
//销毁
int linkqueue_destroy(node_t **head)
{
if (NULL==head )
{
return -1;
}
if(is_empty(*head) == 1)
{
return -1;
}
node_t *p = (*head)->pnext;
while (p != NULL)
{
node_t *cur = p->pnext;
free(p);
p = cur;
}free(*head); *head = NULL; return 0;}
主要函数layer_order_traverse 层序遍历
该函数实现二叉树层序遍历,首先判断根节点是否为空,为空直接返回;创建带头结点的链表队列并将根节点入队;循环判断队列非空时执行出队操作,取出二叉树节点并打印数据;若该节点存在左孩子、右孩子,则依次将左右孩子入队,持续循环直至队列为空;遍历完成后释放队列内存,实现从上到下、同层从左至右依次访问二叉树所有节点。