二叉树练习习题题集二(Java)

devtools/2024/9/25 3:38:53/

1.

思路:从上到下,从左到右,先遍历到的先打印,可以用队列的特性“先进先出”实现,先放进根结点,并创建一个一维数组,然后求此时队列的个数size,每次弹出队列的一个元素放进一维数组,size--,如果size==0,就将这个一维数组放进存一维数组的一维数组,然后将不为null的左右孩子放进队列,如此循环即可

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> list=new ArrayList<List<Integer>>();//存放一维数组的一维数组Queue<TreeNode> queue=new LinkedList<TreeNode>();//创建一个队列if(root==null){//如果根结点为空return list;}queue.offer(root);//将根节点放进队列while(!queue.isEmpty()){List<Integer> list1=new ArrayList<Integer>();//创建一维数组int size=queue.size();//求此时队列的个数(即这一层的结点有多少个)while(size!=0){TreeNode node=queue.poll();//弹出队列的一个元素size--;//个数减一list1.add(node.val);//放进一维数组if(size==0){//如果此时个数等于0,说明这一层的结点都弹出完了list.add(list1);//将一维数组放进存放一维数组的一维数组}if(node.left!=null){//将不为null的左孩子放进队列queue.offer(node.left);}if(node.right!=null){//将不为null的右孩子放进队列queue.offer(node.right);}} }return list;}
}

2.

思路:只是变成了从下到上,只需要在从上到下的层序遍历的基础上,每次将一维数组放进存放一维数组的一维数组由原来的尾插变为头插即可(即只需要改一行代码,其他一模一样)

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<List<Integer>> levelOrderBottom(TreeNode root) {List<List<Integer>> list=new ArrayList<List<Integer>>();//存放一维数组的一维数组Queue<TreeNode> queue=new LinkedList<TreeNode>();//创建一个队列if(root==null){//如果根结点为空return list;}queue.offer(root);//将根节点放进队列while(!queue.isEmpty()){List<Integer> list1=new ArrayList<Integer>();//创建一维数组int size=queue.size();//求此时队列的个数(即这一层的结点有多少个)while(size!=0){TreeNode node=queue.poll();//弹出队列的一个元素size--;//个数减一list1.add(node.val);//放进一维数组if(size==0){//如果此时个数等于0,说明这一层的结点都弹出完了list.add(0,list1);//将一维数组放进存放一维数组的一维数组(头插)}if(node.left!=null){//将不为null的左孩子放进队列queue.offer(node.left);}if(node.right!=null){//将不为null的右孩子放进队列queue.offer(node.right);}}}return list;}
}

 3.

法一:

思路:判断一个结点是否是p,q的公共祖先,无非有一下三种情况:

1.p在左,q在右

2.p,q都在左边

3.p,q都在右边

此时只有第一种情况才能判断该结点是公共祖先,而第二,三种不能判断,还需要继续递归下去,让该结点的左孩子作为新的公共祖先候选人,然后让该结点的右孩子作为新的公共祖先候选人,如果左孩子这棵树找到了p或者q,那么左孩子这个结点就是公共祖先,同理,右孩子这棵树找到了p或者q,那么右孩子这个结点就是公共祖先。

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode(int x) { val = x; }* }*/
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root==null){//为空返回nullreturn null;}if(p==root||q==root){//如果是要找的结点,返回该结点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){//如果左树有,但右树为空,说明左树的根结点为公共祖先return leftTree;}else{//如果右树有,但左树为空,说明右树的根结点为公共祖先return rightTree;}}
}

法二:

在树里这个叫公共祖先,但是跟之前写过的交叉链表其实几乎一样,求两条链表的交叉点,方法就是求出两条链表的长度,然后求得差值,让长的先走差值步,然后以相同的速度一起走,相同的时候就是交叉点。这里树也同理,求得p,q两个结点的路径和长度,让路径较长的先走差值步,然后以相同的速度一起走,相同的时候就是公共祖先。那么怎么求根节点到p和q的路径呢

创建一个栈,用栈来记录路径,因为当我们找结点的时候,有可能这个结点不在这个路径上,所以这个结点要弹出,如果是队列的话,就不能将这个最后的结点弹出,而要先弹出根节点,所以要用栈来实现,发现这个结点不在路径上,因为“后进先出”,刚好就能把这个结点弹出去。

然后还是用递归去找左右孩子,如果左右孩子都没找到,就说明这个结点不在路径上,如果找到了,说明这个结点在路径上

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode(int x) { val = x; }* }*/
class Solution {//root是遍历到的结点,node是要找的结点,stack是存放路径的地方public boolean getPath(TreeNode root,TreeNode node,Stack<TreeNode> stack){if(root==null){//如果为空,说明没有找到return false;}stack.push(root);//不为空,将这个结点先进栈if(root==node){//如果这个结点是要找到结点return true;//说明找到了}boolean ret=getPath(root.left,node,stack);//找左孩子if(ret){//如果左孩子是要找到结点return true;//说明找到了}ret=getPath(root.right,node,stack);//找右孩子if(ret){//如果右孩子是要找到结点return true;//说明找到了}stack.pop();//左右孩子都没有,说明这个结点不在我们要找的结点的路径上return false;//没有找到}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {//特殊情况if(root==null){return null;}if(root==p||root==q){return root;}//存放两个结点的路径Stack<TreeNode> stackP=new Stack<>();Stack<TreeNode> stackQ=new Stack<>();getPath(root,p,stackP);getPath(root,q,stackQ);//判断哪个路径更长,长的先走差值步int sizeP=stackP.size();int sizeQ=stackQ.size();if(sizeP>sizeQ){int size=sizeP-sizeQ;while(size!=0){stackP.pop();size--;}}if(sizeP<sizeQ){int size=sizeQ-sizeP;while(size!=0){stackQ.pop();size--;}}//走完差值步,以相同速度走,相同则是公共祖先while(!stackP.isEmpty()&&!stackQ.isEmpty()){if(stackP.peek()==stackQ.peek()){return stackP.pop();}else{stackP.pop();stackQ.pop();}}//没有公共祖先(即不在同一棵树)return null;}
}

4.

思路:

首先要了解怎么通过前序和中序创建二叉树的步骤

前序是用来找根的,中序是用来判断左右树的结点的

第一步:从下标0开始遍历前序数组

第二步:每从前序数组遍历一个元素,就去中序数组找对应的元素并记录该元素在中序数组的下标

第三步:那么该中序下标左边的元素都在它的左树,右边的元素都在它的右树

第四步:循环第一步到第三步,找到刚刚那个结点的左右孩子的结点,直到该下标左右没有元素,说明该结点没有左右孩子,是叶子结点

所以现在就是将如上步骤转换成代码即可

class Solution {//用来遍历前序数组的下标(必须用全局变量,如果是局部变量,递归回来局部变量又没有发生变化)public int preindex=0;//用来在中序数组中找到前序数组遍历到的元素//在arr数组中(中序数组),从begin到end区间里,找val,返回下标i,没有就返回-1private int findval(int[] arr,int begin,int end,int val){for(int i=begin;i<=end;i++){if(arr[i]==val){return i;}}return -1;}//用来创建二叉树,preorder:前序数组,rt:根结点的下标,inorder:中序数组//inbegin:这棵树的孩子结点在中序数组的起点下标,inend:这棵树的孩子结点在中序数组的结束下标public TreeNode buildTreeChild(int[] preorder,int rt,int[] inorder,int inbegin,int inend){//如果没有孩子结点,返回nullif(inbegin>inend){return null;}//创建根结点TreeNode root=new TreeNode(preorder[rt]);//往后遍历前序数组的下标++preindex++;//找到该根结点在中序数组的下标int rootindex=findval(inorder,inbegin,inend,preorder[rt]);//创建左孩子这棵树的根root.left=buildTreeChild(preorder,preindex,inorder,inbegin,rootindex-1);//创建右孩子这颗树的根root.right=buildTreeChild(preorder,preindex,inorder,rootindex+1,inend);//返回根节点return root;}//输入前序数组和中序数组创建二叉树public TreeNode buildTree(int[] preorder, int[] inorder){return buildTreeChild(preorder,preindex,inorder,0,inorder.length-1);}
}

5.

思路:

同上面一样,只不过由前序变成后序

因为前序是:根,左,右;后序是:左,右,根

所以后序数组中,最后一个元素才是根结点,如何往前遍历,先是右树的根,再是左树的根

所以只需要将上一题的代码稍作修改即可

class Solution {//用来遍历后序数组的下标(必须用全局变量,如果是局部变量,递归回来局部变量又没有发生变化)public int postindex=0;//用来在中序数组中找到后序数组遍历到的元素//在arr数组中(中序数组),从begin到end区间里,找val,返回下标i,没有就返回-1private int findval(int[] arr,int begin,int end,int val){for(int i=begin;i<=end;i++){if(arr[i]==val){return i;}}return -1;}//用来创建二叉树,inorder:中序数组,rt:根结点的下标,postorder:后序数组//inbegin:这棵树的孩子结点在中序数组的起点下标,inend:这棵树的孩子结点在中序数组的结束下标public TreeNode buildTreeChild(int[] inorder,int rt,int[] postorder,int inbegin,int inend){//如果没有孩子结点,返回nullif(inbegin>inend){return null;}//创建根结点TreeNode root=new TreeNode(postorder[rt]);//往前遍历后序数组的下标--postindex--;//找到该根结点在中序数组的下标int rootindex=findval(inorder,inbegin,inend,postorder[rt]);//创建右孩子这颗树的根root.right=buildTreeChild(inorder,postindex,postorder,rootindex+1,inend);//创建左孩子这棵树的根root.left=buildTreeChild(inorder,postindex,postorder,inbegin,rootindex-1);//返回根节点return root;}//输入中序数组和后序数组创建二叉树public TreeNode buildTree(int[] inorder, int[] postorder) {postindex=postorder.length-1;return buildTreeChild(inorder,postindex,postorder,0,postindex);}
}

6.

思路:

先理解题目意思,就是根结点直接拼接到字符串,然后按照前序遍历的顺序,有孩子就给()包着,孩子里面套孩子,如果有左孩子但没右孩子可以省略右孩子的(),如果没有左孩子但有右孩子,要用()标记左孩子,代表左孩子为null,如果没左右孩子,就拼接),然后返回

如上图,步骤为:

1.按照前序遍历顺序,根结点直接拼接到字符串,现在字符串为"1"

2.“1”这个结点有左孩子,拼接"(",然后递归到“2”这个结点,现在字符串为"1("

3.到“2”这个结点,将结点的值拼接,拼接"2",“2”这个结点有左孩子,拼接"(",然后递归到“4”这个结点,现在字符串为"1(2("

4.到“4”这个结点,将结点的值拼接,拼接"4",“4”这个结点没有左右孩子,用")"包住4返回,现在字符串为"1(2(4)"

5.回到“2”这个结点,2没有右孩子,"()"可以省略,用")"包住2返回,现在字符串为"1(2(4))"

6.回到“1”这个结点,1有右孩子,然后递归到“3”这个结点,现在字符串为"1(2(4))("

7.到“3”这个结点,将结点的值拼接,拼接"3",“3”这个结点没有左右孩子,用")"包住3返回,现在字符串为"1(2(4))(3)"

8.根结点“1”的左右孩子遍历完了,返回字符串即可

注:本题底下有提示,不为空树

class Solution {//因为用String的不可变性,+=效率太低了所以可以用StringBuffer或者StringBuilderStringBuffer str=new StringBuffer();public String tree2str(TreeNode root) {//添加值str.append(root.val);//如果有左孩子if(root.left!=null){str.append("(");tree2str(root.left);str.append(")");//有左无右if(root.right==null){//无事发生str.append("");}else{//有左有右str.append("(");tree2str(root.right);str.append(")");}}else{//如果没有左孩子if(root.right==null){//无左无右//无事发生str.append("");}else{//无左有右str.append("()");//记录左孩子为nullstr.append("(");tree2str(root.right);str.append(")");}}return str.toString();//转换为String类型返回}
}

http://www.ppmy.cn/devtools/104780.html

相关文章

Qt中的各种“q+基本数据类型“

前言 虽说Qt支持C的数据类型&#xff0c;但是还是用Qt自己又封装的数据类型比较好。你在支持能有我原生的支持&#xff1f; 正文 先看qint系列 有qint8,quint8,qint16,quint16,qint32,quint32,qint64,quint64 源码如下 解读 1. typedef signed char qint8; 说明: 定义…

01.项目初始化

截至目前时间 2024.8.28&#xff0c;参考官方文档创建项目&#xff0c;这里只记录当前版本的创建过程&#xff0c;如果版本出入较大&#xff0c;还请参考官方最新文档。 官网地址&#xff1a;https://cn.vuejs.org/guide/quick-start.html 1. 创建项目 npm create vuelatest这…

mysql 修改用户密码

在 MySQL 中修改用户的密码可以通过几种不同的方法来实现。这里提供两种常见的方法&#xff1a; 方法一&#xff1a;使用 SET PASSWORD 语句 这是最直接的方法&#xff0c;需要具有足够的权限&#xff08;如 root 用户&#xff09;来执行此操作&#xff1a; FLUSH PRIVILEGE…

深度学习100问15:什么是交叉熵误差

嘿&#xff0c;交叉熵误差就像是一个“挑剔的裁判”。 一、定义及原理 想象一下&#xff0c;你在玩一个猜概率的游戏。比如说有个神秘盒子&#xff0c;里面可能有个红球或者蓝球&#xff0c;你要猜猜红球 出现的概率是多少。真实的情况呢&#xff0c;就像是有个“标准答案…

uniapp组件中的emit声明触发事件

emit解析 在 uniapp 中&#xff0c;emit 主要用于组件间通信&#xff0c;特别是在子组件需要向父组件或者其他组件发送消息的时候。具体用途包括&#xff1a; 子传父数据&#xff1a;子组件通过 $emit 触发一个事件&#xff0c;并携带参数&#xff0c;父组件监听这个事件并对参…

Redis的内存淘汰策略- allkeys-lru

allkeys-lru 策略简介 在 allkeys-lru 策略下&#xff0c;当 Redis 的内存使用达到设置的上限&#xff08;maxmemory&#xff09;时&#xff0c;它会根据 LRU 算法选择和删除那些最近最少使用的键。LRU 算法会记录每个键的最近访问时间&#xff0c;当内存不足时&#xff0c;Re…

出现 WebServerException: Unable to start embedded Tomcat 解决方法(全)

目录 1. 问题所示2. 原理分析3. 解决方法4. 彩蛋总结4.1 方式一4.2 方式二4.3 方式三4.4 方式四1. 问题所示 原本今天早上可以执行,但是突然下午执行springboot项目的时候出现如下问题 Caused by: org.springframework.boot.web.server.WebServerException: Unable to start…

SpringBoot 消息队列RabbitMQ Work模型 绑定多个消费者 负载均衡 消息处理速度

介绍 RabbitMQ 会将消息轮询地分发给所有绑定的消费者。多个消费者能够并发处理消息&#xff0c;提高了处理效率和系统的鲁棒性。 多个消费者 如果有50条消息将会 a1 a2 a1 a2 的方式进行去轮询消费 RabbitListener(queues "insert.queue") ///insert.queue 为监听…