【LeetCode】二叉树基础练习 5 道题

第一题:单值二叉树

题目介绍:

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树
只有给定的树是单值二叉树时,才返回true;否则返回false

//题目框架
bool isUnivalTree(struct TreeNode* root){
}

问题分析:

很多老铁看到这道题,一上来会选择直接遍历二叉树来试图解决这道题。当然遍历固然可行,这道题使用二叉树的前中后遍历的方式来解决,虽然实现的过程存在一定的差异,但都能做出来。
这里给出前序遍历的实现,以便参考。
前序遍历,无非是先判断根节点,在判断左右子树。根节点的值不一样,返回false,左右子树中任何一方存在节点的值不一样都返回false

bool PreOrderCompare(struct TreeNode* root, int val)
{
    if(NULL == root)
    {
        return true;
    }
	
	//先判断根节点
    if(val != root->val)
    {
        return false;
    }
	
	//在判断左右子树
    return PreOrderCompare(root->left,val) && PreOrderCompare(root->right,val);
}

bool isUnivalTree(struct TreeNode* root)
{
    if(NULL == root)
    {
        return true;
    }
	
	//比较后,所有的值都相同返回true;存在有的值不相同返回false.
    return PreOrderCompare(root, root->val);
    
}

但是,我这里想给出另一种非遍历比较的方法。
要判断一棵二叉树的每个结点是否都具有相同的值,只要判断这棵二叉树的根节点、左子树、右子树是否都是相同的值即可。
如果左子树节点的值==根节点的值 并且 右子树节点的值==根节点的值,那么由等式的传递性,就可以知道这棵二叉树是单值二叉树。
所以,可以给出参考代码如下:

bool isUnivalTree(struct TreeNode* root)
{
    if(NULL == root)
    {
        return true;
    }
	
	//判断左子树的值是否等于根节点的值
    if(root->left!=NULL && root->left->val!=root->val)
    {
        return false;
    }
    
    //判断右子树的值是否等于根节点的值
    if(root->right!=NULL && root->right->val!=root->val)
    {
        return false;
    }
	
	//接着从左右子树的根节点开始判断
    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

这种比较判断的方法,本质也是利用了类似前序遍历的方式来解决的。
有小伙伴想使用中序遍历或后序遍历来解决这个问题,其实从分析来看是不太好的。
因为,假设整棵树的根节点的值就是那个异同的值,或者异同的值在整棵树比较靠上的层次。中序遍历和后序遍历都是在后来才遍历根节点,需要先去树里面找一圈回来才发现有一个异同的值,这样的话就增加了不必要的遍历。虽然问题能解决,但肯定不是优解。
注意:如果二叉树为空的话,其实并不违背单值二叉树的定义,也就是二叉树中并不存在不相等的值,所以仍然可以把空树看作单值二叉树。以上代码也是如此处理的。

第二题:相同的树

题目介绍:

给你两棵二叉树的根节点pq,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

//题目框架
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
}

问题分析:

判断这两棵二叉树是否相同,只要判断根节点相同,并且左右子树相同即可。
但只要根节点,或者左右子树有一个不相同,那就返回false
同时要注意树是否为空的情况处理。

bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
	//两棵树都为空的情况
    if(NULL==p&&NULL==q)
    {
        return true;
    }
    
    //一棵树为空,一棵树不为空的情况
    if(NULL==p||NULL==q)
    {
        return false;
    }
	
	//判断根节点
    if(p->val!=q->val)
    {
        return false;
    }

	//判断左右子树
    return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}

上述代码,仍是以前序遍历的思想来解决的。

第三题:对称二叉树

题目介绍:

给你一个二叉树的根节点root, 检查它是否轴对称。
在这里插入图片描述

//题目框架
bool isSymmetric(struct TreeNode* root){
}

问题分析:

从上图中可以看出,二叉树中整棵树的根节点对于判断该二叉树是否对称影响并不大。
所以,如果根节点为空,直接判断它是对称的(其实空树并不违背对称的性质)。
如果整棵二叉树的根节点不为空,就去判断它的左右子树是否符合对称的性质即可。
但是要说明的是,子树中的根节点已经不能像整棵二叉树的根节点那样进行同样的处理了。至于原因看图就懂了😉。
所以,从上面分析之后,我们可以将代码分成两部分来处理。如下面所示。

bool isSymmetricSubTree(struct TreeNode* root1,struct TreeNode* root2)
{
	//这两个if用法和《相同的树》代码中的用法是一致的
    if(root1==NULL&&root2==NULL)
    {
        return true;
    }
    if(root1==NULL||root2==NULL)
    {
        return false;
    }
	
	//根节点值的判断
    if(root1->val!=root2->val)
    {
        return false;
    }
	
	//注意:此处因为是进行对称的判断,由镜像对称的性质,可以知道,left和right是镜像对称点,所以传值不要弄错了
    return isSymmetricSubTree(root1->left,root2->right) && isSymmetricSubTree(root1->right,root2->left);
}
bool isSymmetric(struct TreeNode* root)
{
	//二叉树的根节点为空
    if(NULL==root)
    {
        return true;
    }
	
	//二叉树的根节点不为空
    return isSymmetricSubTree(root->left,root->right);
}

第四题:另一棵树的子树

题目介绍:

给你两棵二叉树rootsubRoot。检验root中是否包含和subRoot具有相同结构和节点值的子树。如果存在,返回true;否则,返回false
二叉树tree的一棵子树包括tree的某个节点和这个节点的所有后代节点。tree也可以看做它自身的一棵子树。
root树上的节点数量范围是[1, 2000]
subRoot树上的节点数量范围是[1, 1000]
在这里插入图片描述

//题目框架
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
}

问题分析:

认真分析之后会发现,这个题就是是《相同的树》的变相问法。
为什么这么说呢?
其实,要判断subRoot的子树是否在root的树之中,肯定要去root树中去找呀(你这不废话吗😓)。
嘿嘿别急,既然去找,那如何去找呢?答案无非就是遍历了呗。
所以,其实就这么简单。
遍历root的每个节点,并都看做其所在子树的根节点,然后使用《相同的树》的判断方法,去和subRoot所在的子树进行比较。如果相同返回true,遍历完后还没找到,就返回false
鉴于此,可得如下代码:

//采用前序遍历
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
    if(isSameTree(root,subRoot))
    {
        return true;
    }
	
	//左右子树中有一个找到了,就返回true.
    return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}

第五题:二叉树遍历

说明:这个题是来自牛客网的,在力扣上没能找到,大家见谅了。

题目介绍:

编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
输入包括1行字符串,长度不超过100

问题分析:

这道题归根到底主要在于二叉树的创建。题目给出了二叉树的前序遍历字符串(带空节点),那咱也自然按照前序的方式来建树(此建树非彼建树,哈哈😜)了。
其实大多数的时候也只能是用前序的方式来建立树了。如果没有根节点的话,生成的左右子树节点的地址就不能直接被保存在根节点的指针域中。而将这些地址保存起来,最终放到后来生成的根节点中的话,想想就比较麻烦,不如使用前序遍历来得直接。另外从自然发展的角度来看的话,如果一棵树连根都没有,那它又如何能枝繁叶茂呢。
也就是说,使用先序遍历来建树,先建立根节点,再建立根节点的左子树,再建立根节点的右子树。如果是空节点#,就返回NULL,不是空节点,就返回创建的root节点。
根据此思路,可以给出二叉树前序创建的代码:

//str代表前序遍历的字符串
//pi是记录字符串下标的地址
struct TreeNode* CreateTree(char* str, int* pi)
{
	//遇到‘#’,返回NULL
    if('#'==str[*pi])
    {
        (*pi)++;
        return NULL;
    }

	//先创建根节点,在创建左子树,再创建右子树,最后返回根节点
    struct TreeNode* root=BuyNode(str[(*pi)++]);
    root->left = CreateTree(str,pi);
    root->right = CreateTree(str,pi);
    return root;
}

好了,通过上面的代码将二叉树创建出来以后,只需再中序遍历一遍,打印输出就OK了。
完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//二叉树节点定义
struct TreeNode
{
    char val;
    struct TreeNode* left; 
    struct TreeNode* right;
};

//生成一个节点
struct TreeNode* BuyNode(char c)
{
    struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    assert(newNode);

    newNode->val=c;
    newNode->left=NULL;
    newNode->right=NULL;

    return newNode;
}

struct TreeNode* CreateTree(char* str, int* pi)
{
    if('#'==str[*pi])
    {
        (*pi)++;
        return NULL;
    }

    struct TreeNode* root=BuyNode(str[(*pi)++]);
    root->left = CreateTree(str,pi);
    root->right = CreateTree(str,pi);
    return root;
}

//中序遍历打印
void InOrderPrint(struct TreeNode* root)
{
    if(NULL==root)
    {
        return;
    }

    InOrderPrint(root->left);
    printf("%c ", root->val);
    InOrderPrint(root->right);
}

int main() 
{
    char str[101]={0};
    scanf("%s",str);
    int i=0;
    struct TreeNode* root =  CreateTree(str, &i);

    InOrderPrint(root);

    return 0;
}

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

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

相关文章

【24】Verilog进阶 - 序列检测2

VL35 状态机-非重叠的序列检测 1 思路 状态机嘛,也是比较熟悉的朋友啦, 我就火速写出了STG。如下黑色所示: 2 初版代码 `timescale 1ns/1nsmodule sequence_test1(input wire clk ,input wire rst ,input wire data ,output reg flag ); //*************code**********…

系统架构:经典三层架构

引言 经典三层架构是分层架构中最原始最典型的分层模式&#xff0c;其他分层架构都是其变种或扩展&#xff0c;例如阿里的四层架构模式和DDD领域驱动模型。阿里的 四层架构模型在三层基础上增加了 Manager 层&#xff0c;从而形成变种四层模型&#xff1b;DDD架构则在顶层用户…

Canvas百战成神-圆(1)

Canvas百战成神-圆 初始化容器 <canvas id"canvas"></canvas>canvas{border: 1px solid black; }让页面占满屏幕 *{margin: 0;padding: 0; } html,body{width: 100%;height: 100%;overflow: hidden; } ::-webkit-scrollbar{display: none; }初始化画笔…

JavaEE--Thread 类的基本用法(不看你会后悔的嘿嘿)

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都唯一对应着一个Thread对象. 因此,认识和掌握Thread类弥足重要. 本文将从 线程创建线程中断线程等待线程休眠获取线程实例 等方面来进行具体说明. 1)线程创建 方法1:通过创建Thread类的子类并重写run () 方法 class M…

UDS 14229 -1 刷写34,36,37服务,标准加Trace讲解,没理由搞不明白

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

Windows环境下实现设计模式——状态模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现状态模式&#xff08;设计模式&#xff09;。不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff0c;无…

【完整代码】用HTML/CSS制作一个美观的个人简介网页

【完整代码】用HTML/CSS制作一个美观的个人简介网页整体结构完整代码用HTML/CSS制作一个美观的个人简介网页——学习周记1HELLO&#xff01;大家好&#xff0c;由于《用HTML/CSS制作一个美观的个人简介网页》这篇笔记有幸被很多伙伴关注&#xff0c;于是特意去找了之前写的完整…

【高阶数据结构】红黑树

文章目录1. 使用场景2. 性质3. 结点定义4. 结点旋转5. 结点插入1. 使用场景 Linux进程调度CFSNginx Timer事件管理Epoll事件块的管理 2. 性质 每一个节点是红色或者黑色根节点一定是黑色每个叶子节点是黑色如果一个节点是红色&#xff0c;那么它的两个儿子节点都是黑色从任意…

【基础算法】单链表的OJ练习(6) # 复制带随机指针的链表 #

文章目录&#x1f347;前言&#x1f34e;复制带随机指针的链表&#x1f351;写在最后&#x1f347;前言 本章的链表OJ练习&#xff0c;是最后的也是最难的。对于本题&#xff0c;我们不仅要学会解题的思路&#xff0c;还要能够通过这个思路正确的写出代码&#xff0c;也就是思路…

20230314整理

1.JVM内存区域 程序计数器&#xff1a;字节码解释器通过改变程序计数器来依次读取指令&#xff0c;在多线程的情况下&#xff0c;程序计数器用于记录当前线程执行的位置&#xff0c;从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。它的生命周期随着线程的创建而创…

基于Java+SpringBoot+vue的学生成绩管理系统设计和实现【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战 &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例&#xff08;200套&#xff09; 目录 一、效果演示 二、…

扯什么 try-catch 性能问题?

“yes&#xff0c;你看着这鬼代码&#xff0c;竟然在 for 循环里面搞了个 try-catch&#xff0c;不知道try-catch有性能损耗吗&#xff1f;”老陈煞有其事地指着屏幕里的代码&#xff1a; for (int i 0; i < 5000; i) {try {dosth} catch (Exception e) {e.printStackTrace…

如何测试一个AI系统?

最近AI大火&#xff0c;chatgpt、GPT-4、文心一言不断的在轰炸着我们的生活、工作&#xff0c;很多时候我们都在感叹这智能化来的太快了。对于一个测试工程师&#xff0c;如何开始测试一个AI系统呢&#xff0c;今天我们就一起来聊聊相关的内容。 智能系统对测试工程师提出的新问…

2023年网络安全比赛--网络安全事件响应中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.找出黑客植入到系统中的二进制木马程序,并将木马程序的名称作为Flag值(若存在多个提交时使用英文逗号隔开,例如bin,sbin,…)提交; 2.找出被黑客修改的系统默认指令,并将被修改的指令里最后一个单词作为Flag值提交; 3.找出…

React 用一个简单案例体验一遍 React-dom React-router React-redux 全家桶

一、准备工作 本文略长&#xff0c;建议耐心读完&#xff0c;每一节的内容与上一节的内容存在关联&#xff0c;最好跟着案例过一遍&#xff0c;加深记忆。 1.1 创建项目 第一步&#xff0c;执行下面的命令来创建一个 React 项目。 npx create-react-app react-example cd rea…

Springboot集成Swagger

一、Swagger简介注意点&#xff01; 在正式发布的时候要关闭swagger&#xff08;出于安全考虑&#xff0c;而且节省内存空间&#xff09;之前开发的时候&#xff0c;前端只用管理静态页面&#xff0c; http请求到后端&#xff0c; 模板引擎JSP&#xff0c;故后端是主力如今是前…

【宝塔面板部署nodeJs项目】网易云nodeJs部署在云服务器上,保姆级教程,写网易云接口用自己的接口不受制于人

看了很多部署的&#xff0c;要么少步骤&#xff0c;要么就是写的太简洁&#xff0c;对新手不友好 文章目录前言一、下载网易云nodejs项目1. git clone下载&#xff0c;两种方式2. 运行项目二、使用步骤1. 先在本地运行2.测试接口三、部署服务器1. 在宝塔面板安装pm2管理器2. 压…

字符函数和字符串函数【上篇】

文章目录&#x1f396;️1.函数介绍&#x1f4ec;1.1. strlen&#x1f4ec;1.2. strcpy&#x1f4ec;1.3. strcat&#x1f4ec;1.4. strcmp&#x1f4ec;1.5. strncpy&#x1f4ec;1.6. strncat&#x1f4ec;1.7. strncmp&#x1f396;️1.函数介绍 &#x1f4ec;1.1. strlen …

入行 5年,跳槽 3次,我终于摸透了软件测试这行(来自过来人的忠告)

目录 前言 第一年 第二年 第三年 第四年 作为过来人的一些忠告 前言 最近几年行业在如火如荼的发展壮大&#xff0c;以及其他传统公司都需要大批量的软件测试人员&#xff0c;但是20年的疫情导致大规模裁员&#xff0c;让人觉得行业寒冬已来&#xff0c;软件测试人员的职…

【YOLOv8/YOLOv7/YOLOv5/YOLOv4/Faster-rcnn系列算法改进NO.60】损失函数改进为wiou

前言作为当前先进的深度学习目标检测算法YOLOv8&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列文章&#xff0c;将重点对YOLOv8的如何改进进行详细的介绍&…