二叉树OJ题目

一.二叉树第k层结点个数

有这样的一个思路:我既然要求第k层的结点个数,我肯定是要用到递归,那么当我在递归到第k层的时候我就开始判断,这一层是不是我所需要的那一层,如果是,就计数有几个节点,如果不是,就继续递归。就像这个图:

我们在往下递归的时候,我们的k也随之递减,当k为1的时候,这里就是我们要求的那一层。 

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->left, k - 1) 
		   + BinaryTreeLevelKSize(root->right, k - 1);
}

二.二叉树查找值为x的结点

这个要处理的细节就比较多了

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data = x)
		return root;

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)//如果ret1不为NULL就说明找到了
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->right, x);
	if (ret2)//与ret1同理
		return ret2;

	return NULL;//最底层的叶子结点找完了,依然没有符合的,返回NULL
}

在这个函数中依旧是需要不断的递归,首先是一直往左子树走,如果没有找到,就要一直到左子树为空树,然后再去右子树找,层层递进。要注意的是当我们在往左子树和右子树递归的时候,我们一定要把值给记录下来再去返回。

如图所示,我们要去找6的话:

三.对称二叉树

OJ链接:对称二叉树

这道题依旧是递归的思想 

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool _isSymmetric(struct TreeNode* root1,struct TreeNode* root2)
{
    if(root1==NULL&&root2==NULL)
    return true;//如果两个都是空树,就说明这个结点是对称的

    if(root1==NULL||root2==NULL)
    return false;//上面的if已经排除了两个都是空树的情况,如果这个成立,说明必有一个不是空树,则就是不对称的

    if(root1->val!=root2->val)
    return false;//两棵树都不是空树,就比较它们的值,注意不能用两值相等作为判断条件,因为如果相等就返回的话,后面的结点就比不了了

    return _isSymmetric(root1->left,root2->right)&&_isSymmetric(root1->right,root2->left);
    //最后就递归,左树的左子树要跟右树的右子树相等,左树的右子树要跟右树的左子树相等
}
bool isSymmetric(struct TreeNode* root) {
    return _isSymmetric(root->left,root->right);
}

注意看注释。

四.二叉树的前序遍历

OJ题目链接:二叉树的前序遍历

看一下我上一篇博客理解了,这个相对于其他的OJ是比较简单的: 

/**
 * 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 PreOrder(struct TreeNode* root,int* a,int* pi)//这个函数就是前序遍历
{
    if(root==NULL)//为空了就结束,返回到上一层
    return;
    a[(*pi)++]=root->val;//先把根的值一个一个的往数组里送
    PreOrder(root->left,a,pi);//然后是左子树
    PreOrder(root->right,a,pi);//右子树
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize=TreeSize(root);
    int* a=(int*)malloc(sizeof(int)*(*returnSize));//右多少个结点我们就开多少的空间
    int i=0;
    PreOrder(root,a,&i);//前序遍历
    return a;//把数组返回
}

 五.相同的树

OJ链接:相同的树

 这个其实就跟上面的那个对称二叉树有异曲同工之妙,只是换了一下对比的方向。看一下上面的代码,这个也就十分的好理解。

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

六.另一棵树的子树 

OJ链接:另一棵树的子树 

 

注意一下这个题是在上一个判断相同的树的基础上去做的

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

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){

    if(root==NULL)//如果树为NULL就不可能是子树
    return false;
    if(root->val==subRoot->val&&isSameTree(root,subRoot))//判断一下根的值相不相等,如果相等判断一下这两个树是不是一样的
    return true;
    
    return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);//然后在这里开始遍历
}

 七.二叉树的构建及遍历

OJ链接:二叉树的构建及遍历

这道题就是把二叉树的构建和遍历整合到一起 

#include <stdio.h>
#include<stdlib.h>
typedef struct BTNode
{
    struct BTNode* left;
    struct BTNode* right;
    char val;
}BTNode;
BTNode* CreatTree(char* a,int* pi)
{
    if(a[*pi]=='#')//如果是#就说明这个结点为空树
    {
        (*pi)++;//数组往后走
        return NULL;
    }
    BTNode* root=(BTNode*)malloc(sizeof(BTNode));//动态开辟一块内存
    root->val=a[(*pi)++];//这个是根,把数组的值给根
    root->left=CreatTree(a,pi);//然后从左子树开始递归
    root->right = CreatTree(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* root=CreatTree(a, &i);
    InOrder(root);
    return 0;
}

这个就是这个题的过程。

如果只看上图不能理解的话看一下下面的代码遍历: 

 

 八.二叉树的销毁

二叉树的销毁就十分简单了,只需要记住一个点,因为我们要用到递归,根要放到最后销毁,如果提前销毁了就会导致找不到左右子树。

void BTDestory(BTNode* root)
{
    if (root == NULL)
    {
        return;
    }
    BTDestory(root->left);
    BTDestory(root->right);
    free(root);
}

九.判断二叉树是否为完全二叉树(层序遍历)

解决这个题我们用层序遍历的话会很好写。

首先先说一下层序遍历:

设二叉树的根结点所在 层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层 上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

简单的说,我们遍历的顺序是这样的:

要实现这个,我们可以用一下队列。

先构建结构体二叉树:

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

这个是队列的实现:

特别注意一下第一行的代码,我构建的队列里面的值是指针

typedef struct BTNode* QDataType;

typedef struct QueueNode
{
    struct QueueNode* next;
    QDataType a;
}QNode;
typedef struct Queue
{
    QNode* phead;
    QNode* ptail;
    int size;
}Queue;

void QueueInit(Queue* pq)
{
    assert(pq);
    pq->phead = pq->ptail = NULL;
    pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
    assert(pq);
    QNode* pcur = pq->phead;
    while (pcur)
    {
        QNode* next = pcur->next;//把下一个节点提前保存起来
        free(pcur);
        pcur = next;//指针往后移动
    }
    pq->phead = pq->ptail = NULL;//全部释放完毕后注意指针也要置为NULL
    pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
    assert(pq);
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    if (newnode == NULL)
    {
        perror("QueuePush::malloc");
        return;
    }
    newnode->a = x;
    newnode->next = NULL;
    if (pq->ptail == NULL)
    {
        pq->phead = pq->ptail = newnode;
    }
    else
    {
        pq->ptail->next = newnode;
        pq->ptail = pq->ptail->next;
    }
    pq->size++;
}
void QueuePop(Queue* pq)
{
    assert(pq);
    assert(pq->size != 0);
    // 一个节点
    if (pq->phead->next == NULL)
    {
        free(pq->phead);
        pq->phead = pq->ptail = NULL;
    }
    else // 多个节点
    {
        QNode* next = pq->phead->next;
        free(pq->phead);
        pq->phead = next;
    }

    pq->size--;
}
QDataType QueueFront(Queue* pq)
{
    assert(pq);
    assert(pq->phead);
    return pq->phead->a;
}
bool QueueEmpty(Queue* pq)
{
    assert(pq);
    return pq->size == 0;
}

 然后就是正式的层序遍历:

void LevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);//初始化队列
    if (root)//如果根不为空,就入队列里面
        QueuePush(&q, root);
    while (!QueueEmpty(&q))//循环是等到队列完全没有值了跳出循环
    {
        BTNode* front = QueueFront(&q);//提出队头的值
        QueuePop(&q);//删除队头
        printf("%d ", front->val);//打印二叉树的根结点的值
        if (front->left)//如果左子树不为空,就把左子树入到队列里
            QueuePush(&q, front->left);
        if (front->right)//同理,如果右子树不为空,就把右子树入到队列里
            QueuePush(&q, front->right);
    }
    QueueDestroy(&q);
}

知道了层序遍历,我们就可以做这道题目了:

我们知道,关于完全二叉树它的最后一层的结点一定是顺序存放的。所以我们就可以利用层序遍历往后走,即使遇到空也进队列。当我们遇到第一个空结点时,我们就开始判断,如果后面全是空这棵树就是完全二叉树,反之不是。

bool BTComplete(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if (root)
        QueuePush(&q, root);
    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        if (front == NULL)//如果刚好是一个完全二叉树,那么这里为NULL了就说明所有的结点都遍历完毕
        {
            break;//直接跳出循环
        }
        QueuePush(&q, front->left);
        QueuePush(&q, front->right);
    }
    while (!QueueEmpty(&q))//这个循环就是用来判断后面有没有非空节点,有的话就不是完全二叉树
    {
        BTNode* front = QueueFront(&q);
        if (front)//进入这个if语句就说明找到了非空的结点,就不是完全二叉树
        {
            QueueDestroy(&q);
            return false;
        }
    }
    QueueDestroy(&q);
    return true;
}

可以带入图看一下:

到这里这些题目就完全结束了,当然二叉树还没有结束,我现在这个程度,二叉树差不多到这里就是极限了。感谢大家的观看,如有错误还请多多指出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/642272.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从零开始写 Docker(十五)---实现 mydocker run -e 支持环境变量传递

本文为从零开始写 Docker 系列第十五篇&#xff0c;实现 mydocker run -e, 支持在启动容器时指定环境变量&#xff0c;让容器内运行的程序可以使用外部传递的环境变量。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实现…

【开发 | 环境配置】解决 VSCode 编写 eBPF 程序找不到头文件

问题描述&#xff1a; 在使用 vscode 编写 eBPF 程序时&#xff0c;如果不做一些头文件定位的操作&#xff0c;默认情况下头文件总是带有“红色下划线”&#xff0c;并且大部分的变量不会有提示与补全。 在编写代码文件较小时&#xff08;或者功能需求小时&#xff09;并不会…

42-2 应急响应之计划任务排查

一、进程排查 进程排查是指通过分析系统中正在运行的进程,以识别和处理恶意程序或异常行为。在Windows和Linux系统中,进程是操作系统的基本单位,因此对于发现和处理恶意软件或异常活动至关重要。恶意程序通常会以进程的形式在系统中运行,执行各种恶意操作,比如窃取信息、破…

CSAPP(datalab)解析

howManyBits /* howManyBits - 返回用二进制补码表示x所需的最小位数* 示例: howManyBits(12) 5* howManyBits(298) 10* howManyBits(-5) 4* howManyBits(0) 1* howManyBits(-1) 1* howManyBits(0x80000000) …

零部件销售|基于SSM+vue的轻型卡车零部件销售平台系统的设计与实现(源码+数据库+文档)

轻型卡车零部件销售平台 目录 基于SSM&#xff0b;vue的轻型卡车零部件销售平台系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2 管理员功能模块 3 用户后台功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题…

Leetcode—2769. 找出最大的可达成数字【简单】

2024每日刷题&#xff08;139&#xff09; Leetcode—2769. 找出最大的可达成数字 实现代码 class Solution { public:int theMaximumAchievableX(int num, int t) {return num t * 2;} };运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连…

Unity Assembly Definition Dotween 引用

原理&#xff1a; 具体Unity程序集原理用法&#xff0c;暂时留坑&#xff0c;不介绍了&#xff0c;相信有很多人也写过了 这里简单放个官方API链接 https://docs.unity3d.com/cn/current/Manual/ScriptCompilationAssemblyDefinitionFiles.html 现象 &#xff1a;Dotween引用…

二十七篇:未来掌控:嵌入式系统的革命性进展

未来掌控&#xff1a;嵌入式系统的革命性进展 1. 引言&#xff1a;嵌入式系统的重要性及其在未来科技中的角色 在当今这个数字化迅速发展的时代&#xff0c;嵌入式系统已成为推动现代科技进步的基石。从智能手机到智能家居&#xff0c;从自动驾驶汽车到复杂的工业控制系统&…

四元数学习总结(1)

导语&#xff1a;相比矩阵&#xff0c;用四元数处理3D旋转的优势是毋庸置疑的&#xff0c;但由于概念复杂&#xff0c;难于理解&#xff0c;一直令我摸不着头脑。最近学习更是发现在机器人、无人机、SLAM等先进领域&#xff0c;四元数被当成实数、整数这样的基础&#xff0c;所…

详解CSS(一)

目录 1.CSS是什么 2.基本语法规范 3.引入方式 3.1内部样式表 3.2行内样式表 3.3外部样式表 4.选择器 4.1基础选择器 4.1.1标签选择器 4.1.2类选择器 4.1.3id选择器 4.1.4通配符选择器 4.2复合选择器 4.2.1后代选择器 4.2.2子选择器 4.2.3并集选择器 4.2.4伪类选择…

虚拟化技术[3]之网络虚拟化

网络虚拟化 网络虚拟化简介核心层网络虚拟化接入层网络虚拟化虚拟机网络虚拟化案例: VMware网络虚拟化技术虚拟网络接口卡虚拟交换机vSwitch分布式交换机端口组VLAN 网络虚拟化简介 传统的数据中心&#xff1a;服务器之间操作系统和上层软件异构、接口与数据格式不统一&#x…

@JsonFormat注解出现日期序列化以及反序列化问题(日期比实际日期少一天)

文章目录 前言一、场景如下所示二、问题分析三、JsonFormat注解是什么以下是 JsonFormat 注解的一些常用属性&#xff1a; 四、解决问题解决方式&#xff1a;只需要指定对应的时区就好效果如下&#xff1a; 五、JsonFormat 注解时出现日期问题总结 前言 在一次的偶然机会下发现…

二进制中1的个数c++

题目描述 计算鸭给定一个十进制非负整数 NN&#xff0c;求其对应 22 进制数中 11 的个数。 输入 输入包含一行&#xff0c;包含一个非负整数 NN。(N < 10^9) 输出 输出一行&#xff0c;包含一个整数&#xff0c;表示 NN 的 22 进制表示中 11 的个数。 样例输入 100 …

【算法题】520 钻石争霸赛 2024 全解析

都是自己写的代码&#xff0c;发现自己的问题是做题速度还是不够快 520-1 爱之恒久远 在 520 这个特殊的日子里&#xff0c;请你直接在屏幕上输出&#xff1a;Forever and always。 输入格式&#xff1a; 本题没有输入。 输出格式&#xff1a; 在一行中输出 Forever and always…

Ubuntu 如何根据NVIDIA显卡型号确定对应的显卡驱动版本并安装

目录 一、查询推荐安装的驱动版本 二、安装推荐版本的驱动 1. 通过终端安装&#xff0c;只安装 nvidia 驱动&#xff08;亲测可用&#xff01;&#xff09; 2. 通过 software & Updates 安装&#xff0c;安装 nvidia 驱动。 三、查询能安装的最新的显卡驱动版本 1. 方…

洛谷P3574 [POI2014] FAR-FarmCraft(树形dp)

洛谷 P 3574 [ P O I 2014 ] F A R − F a r m C r a f t &#xff08;树形 d p &#xff09; \Huge{洛谷P3574 [POI2014] FAR-FarmCraft&#xff08;树形dp&#xff09;} 洛谷P3574[POI2014]FAR−FarmCraft&#xff08;树形dp&#xff09; 文章目录 题意题目说明 思路标程 题目…

『Stable Diffusion 』AI绘画,不会写提示词怎么办?

提示词 有没有想过&#xff0c;为什么你用 SD 生成的猫是长这样的。 而其他人可以生成这样的猫。 虽然生成的都是猫&#xff0c;但猫与猫之间还是有差距的。 如果你的提示词只是“cat”&#xff0c;那大概率就会出现本文第一张图的那个效果。而如果你加上一些形容词&#xff…

如何选择一款安全高效的数据自动同步工具?

随着科技的不断发展&#xff0c;企业处理的数据量愈发庞大。数字化浪潮的涌现使得数据在业务活动和决策中的角色变得日益重要&#xff0c;然而这些数据往往分布在不同的位置&#xff0c;需要进行同步和分类&#xff0c;以便更有效地利用。以下是一些常见的数据自动同步场景&…

【Linux安全】iptables防火墙(二)

目录 一.iptables规则的保存 1.保存规则 2.还原规则 3.保存为默认规则 二.SNAT的策略及应用 1.SNAT策略的典型应用环境 2.SNAT策略的原理 2.1.未进行SNAT转换后的情况 2.2.进行SNAT转换后的情况 3.SNAT策略的应用 3.1.前提条件 3.2.实现方法 三.DNAT策略及应用 1…