目录
一,二叉树的遍历
二叉树的遍历分为四种:1,先序遍历 2,中序遍历 3,后序遍历4,层次遍历
给定下面的二叉树,进行遍历演示:

由上面的图可知:二叉树其实是按照递归定义的,每个节点都可以分为该节点和它的左右子树。
先序遍历:先访问该节点,再访问左子树,最后访问右子树
中序遍历:先访问左子树,再访问该节点,最后访问右子树
后序遍历:先访问左子树,再访问右子树,最后访问该节点
层次遍历:一层一层从左到右依次访问节点
先序遍历过程分析:

代码的具体实现:
cpp
typedef int BTDataType;
//节点
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
//先序遍历
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->_data);
PreOrder(root->_left);
PreOrder(root->_right);
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->_left);
printf("%d ", root->_data);
InOrder(root->_right);
}
//后序遍历
void AfOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
AfOrder(root->_left);
AfOrder(root->_right);
printf("%d ", root->_data);
}
函数调用过程;

补充题型:
给定先序遍历和中序遍历,构造出二叉树

二,节点的个数和高度
1,求二叉树的节点个数

利用递归将树拆分为两个子条件:
1,节点为空,0个(终止条件)
2 ,节点不为空,左树节点数+右树节点数+1
代码实现:
cpp
//求树的节点个数--利用递归
int TreeSize(BTNode* root)
{
return root == NULL ? 0 : TreeSize(root->_left) + TreeSize(root->_right) + 1;
}
2,求二叉树的叶子节点的个数

依旧利用递归进行拆分:
1,节点的左节点和右节点不为空:左节点+右节点
2,节点的左右节点都为空,1
代码实现:
cpp
//求树的叶子节点--利用递归
int TreeleafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return root->_left == NULL && root->_right == NULL ?
1 : TreeleafSize(root->_left) + TreeleafSize(root->_right);
}
3,求二叉树的高度

利用递归进行拆分:
1,节点为空,0
2,节点不为空,左右子树中最高的树的高度+1
代码实现:
cpp
//求树的高度--利用递归
int TreeHight(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftHight = TreeHight(root->_left);
int rightHight = TreeHight(root->_right);
return leftHight > rightHight ? leftHight + 1:rightHight + 1;
}
4,求二叉树第k层的节点个数


代码实现:
cpp
//求第k层的节点个数
int TreelevelkSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return TreelevelkSize(root->_left, k - 1) + TreelevelkSize(root->_right, k - 1);
}
5,二叉树中查找值为x的节点

代码实现:
cpp
//查找值为a的节点
//若要求的节点存在多个时,按照先序遍历的顺序取第一个
BTNode* TreekFind(BTNode* root, int a)
{
if (root == NULL)
return NULL;
if (root->_data == a)
{
return root;
}
//提前记录,防止重复计算
//先左后右--先序,在左子树中找到就不用在右子树中找
BTNode* leftNode = TreekFind(root->_left, a);
if (leftNode != NULL) //如果找到了就返回--不为空就是找到了
return leftNode;
BTNode* rightNode = TreekFind(root->_right, a);
if (rightNode != NULL)
return rightNode;
return NULL; //两边都为空,没找到返回NULL
}
6,二叉树的销毁

对二叉树进行销毁就是对每个结点进行销毁,销毁时要先销毁节点的左右子树,再销毁根节点。如果先销毁根节点就会找不到结点的左右子树。
代码实现:
cpp
//二叉树的销毁
void TreeDestroy(BTNode* root)
{
//结点为空,不需要进行销毁,直接返回
if (root == NULL)
return;
//先左后右最后根
TreeDestroy(root->_left);
TreeDestroy(root->_right);
free(root);
//不需要置空,因为置空的是局部变量的值,不会影响实参
//想在函数内对一个指针置空,需要二级指针
}
7,二叉树的层次遍历

代码实现:
需要引入相关的队列创建和初始化函数
同时队列中的元素为二叉树结点的地址
队列头文件:

不能直接使用 typedef struct BTNode* QuNodeType;
不能对重命名的名字进行重命名,只能使用初始名字
二叉树源文件:

cpp
//二叉树的层次遍历
void TreelevelOrder(BTNode* root)
{
Que q1;
//队列的创建
QuInit(&q1);
//判断空树
if(root)
QuPush(&q1, root);
while (!QuEmpty(&q1)) //如果队列不为空就继续执行
{
//提前通过一个变量进行接收队头元素,防止删除队头元素后丢失
BTNode* front = QuFront(&q1);
printf("%d ", front->_data);
QuPop(&q1);
//传入队头元素的左右非空结点
if(front->_left)
QuPush(&q1, front->_left);
if (front->_right)
QuPush(&q1, front->_right);
}
//队列的销毁
QuDestroy(&q1);
}
8,判断二叉树是否为完全二叉树

代码实现:
cpp
//判断二叉树是否为完全二叉树
bool TreeCompelet(BTNode* root)
{
Que q1;
QuInit(&q1);
if (root)
QuPush(&q1, root);
while (!QuEmpty(&q1)) //如果队列不为空就继续执行
{
BTNode* front = QuFront(&q1);
//遇到第一个空节点时就跳出循环
if (front == NULL)
{
break;
}
QuPop(&q1);
//空节点也传入队列中
QuPush(&q1, front->_left);
QuPush(&q1, front->_right);
}
//判断第二部分队列是否有非空元素
while (!QuEmpty(&q1))
{
BTNode* front = QuFront(&q1);
if (front)
{
QuDestroy(&q1);
return false;
}
QuPop(&q1);
}
QuDestroy(&q1);
return true;
}
三,例题:
1,单值二叉树

利用递归进行拆分:

代码实现:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isUnivalTree(struct TreeNode* root) {
if(root==NULL)
{
return true;
}
if(root->left && root->left->val != root->val) //左节点存在,且不相等
{
return false;
}
if(root->right && root->right->val != root->val)
{
return false;
}
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
2,相同的树

相同问题可以拆分为该节点值相同和左右子树相同
另外,如果两个节点都为空时,也属于相等。
一个为空,另一个不为空,直接不相等。
两个都不为空,且值相同时,就递归左右子树
代码实现:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p==NULL && q==NULL) //两个都为空,说明相等
{
return true;
}
if(p == NULL || q == NULL) //其中一个为空就返回 false
{
return false;
}
if(p->val != q->val) //如果不是不相等,那么就是两个节点的值就是相等
{
return false;
}
//判断两个节点的左子树和右子树是否相同
return isSameTree(p->left,q->left)
&& isSameTree(p->right,q->right);
}
3,判断对称二叉树

和相同的树类似:判断根节点的左右子树是否对称,变成了左节点的左子树是否和右节点的右子树相等,左节点的右子树是否和右节点的左子树相等。
代码实现:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
//判断两个二叉树是否对称
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p==NULL && q==NULL) //两个都为空,说明相等
{
return true;
}
if(p == NULL || q == NULL) //其中一个为空就返回 false
{
return false;
}
if(p->val != q->val) //如果不是不相等,那么就是两个节点的值就是相等
{
return false;
}
//判断两个节点的左子树和右子树是否相同
return isSameTree(p->left,q->right)
&& isSameTree(p->right,q->left);
}
//判断是否为对称二叉树
bool isSymmetric(struct TreeNode* root)
{
return isSameTree(root->left,root->right);
}
4,二叉树的先序遍历

代码实现:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
//计算二叉树的节点个数
int TreeSize(struct TreeNode* root)
{
return root==NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//进行先序遍历,并把数字传入数组中
void preorderTree(struct TreeNode* root,int* a,int* pi)
{
if(root==NULL)
return;
//传入地址才能在递归中实现一个整数的不断运算
//否则运算在每次调用子函数,值都会重置,所以要从外边传入指针参数
a[(*pi)++] = root->val; //将有效节点数传入到数组当中
preorderTree(root->left, a, pi);
preorderTree(root->right, a, pi);
}
//returnSize 属于输出型参数
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = TreeSize(root);
int* a = (int*)malloc(*returnSize * sizeof(int));
//i为数组的下标--函数递归部分
int i = 0;
preorderTree(root,a,&i);
return a;
}
输出型参数:
returnSize 属于输出型参数,由于函数的返回值只能有一个--数组的首元素地址,但是不知道数组中元素的个数,这时使用输出型参数的形式,传入一个变量的地址,通过在函数内进行改变,即使不返回值,这个变量的值也改变了,就达到了一个函数返回两个值的效果。
传入地址:int* pi
另外注意在函数中一般不使用局部变量进行运算,这些局部变量每次进入函数就会重置为初始值,不能达到随着二叉树结点的改变而改变的效果。这时需要传入指针才能达到效果。
5,另一棵树的子树

代码实现:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
if(p==NULL && q==NULL) //两个都为空,说明相等
{
return true;
}
if(p == NULL || q == NULL) //其中一个为空就返回 false
{
return false;
}
if(p->val != q->val) //如果不是不相等,那么就是两个节点的值就是相等
{
return false;
}
//判断两个节点的左子树和右子树是否相同
return isSameTree(p->left,q->left)
&& isSameTree(p->right,q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
if(root==NULL)
{
return false;
}
if(isSameTree(root, subRoot))
{
return true;
}
return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}
6,二叉树的遍历


先根据所给的字符串先序创造一个二叉树,再中序遍历这个二叉树。
代码实现:
cpp
#include <stdio.h>
#include <stdlib.h>
//创建结点
typedef struct TreeNode
{
char val;
struct TreeNode* left;
struct TreeNode* right;
}BTNode;
//先序创造二叉树
BTNode* PreCreateTree(char* a,int* pi)
{
//为空
if(a[*pi] == '#')
{
(*pi)++;
return NULL;
}
//开辟结点
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
//按照先序的顺序进行创造二叉树
root->val = a[(*pi)++];
root->left = PreCreateTree(a,pi);
root->right = PreCreateTree(a,pi);
return root;
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ", root->val);
InOrder(root->right);
}
int main()
{
char a[100];
scanf("%s",a);
int i = 0;
//先序创造二叉树
BTNode* node = PreCreateTree(a,&i);
//中序遍历
InOrder(node);
}