二叉树算法题

在之前二叉树的相关章节(数据结构之《二叉树》(上) 数据结构之《二叉树》(中) 数据结构之《二叉树》(下))后,我们了解了二叉树的相关概念和性质,并且实现了顺序结构二叉树------堆和链式结构二叉树,那么在本篇中我们就来试着解决一些二叉树相关的算法题和选择题,相信通过这些练习之后你会对二叉树有更深的理解,一起加油吧!!!


1.二叉树算法题

1.1单值二叉树

965. 单值二叉树 - 力扣(LeetCode)

通过以上的题目描述就可以了解到该算法题要我们实现的判断二叉树是否是单值二叉树也其中单值是指二叉树每个节点的值都相同

在实现该算法题的代码前先来分析单值二叉树还有什么其他的特点
例如以下示例

在以上的单值二叉树可以看出在任何子树当中如果节点的左孩子节点不为NULL,那么该左节点的孩子节点的值一定与该节点的值相同; 如果节点的右孩子节点不为NULL,那么该节点的右孩子节点的值一定与该节点的值相同

在分析完单值二叉树的特点后接下来就试着来实现该算法题的代码

在实现该算法题我们使用的是在链式结构二叉树 中很常见的方法------递归 ,在此函数递归的限制条件为当节点为空时,这时就返回true

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);
}

1.2相同的树

100. 相同的树 - 力扣(LeetCode)

通过以上的题目描述就可以了解到该算法题要我们实现的是判断两棵二叉树是否为相同的,在此相同的树是结构相同并且各个节点内的数据值也是相同的

在实现该算法题的代码前我们先来两棵相同的二叉树在结构上还有什么特征
我们来看以下示例

以上两棵二叉树就为相同的树,两棵树在相同的节点位置节点内的值相等,并且当节点为根结点时左右孩子节点都为NULL

在相同二叉树的特点后接下来就试着来实现该算法题的代码

在实现该算法题依然使用是递归递归的限制条件是当两棵二叉树都递推到空节点时就返回true

递归的限制条件判断为p==NULL && q==NULL在此判断完后若还会进行此次函数栈帧就说明这时两个二叉树的节点不都为NULL这时若有一个节点为NULL就说明在相同位置两个二叉树不相同,在此就使用一个if语句来判断是否此时两个节点,判断条件为p==NULL || q==NULL

并且还需要对相同位置两个节点的值是否相同进行判断若不相同就返回false

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)
    {
        return false;
    }
    if(p->val!=q->val)
    {
        return false;
    }
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}

1.3对称二叉树

101. 对称二叉树 - 力扣(LeetCode)

通过以上的题目描述就可以了解到该算法题要我们实现的是判断是否为对称二叉树

当一棵二叉树为对称二叉树时,该二叉树的根结点的左子树和右子树就为可看作是两棵对称的树,这时在解决该算法题时就可以用到以上相同的树算法题的代码,不过在这里需要将以上代码做出一些修改

在原来的相同的树代码中是两棵树的两个左节点比较,但在该算法题中需要将一个子树的左节点和另一个子树的右节点进行比较

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)
    {
        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);
}

1.4二叉树遍历

1.4.1前序遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

在之前二叉树(下)中我们已经学习过二叉树的前序遍历,但是在该算法题中要求和我们之前实现的前序遍历有所不同,在之前的前序遍历中我们是将遍历到的节点存储的数据直接打印出来,但在该算法题中是要将前序遍历后得到的数据存储到数组当中,最后将数组返回 ,因此在此在遍历之前需要我们自己创建一个数组 ,但是这时又会存在一个问题了,数组的空间需要我们来动态开辟这时要开辟多大的空间呢?

在此就需要用到之前在二叉树(下)中实现的二叉树结点个数的函数

完成算法题的分析后接下来就来实现代码

在此_preorderTraversal为preorderTraversal的子方法,是用来完成二叉树的前序遍历

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().
 */
typedef struct TreeNode TreeNode;
int TreeSize(TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    return 1+TreeSize(root->left)+TreeSize(root->right);
}

void _preorderTraversal(TreeNode* root,int* arr,int* pi)
{
    if(root==NULL)
    {
        return;
    }
    arr[(*pi)++]=root->val;
    PreorderTraversal(root->left,arr,pi);
    PreorderTraversal(root->right,arr,pi);
}

int* preorderTraversal(struct TreeNode* root, int* returnSize) 
{
    *returnSize=TreeSize(root);//得到二叉树结点个数
    int* Returnarr=(int*)malloc(sizeof(int)*(*returnSize));//动态开辟大小为二叉树结点个数的数组
    int i=0;

    _preorderTraversal(root,Returnarr,&i);
    return Returnarr;//返回数组名
}

1.4.2中序遍历

94. 二叉树的中序遍历 - 力扣(LeetCode)

在实现了前序遍历的算法题后接下来完成中序遍历算法题的代码就很容易了,只需要在遍历的函数内改成按照左根右顺序进行即可

最终实现代码如下:

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().
 */
 typedef struct TreeNode TreeNode;

int TreeSize(TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    return 1+TreeSize(root->left)+TreeSize(root->right);
}

void InorderTraversal(TreeNode* root,int* arr,int* pi)
{
    if(root==NULL)
    {
        return;
    }
    InorderTraversal(root->left,arr,pi);
    arr[(*pi)++]=root->val;
    InorderTraversal(root->right,arr,pi);
}

int* inorderTraversal(struct TreeNode* root, int* returnSize) 
{
    *returnSize=TreeSize(root);
    int* Returnarr=(int*)malloc(sizeof(int)*(*returnSize));
    int i=0;
    InorderTraversal(root,Returnarr,&i);
    return Returnarr;
}

1.4.3后序遍历

145. 二叉树的后序遍历 - 力扣(LeetCode)

在实现了前序遍历的算法题后接下来完成后序遍历算法题的代码就很容易了,只需要在遍历的函数内改成按照左右根顺序进行即可

最终实现代码如下:

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().
 */
 typedef struct TreeNode TreeNode;
int TreeSize(TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    return 1+TreeSize(root->left)+TreeSize(root->right);
}

void PostorderTraversal(TreeNode* root,int* arr,int* pi)
{
    if(root==NULL)
    {
        return;
    }
    
    PostorderTraversal(root->left,arr,pi);
    PostorderTraversal(root->right,arr,pi);
    arr[(*pi)++]=root->val;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize) 
{
    *returnSize=TreeSize(root);
    int* Returnarr=(int*)malloc(sizeof(int)*(*returnSize));
    int i=0;

    PostorderTraversal(root,Returnarr,&i);
    return Returnarr;
}

1.5二叉树的构建及遍历

二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

在以上算法题中和之前我们写的算法题不同,之前写的算法题都是只需要我们实现大体的函数就可以了,在该算法题中整个程序都需要需要我们自己来实现这就和使用VS等IDE写程序一样

首先是要读取用户输入的字符串,那么这时就需要我们先创建一个字符数组arr然后再使用scanf来将输入的字符串输入到字符数组当中。(在此因输入字符长度不超过100所以数组的大小就初始化为100)
之后就要将字符数组中的数据存放到结点当中并且将这些节点根据前序遍历来构建一个二叉树,因此这时就需要先写一个创建二叉树节点的函数,以下代码中函数NewNode就是来实现节点的创建,之后在CreatTree函数中实现二叉树创建,在此是使用递归的方法来创建二叉树的,递归的限制条件是当遍历数组的元素为'#'时;这时就代表该节点为NULL

完成二叉树的创建后就来实现二叉树的中序遍历,这时在以下代码中Inorder就实现输出中序遍历的结果

以下就是该算法题完整的代码

cpp 复制代码
#include <stdio.h>
#include<stdlib.h>

typedef struct BTNOde
{
    char val;
    struct BTNode* left;
    struct BTNode* right;
}BTNode;


BTNode* NewNode(int x)
{
    BTNode* newnode=(BTNode*)malloc(sizeof(BTNode));
    if(newnode==NULL)
    {
        exit(1);
    }
    newnode->val=x;
    newnode->left=newnode->right=NULL;
    return newnode;
}
BTNode* CreatTree(char* arr,int* pi)
{
    if(arr[*pi]=='#')
    {
        (*pi)++;
        return NULL;
    }
    BTNode* root=NewNode(arr[(*pi)++]);

    root->left=CreatTree(arr,pi);
    root->right=CreatTree(arr,pi);

    return root;

}

void Inorder(BTNode* root)
{
    if(root==NULL)
    {
        return;
    }
    Inorder(root->left);
    printf("%c ",root->val);
    Inorder(root->right);
}

int main() 
{
    //读取字符至数组arr中
    char arr[100];
    scanf("%s",arr);
    //创建二叉树
    int i=0;
    BTNode* root=CreatTree(arr,&i);
    //打印中序遍历结果
    Inorder(root);    
}

2.二叉树选择题

在完成二叉树的选择题之前先来了解之前未学习到的一个二叉树的性质

💡二叉树性质

(1)对任何一棵二叉树, 如果度为 0 其叶结点个数为 n0 , 度为 2 的分支结点个数为 n2 ,则有
n0 = n2 + 1

那么这个性质是怎么来的呢?接下来来看以下示例

证明上述性质:
假设一个二叉树有 a 个度为2的节点, b 个度为1的节点, c 个叶节点,则这个二叉树的边数是
2a+b
另一方面,由于共有a+b+c 个节点,所以边数等于a+b+c-1
结合上面两个公式:2a+b = a+b+c-1 ,即: a = c-1

了解了二叉树的上述这个性质接下来就来完成以下二叉树的选择题吧

1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
A 不存在这样的二叉树
B 200
C 198
D 199

在以上我们刚了解了在二叉树中叶子节点的个数会等于度为2节点个数再加一,在本题中度为2节点个数为199,那么叶子节点个数就为199+1=200,因此这题选B

2.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n+1
C n-1
D n/2

在这题中是完全二叉树,因此树中度为1的节点只有可能为1个或者0个,在此设叶子节点个数为x1,度为1节点为x2,则度为2节点个数为x1-1,又因为二叉树总的节点个数为2n,这时就可以的得到以下公式:


这时就可以得到x2为0时x1=n+1/2,这时节点个数不为整数因此度为1的节点0个时这种情况排除
所以x2一定为1这时x1=n

因此这题选A
3.一棵完全二叉树的结点数为531个,那么这棵树的高度为( )
A 11
B 10
C 8
D 12

我们之前学习过满二叉树总的节点个数公式为,在此之后得到公式
因此就可以得出深度为9的满二叉树结点个数为512,所以531个结点的完全二叉树高度为10

因此这题选B

4.一个具有767个结点的完全二叉树,其叶子结点个数为()
A 383
B 384
C 385

D 386

在这题中是完全二叉树,因此树中度为1的节点只有可能为1个或者0个,在此设叶子节点个数为x1,度为1节点为x2,则度为2节点个数为x1-1,又因为二叉树总的节点个数为2n,这时就可以的得到以下公式:

这时就可以得到x2为1时x1=383.5,这时节点个数不为整数因此度为1的节点1个时这种情况排除
所以x2一定为0这时x1=384

因此这题选B

接下来来完成一些链式二叉树的选择题

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为()
A ABDHECFG
B ABCDEFGH
C HDBEAFCG
D HDEBFGCA

当完全二叉树层序遍历为 ABCDEFGH,则该二叉树结构如下图所示

以上二叉树前序遍历结果就为:A B D H E F C F G

因此这题选A
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为___。
()
A E
B F
C G
D H

在二叉树的先序遍历中最新遍历到的就是根结点,这题中先序遍历为:EFHIGJK,所以根结点就为E

因此这题选A
3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为____。
A adbce
B decab
C debac
D abcde

在二叉树的后序遍历中最后遍历到的就是根结点,这题中先序遍历为:bdeca,所以根结点就为a
中序遍历为:badce,在结合根结点为a,则就可得b为叶子节点,dce再组成子树,子树根结点为c

通过以上分析就可以得出该二叉树结构如下图所示

通过以上图示就可以得出该二叉树前序遍历序列为:a b c d e

因此这题选D
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为
A FEDCBA
B CBAFED
C DEFCBA
D ABCDEF

在二叉树的后序遍历中最后遍历到的就是根结点,这题中先序遍历为:ABCDEF,所以根结点就为F

在该二叉树中中序遍历序列也为 ABCDEF,结合根结点为F,就可以得出根结点无右子树,之后节点也按照这样分析就可以得出该二叉树结构如下图所示

通过以上图示就可以得出该二叉树前序遍历序列为:F E D C B A

因此这题选A

以上就是二叉树算法题篇章的所有内容了,希望能得到你的点赞收藏

相关推荐
A懿轩A28 分钟前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
古希腊掌管学习的神29 分钟前
[搜广推]王树森推荐系统——矩阵补充&最近邻查找
python·算法·机器学习·矩阵
云边有个稻草人32 分钟前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
半盏茶香33 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
忘梓.1 小时前
解锁动态规划的奥秘:从零到精通的创新思维解析(3)
算法·动态规划
️南城丶北离2 小时前
[数据结构]图——C++描述
数据结构··最小生成树·最短路径·aov网络·aoe网络
✿ ༺ ོIT技术༻2 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
tinker在coding4 小时前
Coding Caprice - Linked-List 1
算法·leetcode
XH华8 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生8 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论