前言
前面学习的是二叉树的一部分基础知识,现在我们主要是总结普通链式二叉树中常见的一些算法题,一些比较高级的二叉树的相关知识我们会放在C++再进行总结。二叉树的解题思想主要是分治算法思想,因此,学习并掌握好分治算法思想对于解决二叉树相关算法题尤为重要。
分治算法
分治算法的本质是我们要认清楚一个很重要的问题:任何的一棵二叉树都是由很多的子树构成的,每一棵子树都是由对应的根节点和左右子树构成,其中根节点和左右子树可能为空,可能不为空,当根节点为空时,则表示没有对应的树,当左子树为空时,则表示该子树没有左子树,当右子树为空时,则表示该子树没有右子树。分治算法思想主要是要利用好题目给定的二叉树和二叉树中的子树之间的关系进行合理的递归,从而将大问题转化为小问题。
一、单值二叉树
- 题目
- 思路分析
-
思路1:遍历+比较
如果根节点为空,那么符合题意。如果根节点不为空,则说明树不是空树,则比较其他结点存储的值和根节点的值是否相等,如果相等,则说明符合题意,是单值二叉树。 -
代码:
bool _isUnivalTree(struct TreeNode* root,int data)
{
if(root == NULL)
{
return true;
}
if(root->val != data)
{
return false;
}
return _isUnivalTree(root->left,data)&&_isUnivalTree(root->right,data);
}
bool isUnivalTree(struct TreeNode* root){
if(root == NULL)
{
// 空树
return true;
}
// 遍历的思想
// 非空树
int data = root->val;
return _isUnivalTree(root,data);
}
-
提交结果:
-
思路2:分治算法
我们将题目给定的二叉树想象成是由许多的子树构成的,那么每一棵子树都是由根节点和左右子树构成的,那么我们需要比较每一棵子树的左右子树的结点存储的值是否和根节点存储的值相等,如果相等,则说明给定的树是单值二叉树,否则,说明不是单值二叉树。 -
代码
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);
}
- 提交结果
二、相同的树
- 题目
- 思路分析
首先需要判断题目给定的两棵树是否为空,如果都是空树,则说明原来的两棵树是相同的树,如果两棵树中其中一棵为空树,另一棵不是空树,则说明不是相同的树。当两棵树都不是空树时,如果两棵树对应的根节点存储的值不相等,则说明不是相同的树,否则,判断两棵树对应的其他子树是否满足以上判断条件。 - 代码实现:
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p == NULL&&q == NULL)
{
// 两棵树对应的子树都为空
return true;
}
if(p == NULL||q == NULL)
{
// 两棵树其中一棵为空,则不符合条件
return false;
}
// 两棵树都不为空的情况
if(p->val != q->val)
{
// 对应的结点值不相等,不符合条件
return false;
}
// 判断其他子树
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}
- 提交结果
三、对称二叉树
- 题目
- 思路分析
首先对题目给定的树做判断,题目给定的树为空树时,符合题意。题目给定的树不是空树时,我们将题目给定的树的左右子树拆分出来,利用一个子函数去处理判断这个问题,将题目给定的树的左右子树的根节点传给子函数,分别判断左子树中的左子树和右子树中的右子树是否相等以及左子树中的右子树和右子树中的左子树是否相等,这个过程需要递归判断。 - 代码实现:
bool _isSymmetric(struct TreeNode* p,struct TreeNode* q)
{
if(p == NULL&&q == NULL)
{
// 两边对应的子树都为空,符合条件
return true;
}
if(p == NULL||q == NULL)
{
// 两边对应的子树其中一边为空,不符合条件
return false;
}
// 两边对应的子树都不为空
if(p->val!=q->val)
{
// 两边对应的子树中存储的值不相等
return false;
}
// 判断其他子树
return _isSymmetric(p->left,q->right)&&_isSymmetric(p->right,q->left);
}
bool isSymmetric(struct TreeNode* root){
if(root == NULL)
{
// 空树,符合条件
return true;
}
// 非空树
return _isSymmetric(root->left,root->right);
}
- 提交结果
四、二叉树的前序遍历
-
题目
-
思路分析
-
代码实现:
int TreeSize(struct TreeNode* root)
{
// 求树的结点个数
return root == NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
}
void _preorderTraversal(struct TreeNode* root,int*a,int* pi)
{
if(root == NULL)
{
// 对应的子树为空树
return;
}
// 对应的子树不为空
a[(*pi)++] = root->val;
_preorderTraversal(root->left,a,pi);
_preorderTraversal(root->right,a,pi);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
int size = TreeSize(root);
*returnSize = size;
int* a = (int*)malloc(sizeof(int)*size);
if(a == NULL)
{
return NULL;
}
// 递归遍历树
int i = 0;// 遍历数组的下标的
_preorderTraversal(root,a,&i);
return a;
}
- 提交结果
五、二叉树的中序遍历
-
题目
-
思路分析
-
代码实现:
int TreeSize(struct TreeNode* root)
{
// 求树的结点个数
return root == NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
}
void _inorderTraversal(struct TreeNode* root,int*a,int* pi)
{
if(root == NULL)
{
// 对应的子树为空树
return;
}
// 对应的子树不为空
_inorderTraversal(root->left,a,pi);
a[(*pi)++] = root->val;
_inorderTraversal(root->right,a,pi);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
int size = TreeSize(root);
*returnSize = size;
int* a = (int*)malloc(sizeof(int)*size);
if(a == NULL)
{
return NULL;
}
// 递归遍历树
int i = 0;// 遍历数组的下标的
_inorderTraversal(root,a,&i);
return a;
}
- 提交结果
六、二叉树的后序遍历
-
题目
-
思路分析
-
代码实现:
int TreeSize(struct TreeNode* root)
{
// 求树的结点个数
return root == NULL?0:TreeSize(root->left)+TreeSize(root->right)+1;
}
void _postorderTraversal(struct TreeNode* root,int*a,int* pi)
{
if(root == NULL)
{
// 对应的子树为空树
return;
}
// 对应的子树不为空
_postorderTraversal(root->left,a,pi);
_postorderTraversal(root->right,a,pi);
a[(*pi)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){
int size = TreeSize(root);
*returnSize = size;
int* a = (int*)malloc(sizeof(int)*size);
if(a == NULL)
{
return NULL;
}
// 递归遍历树
int i = 0;// 遍历数组的下标的
_postorderTraversal(root,a,&i);
return a;
}
- 提交结果
七、另一棵树的子树
-
题目
-
思路分析
先判断题目给定的树是否为空树,如果为空树,返回false。不是空树,需要递归题目给定的树中的左右子树的每一棵子树,判断是否存在和题目给定的子树一样的树,若存在则返回true,否则返回false。 -
代码实现:
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p == NULL&&q == NULL)
{
// 两棵树对应的子树都为空
return true;
}
if(p == NULL||q == NULL)
{
// 两棵树其中一棵为空,则不符合条件
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;
}
return isSameTree(root,subRoot)||isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}
- 提交结果
八、创建及遍历二叉树
- 题目
- 代码
#include <stdio.h>
#include <stdlib.h>
// 创建及遍历树
// 树的结构
typedef char TreeDataType;
typedef struct TreeNode
{
TreeDataType val;
struct TreeNode* left;
struct TreeNode* right;
}TreeNode;
TreeNode* CreateTree(char* a, int* pi)
{
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
if (node == NULL)
{
return NULL;
}
node->val = a[(*pi)++];
node->left = CreateTree(a, pi);
node->right = CreateTree(a, pi);
return node;
}
// 中序遍历
void InOrder(TreeNode* 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;
TreeNode* root = CreateTree(a, &i);
InOrder(root);
return 0;
}
-
思路分析
本题的重点主要在根据前序遍历创建二叉树的实现上,注意前序遍历的顺序就是:根节点,左子树,右子树,所以先创建结点,并初始化,此时再递归创建左子树,最后递归创建右子树,上述的函数在实际过程中是一个递归的过程,递归创建的是每一棵子树。 -
提交结果