1 二叉树定义
二叉树是每个结点最多有两个子树的树结构。二叉树不允许存在度⼤于2的树。
它有五种最基本的形态:
二叉树可以是空集;
根可以有空的左子树或者右子树;
左右子树都是空。只有左子树或者右子树的叫做斜树。

2 二叉树的概念和性质
2.1 完全二叉树和满二叉树
满二叉树

完全二叉树
如果一棵深度为k,有n个结点的二叉树中各结点能够与深度为k的顺序编号的满二叉树从1到n标号的结点相对应的二叉树称为完全二叉树。

特点:
所有的叶结点都出现在第k层或k-1层
若任一结点,如果其右子树的最⼤层次为i,则其左子树的最⼤层次为i或i+1
2.2 二叉树的性质(不用背,理解就行)
性质1:
在二叉树的第i层上的结点最多为2^(i-1) 个。(i ≥ 1)
性质2: 深度为k的二叉树至多有2^k -1个结点。(i ≥ 1)
性质3:
在一棵二叉树中,叶结点的数目比度为2的结点数目多一个。
推导过程:
n = 各类结点个数之和 = n0 + n1 + n2
n = 分叉数+1 = n1 + 2*n2 + 1
n0 + n1 + n2 = n1 + 2*n2 + 1
故n0 = n2 + 1
性质4:具有N个结点的完全二叉树的深度为log2 N+1。(向下取整)
性质5:(必须是完全二叉树)
如果有一棵n个结点的
完全二叉树
,其结点编号按照层次序(从上到下,从左到右),则除根结点外,满足[i/2 , i, 2i,2i+1]的规则。
如图,i = 5时,i/2=2(2是5的父结点),2i=10,2i+1=11(10,11是5的孩子结点)
i = 7时,i/2=3(3是7的父结点),2i=14,2i+1=15(14是7的孩子结点,15不存在)

3 二叉树的存储
3.1 顺序存储
顺序存储,有天然的下标索引[0,1,2,3,4,...n],我们需要规定下标的运算规则。
依靠性质5,可以将任意棵二叉树构造成满二叉树结构或完全二叉树结构,依据下标规则,就可以找到⽗结点,子结点。
如图,这棵树虽然不是完全二叉树,但我们可以将其构造成完全二叉树,把不存在的结点赋值为0。(这种方法只适用于接近完全二叉树的树,如果差的太多,需要补充的结点过多,浪费空间)。

3.2 链式存储
由于二叉树的每个结点最多只能有两个子树,每个结点定义两个指针域和一个数据域即可。
找到根结点,顺着往下走就行

4 二叉树的遍历
4.1遍历思想
遍历 :沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次。
对线性结构⽽言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。
二叉树是非线性结构,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径进行遍历的问题。
<1>深度遍历
每个结点的处理方式一样
大任务分解成小任务,小任务处理完后,最终回到大任务处结束(递 归)
先序遍历:
访问结点
结点左边
结点右边

中序遍历:
处理结点左边
访问结点
处理结点右边
后序遍历:
处理结点左边
处理结点右边
访问结点

先:
ABCDEFGHK
中: BDC
AEHGKF
后: DCBHKGFE
A
先序遍历代码:
binaryTree.h
#ifndef BINARY_TREE_H
#define BINARY_TREE_H
#include <stdlib.h>
#include <stdio.h>
typedef int Element;
// 树的结点结构
typedef struct treeNode {
Element data;
struct treeNode *left;
struct treeNode *right;
}TreeNode;
// 二叉树的描述信息结构
typedef struct {
TreeNode *root; // 二叉树的根结点
int count; // 二叉树的结点个数
}BinaryTree;
BinaryTree *createBinaryTree(TreeNode *root);
void releaseBinaryTree(BinaryTree *tree);
TreeNode *createTreeNode(Element e);
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right);
void visitTreeNode(TreeNode *node);
void preOrderBTreeRecur(BinaryTree *tree); // 先序遍历tree,使用递归方法
#endif
binaryTree.c
#include "binaryTree.h"
/* 申请二叉树的信息体,若有根结点,那么指向根结点,并更新树的结点个数 */
BinaryTree *createBinaryTree(TreeNode *root) {
BinaryTree *tree = (BinaryTree *) malloc(sizeof(BinaryTree));
if (tree == NULL) {
fprintf(stderr, "tree malloc failed!\n");
return NULL;
}
if (root) {
tree->root = root;
tree->count = 1;
} else {
tree->root = NULL;
tree->count = 0;
}
return tree;
}
/* 产生新结点,初始化左右指针为NULL */
TreeNode *createTreeNode(Element e) {
TreeNode *node = (TreeNode *) malloc(sizeof(TreeNode));
if (node == NULL) {
fprintf(stderr, "tree node malloc failed!\n");
return NULL;
}
node->data = e;
node->left = node->right = NULL;
return node;
}
/* 向父节点插入左右结点 */
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right) {
if (tree && parent) {
parent->left = left;
parent->right = right;
if (left)
tree->count++;
if (right)
tree->count++;
}
}
static void preOrder(TreeNode *node) {
if (node) {
visitTreeNode(node);
preOrder(node->left);
preOrder(node->right);
}
}
void preOrderBTreeRecur(BinaryTree *tree) {
if (tree) {
preOrder(tree->root);
}
}
void visitTreeNode(TreeNode *node) {
printf("\t%c", node->data);
}
main.c
#include "binaryTree.h"
BinaryTree *initTree1() {
TreeNode *nodeA = createTreeNode('A');
TreeNode *nodeB = createTreeNode('B');
TreeNode *nodeC = createTreeNode('C');
TreeNode *nodeD = createTreeNode('D');
TreeNode *nodeE = createTreeNode('E');
TreeNode *nodeF = createTreeNode('F');
TreeNode *nodeG = createTreeNode('G');
TreeNode *nodeH = createTreeNode('H');
TreeNode *nodeK = createTreeNode('K');
BinaryTree *tree = createBinaryTree(nodeA);
insertBinaryTree(tree, nodeA, nodeB, nodeE);
insertBinaryTree(tree, nodeB, NULL, nodeC);
insertBinaryTree(tree, nodeE, NULL, nodeF);
insertBinaryTree(tree, nodeC, nodeD, NULL);
insertBinaryTree(tree, nodeF, nodeG, NULL);
insertBinaryTree(tree, nodeG, nodeH, nodeK);
return tree;
}
int main() {
BinaryTree *tree = initTree1();
printf("preOrder: ");
preOrderBTreeRecur(tree);
printf("\n");
return 0;
}
在CLion上的运行结果:

根据遍历结果重构二叉树

<2>广度遍历
层次遍历:根据
⽗子关系,知道了⽗,那么就把其所有的子结点都看一遍
如图所示,我们可以用队列来实现广度遍历
que:A
A入队,再出队后,可以访问A,可以知道A的左右孩子B和E,B和E入队
que: B, E
que:E
B出队,访问B,同时可以知道B的右孩子C,C入队
que:E,C
E出队,访问E,可以知道E的右孩子F,F入队
que:C,F
依次类推,直到队列为空,得到树的广度遍历:A B E C F D G H K

二叉树完整代码:[增加了广度遍历(用到队列),中序遍历,后续遍历,非递归遍历(用到栈)]
arrayQueue.h
#ifndef ARRAY_QUEUE_H
#define ARRAY_QUEUE_H
#include "binaryTree.h"
#define MaxQueue 20
// 定义顺序队列的结构
typedef struct {
pTreeNode data[MaxQueue];
int front;
int rear;
}ArrayPTreeQueue;
ArrayPTreeQueue *createArrayQueue();
void releaseArrayQueue(ArrayPTreeQueue *queue);
int enArrayQueue(ArrayPTreeQueue *queue, pTreeNode e);
int deArrayQueue(ArrayPTreeQueue *queue, pTreeNode *e);
#endif
arrayQueue.c
#include "arrayQueue.h"
ArrayPTreeQueue *createArrayQueue() {
ArrayPTreeQueue *queue = (ArrayPTreeQueue *)malloc(sizeof(ArrayPTreeQueue));
if (queue == NULL) {
printf(".....!\n");
return NULL;
}
queue->front = queue->rear = 0;
return queue;
}
void releaseArrayQueue(ArrayPTreeQueue *queue) {
if (queue) {
free(queue);
}
}
int enArrayQueue(ArrayPTreeQueue *queue, pTreeNode e) {
// 先判断队列满不满
if ((queue->rear + 1) % MaxQueue == queue->front) {
printf("Queue full!\n");
return -1;
}
queue->rear = (queue->rear + 1) % MaxQueue;
queue->data[queue->rear] = e;
return 0;
}
int deArrayQueue(ArrayPTreeQueue *queue, pTreeNode *e) {
// 先判断队列空不空
if (queue->rear == queue->front) {
// printf("Queue empty!\n");
return -1;
}
queue->front = (queue->front + 1) % MaxQueue;
*e = queue->data[queue->front];
return 0;
}
arrayStack.h
#ifndef ARRAYSTACK_H
#define ARRAYSTACK_H
/* 顺序栈 满递增栈*/
#include "binaryTree.h"
#define MaxStackSize 20
typedef struct {
pTreeNode data[MaxStackSize];
int top;
}ArrayStack;
ArrayStack *createArrayStack();
void releaseArrayStack(ArrayStack *stack);
int pushArrayStack(ArrayStack *stack, pTreeNode e);
int popArrayStack(ArrayStack *stack, pTreeNode *e);
#endif
arrayStack.c
#include <stdlib.h>
#include <stdio.h>
#include "arrayStack.h"
ArrayStack *createArrayStack() {
ArrayStack *stack = (ArrayStack *) malloc(sizeof(ArrayStack));
if (stack == NULL) {
printf("malloc failed\n");
return NULL;
}
stack->top = -1;
return stack;
}
/* 数据能存放的位置 : [0, 1, ... max-1] */
int pushArrayStack(ArrayStack *stack, pTreeNode e) {
// 上溢出的问题排除
if (stack->top >= MaxStackSize - 1) {
return -1;
}
stack->data[++stack->top] = e;
return 0;
}
int popArrayStack(ArrayStack *stack, pTreeNode *e) {
if (stack->top < 0) {
return -1;
}
*e = stack->data[stack->top--];
return 0;
}
void releaseArrayStack(ArrayStack *stack) {
if (stack) {
free(stack);
}
}
binaryTree.h
#ifndef BINARY_TREE_H
#define BINARY_TREE_H
#include <stdlib.h>
#include <stdio.h>
typedef int Element;
// 树的节点结构
typedef struct treeNode {
Element data;
struct treeNode *left;
struct treeNode *right;
}TreeNode;
typedef TreeNode* pTreeNode;
// 二叉树的描述信息结构
typedef struct {
TreeNode *root; // 二叉树的根节点
int count; // 二叉树的节点个数
}BinaryTree;
BinaryTree *createBinaryTree(TreeNode *root);
void releaseBinaryTree(BinaryTree *tree);
TreeNode *createTreeNode(Element e);
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right);
void visitTreeNode(TreeNode *node);
void preOrderBTreeRecur(BinaryTree *tree); // 先序遍历tree,使用递归方法
void inOrderBTreeRecur(BinaryTree *tree); // 中序遍历tree,使用递归方法
void postOrderBTreeRecur(BinaryTree *tree); // 后序遍历tree,使用递归方法
// 层级遍历、广度遍历
void levelOrderBTree(BinaryTree *tree);
void preOrderBTreeNoRecur(BinaryTree *tree);
void inOrderBTreeNoRecur(BinaryTree *tree);
void postOrderBTreeNoRecur(BinaryTree *tree);
#endif
binaryTree.c
#include "binaryTree.h"
#include "arrayQueue.h"
#include "arrayStack.h"
/* 申请二叉树的信息体,若有根节点,那么指向根节点,并更新树的节点个数 */
BinaryTree *createBinaryTree(TreeNode *root) {
BinaryTree *tree = (BinaryTree *) malloc(sizeof(BinaryTree));
if (tree == NULL) {
fprintf(stderr, "tree malloc failed!\n");
return NULL;
}
if (root) {
tree->root = root;
tree->count = 1;
} else {
tree->root = NULL;
tree->count = 0;
}
return tree;
}
/* 后序递归遍历,释放节点 */
static void destroyTreeNode(BinaryTree *tree, TreeNode *node) {
if (node) {
destroyTreeNode(tree, node->left);
destroyTreeNode(tree, node->right);
free(node);
tree->count--;
}
}
/* 释放二叉树,通过后序遍历的方式,将节点逐个释放 */
void releaseBinaryTree(BinaryTree *tree) {
if (tree) {
destroyTreeNode(tree, tree->root);
printf("tree have %d node!\n", tree->count);
free(tree);
}
}
/* 产生新节点,初始化左右指针为NULL */
TreeNode *createTreeNode(Element e) {
TreeNode *node = (TreeNode *) malloc(sizeof(TreeNode));
if (node == NULL) {
fprintf(stderr, "tree node malloc failed!\n");
return NULL;
}
node->data = e;
node->left = node->right = NULL;
return node;
}
/* 向父节点插入左右节点 */
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right) {
if (tree && parent) {
parent->left = left;
parent->right = right;
if (left)
tree->count++;
if (right)
tree->count++;
}
}
static void preOrder(TreeNode *node) {
if (node) {
visitTreeNode(node);
preOrder(node->left);
preOrder(node->right);
}
}
static void inOrder(TreeNode *node) {
if (node) {
inOrder(node->left);
visitTreeNode(node);
inOrder(node->right);
}
}
static void postOrder(TreeNode *node) {
if (node) {
postOrder(node->left);
postOrder(node->right);
visitTreeNode(node);
}
}
void preOrderBTreeRecur(BinaryTree *tree) {
if (tree) {
preOrder(tree->root);
}
}
void visitTreeNode(TreeNode *node) {
printf("\t%c", node->data);
}
void levelOrderBTree(BinaryTree *tree) {
ArrayPTreeQueue *queue = createArrayQueue();
pTreeNode node;
enArrayQueue(queue, tree->root); // 初始化任务队列
while (deArrayQueue(queue, &node) != -1) {
// 访问这个节点
visitTreeNode(node);
// 根据这个节点的左右情况,再次更新任务(如果左,就把左节点的地址入队,如果右...)
if (node->left) {
enArrayQueue(queue, node->left);
}
if (node->right) {
enArrayQueue(queue, node->right);
}
}
releaseArrayQueue(queue);
}
void inOrderBTreeRecur(BinaryTree *tree) {
if (tree) {
inOrder(tree->root);
}
}
void postOrderBTreeRecur(BinaryTree *tree) {
if (tree) {
postOrder(tree->root);
}
}
/* 非递归的先序遍历:
* 1. 压入根节点到栈
* 2. 弹出元素,访问该元素
* 3. 将该元素的所知道的任务压入栈(有右压右,有左压左)
* */
void preOrderBTreeNoRecur(BinaryTree *tree) {
TreeNode *node;
if (tree) {
ArrayStack *stack = createArrayStack();
pushArrayStack(stack, tree->root);
while (popArrayStack(stack, &node) != -1 && node) {
visitTreeNode(node);
if (node->right) {
pushArrayStack(stack, node->right);
}
if (node->left) {
pushArrayStack(stack, node->left);
}
}
releaseArrayStack(stack);
}
}
/* 非递归的中序遍历:
* 以根节点开始,整条左边进栈
* 从栈中弹出节点,访问,然后以这个节点的右孩子为新节点
* 再次安装整条左边进栈,再弹栈
* */
void inOrderBTreeNoRecur(BinaryTree *tree) {
TreeNode *node;
if (tree->root) {
ArrayStack *stack = createArrayStack();
node = tree->root;
while (stack->top >= 0 || node) {
if (node) {
pushArrayStack(stack, node);
node = node->left;
} else {
popArrayStack(stack, &node);
visitTreeNode(node);
node = node->right;
}
}
releaseArrayStack(stack);
}
}
/* 1. 非递归的后序遍历,需要两个栈,第一个栈作为辅助,最后一个栈作为输出
* 2. 第一个栈压入根节点后,弹出第二个栈,根节点就变成最后输出
* 3. 后序遍历的倒数第二个应该是头节点的右孩子,所以辅助栈,先左后右
* 4. 辅助栈弹出元素放入到第二个栈,这个节点先左后右放第一个栈
* */
void postOrderBTreeNoRecur(BinaryTree *tree) {
TreeNode *node;
if (tree) {
ArrayStack *stack1 = createArrayStack();
ArrayStack *stack2 = createArrayStack();
pushArrayStack(stack1, tree->root); // 初始化辅助栈
while (popArrayStack(stack1, &node) != -1) {
pushArrayStack(stack2, node);
if (node->left) {
pushArrayStack(stack1, node->left);
}
if (node->right) {
pushArrayStack(stack1, node->right);
}
}
while (popArrayStack(stack2, &node) != -1) {
visitTreeNode(node);
}
releaseArrayStack(stack1);
releaseArrayStack(stack2);
}
}
main.c
#include "binaryTree.h"
BinaryTree *initTree1() {
TreeNode *nodeA = createTreeNode('A');
TreeNode *nodeB = createTreeNode('B');
TreeNode *nodeC = createTreeNode('C');
TreeNode *nodeD = createTreeNode('D');
TreeNode *nodeE = createTreeNode('E');
TreeNode *nodeF = createTreeNode('F');
TreeNode *nodeG = createTreeNode('G');
TreeNode *nodeH = createTreeNode('H');
TreeNode *nodeK = createTreeNode('K');
BinaryTree *tree = createBinaryTree(nodeA);
insertBinaryTree(tree, nodeA, nodeB, nodeE);
insertBinaryTree(tree, nodeB, NULL, nodeC);
insertBinaryTree(tree, nodeE, NULL, nodeF);
insertBinaryTree(tree, nodeC, nodeD, NULL);
insertBinaryTree(tree, nodeF, nodeG, NULL);
insertBinaryTree(tree, nodeG, nodeH, nodeK);
return tree;
}
int main() {
BinaryTree *tree = initTree1();
printf("树的节点数: %d\n", tree->count);
printf("先序遍历: ");
preOrderBTreeRecur(tree);
printf("\n中序遍历: ");
inOrderBTreeRecur(tree);
printf("\n后序遍历: ");
postOrderBTreeRecur(tree);
printf("\n层次遍历: ");
levelOrderBTree(tree);
printf("\n非递归先序遍历: ");
preOrderBTreeNoRecur(tree);
printf("\nInorder: ");
inOrderBTreeNoRecur(tree);
printf("\nPostOrder:");
postOrderBTreeNoRecur(tree);
printf("\n");
releaseBinaryTree(tree);
return 0;
}
在CLion上的运行结果: