二叉树基础及实现(二,加经典OJ)

目录:

一 .接引二叉树(一)

二 .二叉树相关oj题:

1. 检查两颗树是否相同
2. 另一颗树的子树
3. 翻转二叉树
4. 判断一颗二叉树是否是平衡二叉树
5. 对称二叉树
6. 二叉树的构建及遍历
7. 二叉树的分层遍历 
8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
9. 根据一棵树的前序遍历与中序遍历构造二叉树
10. 根据一棵树的中序遍历与后序遍历构造二叉树
11. 二叉树创建字符串
12. 二叉树前序非递归遍历实现 
13. 二叉树中序非递归遍历实现
14. 二叉树后序非递归遍历实现

 


                                                                                 



一 .接引二叉树(一):承接上篇,不清楚的可以回去看看:二叉树基础及实现(一)-CSDN博客

1. 判断一棵树是不是完全二叉树:

图解:

把二叉树元素放入队列中,如果最后队列里全部是元素,“null”,则该二叉树就是完全二叉树。

这里注意区分,空和元素,“null”的概念

代码:

public boolean isCompleteTree(TreeNode root) {//空节点也是完全二叉树if (root == null) {return true;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {TreeNode cur = queue.poll();//空节点也放进去if (cur != null) {queue.offer(cur.left);queue.offer(cur.right);}else {break;}}

二 .二叉树相关oj题:

1. 检查两颗树是否相同:


题目:



图解:

代码:

 public boolean isSameTree(TreeNode p, TreeNode q) {//先判断结构是否一样if(p != null && q == null || q != null && p == null) {return false;}//走到这里,两个都为空或者两个都不为空//排除都为空树,但是空树也是树if(p == null && q == null) {return true;}//走到这里两个都不为空//开始判断树,根的值是否一样if(p.val != q.val) {return false;}//树都不为空,树根的值一样就判断:左子树 && 右边是否一样if(isSameTree(p.left, q.left) && isSameTree(p.right, q.right)) {return true;}return false;}


2. 另一颗树的子树:


题目:



图解:

代码:

 public boolean isSubtree(TreeNode root, TreeNode subRoot) {//这里是递归遍历root的返回条件,不能省略,我们要在root这颗大树上遍历找subRoot树if(root == null) {return false;}//判断整个树的根只有一次,就用isSameTreeif(isSameTree(subRoot, root)) return true;//来到第二层,这里要递归遍历root,//因为这棵树可能包含subRoot的子树if(isSubtree(root.left,subRoot)) return true;//root的左树if(isSubtree(root.right,subRoot)) return true;//root的右树return false;}//判断两棵树是否相同public boolean isSameTree(TreeNode p, TreeNode q) {//先= null) {return true;}//走到这里两个都不为空//开始判断树,根的值是否一样if(p.val != q.val) {return false;}//树都不为空,树根的值一样就判断:左子树 && 右边是否一样if(isSameTree(p.left, q.left) && isSameTree(p.right判断结构是否一样if(p != null && q == null || q != null && p == null) {return false;}//走到这里,两个都为空或者两个都不为空//排除都为空树,但是空树也是树if(p == null && q =, q.right)) {return true;}return false;}


3. 翻转二叉树:

题目:





图解:



代码:

 public TreeNode invertTree(TreeNode root) {if(root == null) {return null;}TreeNode tmp = root.left;root.left = root.right;root.right = tmp;//递归前序遍历invertTree(root.left);invertTree(root.right);return root;}

4. 判断一颗二叉树是否是平衡二叉树:

题目:





方法一:时间复杂度为O(N^2,N的平方);因为我们求高度自己实现的函数getHeight,是先遍历到,最后一个二叉树再逐一返回,当(isBalanced(root.left) &&  isBalanced(root.right),去递归每一棵树的左右子树)时会重复去算高度。

图解:

代码:

时间复杂度为O(N^2,N的平方);因为我们求高度自己实现的函数getHeight,是先遍历放到最后一次二叉树再,逐一返回,当(isBalanced(root.left) &&  isBalanced(root.right);//去递归每一棵树的左右子树)时会重复去算高度*/
class Solution {public boolean isBalanced(TreeNode root) {if(root == null) {return true;}//判断二叉树节点是平衡树int leftHeight = getHeight(root.left);int rightHeight = getHeight(root.right);return Math.abs(leftHeight - rightHeight) < 2 && isBalanced(root.left) &&  isBalanced(root.right);//去递归每一棵树的左右子树}public  int getHeight(TreeNode root) {if (root == null) {return 0;}//递归遍历左右树高度,遍历到root == null,再返回int leftTreeHeight = getHeight(root.left);int rightTreeHeight = getHeight(root.right);return Math.max(leftTreeHeight, rightTreeHeight) + 1;}
}


解法二:时间复杂度为O(N):

方法 : 返回时,顺便判断属不属于平衡二叉树,不属于返回-1,属于返回高度

代码;

 public boolean isBalanced(TreeNode root) {if(root == null) {return true;}//返回的高度 >= 0则是平衡二叉树return getHeight(root) >= 0;}public  int getHeight(TreeNode root) {if (root == null) {return 0;}//递归遍历左右树高度,遍历到root == null,再返回int leftTreeHeight = getHeight(root.left);if(leftTreeHeight < 0) {return -1;}int rightTreeHeight = getHeight(root.right);/**这里注意当二叉树遍历整棵树的最右边时,右边会返回-1,会破坏右边二叉树返回时的高度所以,rightTreeHeight的值要判断;如果等于-1,就不可以向上返回限制:(rightTreeHeight>=0)*/if(rightTreeHeight>=0 && Math.abs(leftTreeHeight-rightTreeHeight) <= 1){//是平衡二叉树,返回左右树做大值加1return Math.max(leftTreeHeight, rightTreeHeight) + 1;}else {return -1;}}


5. 对称二叉树:

题目:



图解:



代码:

public boolean isSymmetric(TreeNode root) {if(root == null) {return true;}return isSymmetric(root.left, root.right);}//封装起来判断,每个树的左右子树是否对称public boolean isSymmetric(TreeNode leftTree, TreeNode rightTree) {//结构if(leftTree == null && rightTree != null || leftTree != null && rightTree == null) {return false;}if(leftTree == null && rightTree == null) {return true;}//值if(leftTree.val != rightTree.val) {return false;}//开始判断对称,现在leftTree和rightTree都作为根return isSymmetric(leftTree.left,rightTree.right) &&isSymmetric(leftTree.right,rightTree.left);}


6. 二叉树的构建及遍历:

题目:





图解:



 代码:

import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {static class TreeNode {public char val;public TreeNode left;public TreeNode right;public TreeNode(char val) {this.val = val;}}public static void main(String[] args) {Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseString str = in.nextLine();TreeNode root = createTree(str);inorderTree(root);}}//创建二叉树://遍历字符串的全局变量,局部变量递归时会把i置0public static int i = 0;public static TreeNode createTree(String str) {TreeNode root = null;if(str.charAt(i) != '#') {//先创建一个根节点root = new TreeNode(str.charAt(i));//创建好,i往后走,创建左右子树的各个节点i++;root.left = createTree(str);root.right = createTree(str);}else {i++;}return root;}//中序遍历打印二叉树public static void inorderTree(TreeNode root) {if(root == null) {return;}inorderTree(root.left);System.out.print(root.val + " ");inorderTree(root.right);}}


7. 二叉树的分层遍历 

题目:





图解:

 



代码:

 public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> ret = new ArrayList<>();if (root == null) {return ret;}Queue<TreeNode> queue = new LinkedList<>();//先把根节点放入队列queue.offer(root);while (!queue.isEmpty()) {List<Integer> list = new ArrayList<>();//定义放在这里,等二叉树走完一层,才更新另外一层的sizeint size = queue.size();while(size != 0) {TreeNode cur = queue.poll();list.add(cur.val);if (cur.left != null) {queue.offer(cur.left);}if (cur.right != null) {queue.offer(cur.right);}size--;}//一维数组走完一个放入二维数组一个ret.add(list);}return ret;}

 

8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先:

题目:

 



图解:解法一:

代码:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) {return null;}if(root == p || root == q) {return root;}TreeNode leftTree = lowestCommonAncestor(root.left,p,q);TreeNode rightTree = lowestCommonAncestor(root.right,p,q);if(leftTree != null && rightTree != null) {return root;}else if(leftTree != null && rightTree == null) {return leftTree;}else {return rightTree;}}

 

图解2:方法二:通过两个栈解决:

代码:

 //最近公共祖先,方法二 :public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {Stack<TreeNode> stackP = new Stack<>();Stack<TreeNode> stackQ = new Stack<>();//1.把元素存入栈中getPath(root, p,stackP);getPath(root, q,stackQ);//2.求出那个栈元素多,让多的先出栈,直到两个栈元素一样多int sizeP = stackP.size();int sizeQ = stackQ.size();if (sizeP > sizeQ) {int size = sizeP - sizeQ;//元素多的栈先走出栈,他们的差值元素个数while (size != 0) {stackP.pop();size--;}}else {int size = sizeQ - sizeP;while (size != 0) {stackQ.pop();size--;}}//看看栈顶元素,是否相等不相等就弹出,相等则就是最近祖先while (!stackP.isEmpty() && !stackQ.isEmpty()) {//stackP == stackQ,比较节点,如果比较的值是Integer类型,超过一定范围就要无效,要用equals比较if ( stackQ.peek() == stackP.peek()) {return stackP.peek();}else {//元素不一样就出栈stackP.pop();stackQ.pop();}}return null;}/**** @param root* @param node:要找路径的q p 节点* @param stack:存放节点的栈*///获取q, p路径public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {if (root == null) {return false;}//节点放入栈中stack.push(root);//找到返回trueif (root == node) {return true;}//递归找q, p, 递归返回值retboolean ret = getPath(root.left,node,stack);//一层一层往上走if (ret) {return true;}ret = getPath(root.right,node,stack);if (ret) {return true;}//左右树遍历完没有找到,就把不是该路径的,节点弹出栈,并返回falsestack.pop();return false;}

9. 根据一棵树的前序遍历与中序遍历构造二叉树:

题目:
 



图解:

 

代码:

 public TreeNode buildTree(int[] preorder, int[] inorder) {return buildTreeChild(preorder, inorder,0,inorder.length-1);}public int preIndex;public TreeNode buildTreeChild(int[] preorder , int[] inorder,int inbegin, int inend) {//没有左树或者右树if(inbegin > inend) {return null;}//new一个根节点TreeNode root = new TreeNode(preorder[preIndex]);//在中序遍历中从,前序遍历那里找到,代表所有子树的 根节点int rootIndex = findVal(inorder,inbegin,inend, preorder[preIndex]);//preIndex,要改为全局变量,左右每次递归是,才不会重置为0preIndex++;//rootInde用完才加加;//递归每一个创建左右子树root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);return root;}private int findVal(int[] inorder,int inbegin, int inend,int val) {for(int i = inbegin; i<=inend; i++) {if(inorder[i] == val) {return i;}}return -1;}


10. 根据一棵树的中序遍历与后序遍历构造二叉树:

题目:



图解:
 

代码:

public TreeNode buildTree(int[] inorder, int[] postorder) {postorIndex = postorder.length-1;return buildTreeChild(inorder, postorder,0,inorder.length-1);}public int postorIndex;public TreeNode buildTreeChild(int[] inorder , int[] postorder,int inbegin, int inend) {//没有左树或者右树if(inbegin > inend) {return null;}//new一个根节点TreeNode root = new TreeNode(postorder[postorIndex]);//在中序遍历中从,前序遍历那里找到,代表所有子树的 根节点int rootIndex = findVal(inorder,inbegin,inend, postorder[postorIndex]);//preIndex,要改为全局变量,左右每次递归是,才不会重置为0postorIndex--;//rootInde用完才;//递归每一个创建左右子树root.right = buildTreeChild(inorder,postorder,rootIndex+1,inend);root.left = buildTreeChild(inorder,postorder,inbegin,rootIndex-1);return root;}private int findVal(int[] inorder,int inbegin, int inend,int val) {for(int i = inbegin; i<=inend; i++) {if(inorder[i] == val) {return i;}}return -1;}


 

 

11. 二叉树创建字符串:

题目:

图解:

 



代码:

 public String tree2str(TreeNode root) {if(root == null) {return null;}StringBuilder stringbuilder = new StringBuilder();tree2strChild(root, stringbuilder);return stringbuilder.toString();}public void tree2strChild(TreeNode t, StringBuilder stringbuilder) {if(t == null) {return;}//先拼接根节点stringbuilder.append(t.val);if(t.left != null) {stringbuilder = stringbuilder.append("(");tree2strChild(t.left, stringbuilder);//递归拼接左边//每一颗子树走完,就拼接“)”stringbuilder.append(")");}else {if(t.right == null) {//左右都为空return;}else {//左边为空,右边不为空就拼接 “()”。stringbuilder.append("()");}}if(t.right != null) {stringbuilder = stringbuilder.append("(");tree2strChild(t.right, stringbuilder);//递归拼接右边//每一颗子树走完,就拼接“)”stringbuilder.append(")");}else {//右边为空return;}}


12. 二叉树前序非递归遍历实现 

图解:这里用到栈

 

代码:

 // 方法二:List<Integer> list = new LinkedList<>();if (root == null) {return list;}Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;while (cur != null || !stack.isEmpty()) {while (cur != null) {//根节点开始要放入栈中cur = stack.push(cur);list.add(cur.val);cur = cur.left;}//cur == null,就把栈里的元素,拿到top引用中TreeNode top = stack.pop();//cur再走top的右边cur = top.right;}return list;


13. 二叉树中序非递归遍历实现:

这里和上面方法一样,只是在出栈时候,放入链表或者顺序表,又或者打印。
 

代码:

public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list =  new ArrayList<>();if(root == null) {return list;}TreeNode cur = root;Stack<TreeNode> stack = new Stack<>();while(cur != null || !stack.isEmpty()) {while(cur != null) {cur = stack.push(cur);cur = cur.left;} //cur == null,就把栈里的元素,拿到top引用中TreeNode top = stack.pop();list.add(top.val);//cur再走top的右边cur = top.right;}return list;}


14. 二叉树后序非递归遍历实现:

这里思路也是大同小异,但是这里要定义一个,prev引用,记录一下被打印过的节点,左右cur就不会一直在,top的右边死循环了

图解:

代码:

public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new LinkedList<>();//结束条件if (root == null) {return list;}Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;//prev负责记录已经被放入list (被打印过) 的结点TreeNode prev = null;while (cur != null || !stack.isEmpty()) {while (cur != null) {stack.push(cur);cur = cur.left;}TreeNode top = stack.peek();if (top.right == null || top.right == prev) {list.add(top.val);stack.pop();prev = top;//prev负责记录被打印过的结点}else {cur = top.right;}}return list;}

                                                                             

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Fine-BI学习笔记

官方学习文档&#xff1a;快速入门指南- FineBI帮助文档 FineBI帮助文档 (fanruan.com) 1.零基础入门 1.1 功能简介 完成四个流程&#xff1a;新建分析主题、添加数据、分析数据、分享协作。 示例数据获取&#xff1a;5分钟上手FineBI - FineBI帮助文档 (fanruan.com) 1.2 …

R语言优雅的进行广义可加模型泊松回归分析

泊松回归&#xff08;Poisson regression&#xff09;是以结局变量为计数结果时的一种回归分析。泊松回归在我们的生活中应用非常广泛&#xff0c;例如&#xff1a;1分钟内过马路人数&#xff0c;1天内火车站的旅客流动数&#xff0c;1天内的银行取钱人数&#xff0c;一周内的销…

【已解决】如何使用母版视图统一PPT格式?

母版视图在PPT中是一个强大的工具&#xff0c;可以帮助我们统一幻灯片的格式、布局和设计风格。今天来看看如何利用母版视图统一PPT格式&#xff0c;让每张幻灯片看起来一致和专业。 第一步&#xff1a;打开母版视图 打开PPT后&#xff0c;在顶部菜单栏中&#xff0c;选择【视…

Docker容器限制内存与CPU使用

文章目录 Docker 容器限制内存与 CPU 使用内存限额内存限制命令举例使用 `nginx` 镜像学习内存分配只指定 `-m` 参数的情况CPU 限制命令举例验证资源使用Docker 容器限制内存与 CPU 使用 在生产环境中,为了保证服务器不因某一个软件导致服务器资源耗尽,我们会限制软件的资源…

windows服务器启动apache失败,提示请通过cmd命令行启动:net start apache

Windows Server 2012 R2服务器突然停止运行apche&#xff0c;启动apache失败&#xff0c;提示请通过cmd命令行启动:net start apache 1.报错截图&#xff1a; 进入服务里输入命令启动也不行&#xff0c;提示由于登录失败而无法启动服务。 2.问题原因&#xff1a; 服务器www用…

Node.js知识点总结

Node.js知识点总结 Node.js其本质和浏览器一样是一个JavaScript运行环境。浏览器的运行环境为V8引擎浏览器内置API&#xff08;BOM、DOM、Canvas);而node.js的运行环境是V8引擎node提供的API(fs、path、http)。它使JavaScript可以编写后端。 基本知识 fs文件系统模块 它提供一…

springboot宠物相亲平台-计算机毕业设计源码16285

目 录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2 开发环境及相关技术介绍 2.1 MySQL数据库的介绍 2.2 B/S架构的介绍 2.3 Java语言 2.4 SpringBoot框架 3 宠物相亲平台系统分析 3.1 可行性分析 3.1.1 技术可行性分析 3.1.2 经济…

基于FPGA + Qt + OpenCv的人脸考勤系统

一:界面设计 客户端界面设计: 服务端界面设计: 简介:首先服务端在注册界面先注册人脸,然后客户端界面进行人脸识别,将人脸识别的图像发送给服务端以后,服务端在图像数据库里寻找人脸比对,若有数据就将查询到的个人信息发送给客户端,并在客户端显示,查询界面是用来查…

Ribbon负载均衡与内核原理

什么是Ribbon? 目前主流的负载方案分为两种&#xff1a; 集中式负载均衡&#xff0c;在消费者和服务提供方中间使用独立的代理方式进行负载&#xff0c;有硬件的&#xff08;比如F5&#xff09;&#xff0c;也有软件的&#xff08;Nginx&#xff09;客户端根据自己的请求做负…

开放式耳机哪种性价比高?五大高口碑优质款式耳机直入!

​或许我们的日常生活中充满了噪声&#xff0c;例如马路、地铁还有公交上&#xff0c;嘈杂的声音会影响我们的心情&#xff0c;同时还会损伤我们的耳朵&#xff0c;在嘈杂的环境中&#xff0c;想听歌想煲剧了怎么办&#xff0c;又不想沉浸在自己的世界里&#xff0c;就可以使用…

rk3588s 定制版 USB adb , USB2.0与USB3.0 区别,adb 由typeC 转换到USB3.0(第二部分)

硬件资源&#xff1a; rk3588s 核心板定制的地板 软件资源&#xff1a; 网盘上的 android12 源码 1 硬件上 客户只想使用 type c 接口中的 usb2.0 OTG 。在硬件上&#xff0c;甚至连 CC芯片都没有连接。 关于一些前置的知识。 1 USB2.0 与 USB3.0 的区别。 usb3.0 兼容2.0 …

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十九章 平台总线总结回顾

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

EEtrade:现货黄金盈利计算方法

现货黄金交易作为一种极具吸引力的投资方式&#xff0c;其盈利计算涉及多个关键因素&#xff0c;投资者需深入理解这些因素&#xff0c;才能准确评估交易结果&#xff0c;并制定科学的投资策略。 一、现货黄金基本盈利计算&#xff1a; 利润公式&#xff1a; 利润 (收盘价 -…

深入浅出mediasoup—WebRtcTransport

mediasoup 提供了多种 transport&#xff0c;包括 WebRtcTransport、PipeTransport、DirectTransport、PlainTransport 等&#xff0c;用来实现不同目的和场景的媒体通信。WebRtcTransport 是 mediasoup 实现与 WebRTC 客户端进行媒体通信的对象&#xff0c;是 mediasoup 最重要…

如何学习Airflow:糙快猛的大数据之路(附思维导图)

什么是Airflow? 在开始之前,让我们先简单了解一下Airflow是什么。Apache Airflow是一个开源的工作流管理平台。它允许你以代码的方式定义、调度和监控复杂的数据处理管道。 想象一下,你有一系列需要按特定顺序执行的任务,而且这些任务之间还有依赖关系,Airflow就是为解决这…

SpringBoot自动配置(面试重点)

自动配置是指&#xff1a; 自动配置是指在应用程序启动时&#xff0c;SpringBoot根据classpath路径下的jar包自动配置应用程序所需的一系列bean和组件&#xff0c;从而减少开发者的配置工作&#xff0c;提高开发效率。 一&#xff1a;Condition Condition是spring4.0之后添加…

linux离线安装mysql8(单机版)

文章目录 一、检查服务器是否有残留mysql资源&#xff0c;有的话就全删除1.1、查询mysql已安装的相关依赖&#xff1a;1.2、查找含有MySQL的目录 二、安装2.1、上传mysql安装包到文件夹下并解压2.2、移动及重命名2.3、mysql用户2.4、配置mysql所需的my.cnf文件2.5、给my.cnf配置…

JVM之经典垃圾回收器

1.垃圾回收器的分类 处理范围划分&#xff1a; 新生代垃圾回收器&#xff1a;serial、parNew、parallel scavenge&#xff1b; 老年代垃圾回收器&#xff1a;serial Old、parallel Old、CMS&#xff1b; 整堆收集器&#xff1a;G1、ZGC&#xff1b; 2.Serial GC Serial是单…

java单元测试:Mockito常用技巧

Mockito是Java中最流行的Mock框架之一&#xff0c;主要用于创建和配置模拟对象&#xff08;Mock&#xff09;&#xff0c;以测试代码的行为。Mockito使得单元测试更加简单和可控&#xff0c;特别是在需要隔离外部依赖的情况下。 1. Mockito简介 1.1 什么是Mockito Mockito是一个…

QGC二次开发入门教程(一):课程大纲

文章目录 前言一、课程大纲二、修改软件名称三、修改软件图标四、官方QGC中文版BUG修复五、汉化六、修改商标七、添加信号-槽八、添加QML和C交互九、MAVLINK的解析与发送十、换地图十一、添加自定义mavlink消息十二、在主工具栏添加一个自定义图标十三、解析自定义mavlink数据并…