数据结构与算法:二叉树(寻找最近公共祖先、寻找后继节点、序列化和反序列化、折纸问题的板子和相关力扣题目)

最近公共祖先

第一版(前提:p和q默认存在于这棵树中)

可以层序遍历每个节点时用个HashMap存储该结点和其直接父节点的信息。然后从p开始溯源,将所有的父节点都添加到一个HashSet集合里。然后从q开始溯源,每溯源一步看是否在set集合中,在的话就返回。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {HashMap<TreeNode, TreeNode> fatherMap = new HashMap<>();//充当父指针的作用,第一个Node的父亲是第二个NodefatherMap.put(root,root);getFather(root, fatherMap);HashSet<TreeNode> set1 = new HashSet<>();//存放从p开始向上溯源的一系列父节点(包括p自己)TreeNode cur = p;while(fatherMap.get(cur)!=cur){//没有溯源到root节点set1.add(cur);cur = fatherMap.get(cur);}set1.add(root);//最后的root节点也要加上cur = q;while(!set1.contains(cur)) cur = fatherMap.get(cur);//从q开始向上溯源,每溯源一步,就检查是否在set1中//最后得到的cur就是最近公共祖先return cur;}//帮每个节点找父节点的过程public void getFather(TreeNode root,HashMap<TreeNode, TreeNode> fatherMap){if(root==null) return;fatherMap.put(root.left, root);fatherMap.put(root.right, root);getFather(root.left, fatherMap);getFather(root.right, fatherMap);}

可以看到效率很低,12ms,击败7.89%使用 Java 的用户。

第二版(前提:p和q默认存在于这棵树中)

我们可以分析p和q的最近公共祖先的情况,其实总共就两种。

  1. p和q其中有一方是对方的最近公共祖先。最简单的情况如图2。
  2. p和q的最近公共祖先是第三个节点。最简单的情况如图1。
    在这里插入图片描述
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root==null||root==p||root==q) return root;//意味着返回的子树中如果有p或者q,那么才会返回p或者q//如果没有p或者q,那么只会返回空TreeNode left = lowestCommonAncestor(root.left, p, q);TreeNode right = lowestCommonAncestor(root.right, p, q);if(left!=null&&right!=null) return root;//如果左节点和右节点都不为空,说明左子树有p或q其中之一。右子树有p或q其中之一。那么此时就应该返回它俩的父节点return left!=null?left:right;//如果左节点或右节点中有一个为空。那么返回不空的那一个。如果两个都为空,那么返回空节点。}

这部分有点抽象,不好讲,可以去看b站左程云的视频p6:https://www.bilibili.com/video/BV13g41157hK
最终效率是6ms,击败65.96%使用 Java 的用户

相关题目

LeetCode LCR 164.二叉树的最近公共祖先
LeetCode LCR 193.二叉搜索树的最近公共祖先
LeetCode 236.二叉树的最近公共祖先
LeetCode 235.二叉搜索树的最近公共祖先
(这四道题都是一模一样的题面……)

LeetCode 1650.二叉树的最近公共祖先Ⅲ
LeetCode 1644. 二叉树的最近公共祖先 II
(以上题目的题解可以见本人另一篇博客)

LeetCode 1676. 二叉树的最近公共祖先 IV

寻找后继结点

二叉树的结构包含了父节点指针。头节点的父节点指针指向空。
现在只给某个存在于二叉树的节点node,返回node的节点。
且node和其后继结点之间的路径长度为k的话,时间复杂度为O(k)
在这里插入图片描述
由图可知,一颗树的中序遍历的顺序是从左上到右下的。那么一个节点的中序后继节点只会分为两种情况。第一,其中序后继和节点本身在一条线上,如节点2和节点3,节点4和节点5。那么也就是说我们该从这个节点出发,找节点的右子树的最左节点。
第二,其中序后继和节点本身不在一条线上,如节点5和节点6.那么也就是说我们该从这个节点出发,找节点的父树的最右节点。
而且注意,因为中序遍历是从左上到右下,所以应该优先去找节点的右子树的最左节点。
综合一句话就是,找这个节点的右子树的最左节点或者父树的最右节点。

第一版(二叉树结构包含父节点指针的)

public Node inorderSuccessor(Node node){if(node==null) return null;if(node.right!=null){Node cur = node.right;while(cur.left!=null){cur = cur.left;}return cur;}else{Node cur = node;Node curFather = node.parent;while(curFather!=null&&curFather.left!=cur){cur = cur.parent;curFather = cur.parent;}return curFather;}}

第二版(二叉树结构不包含父节点指针的)

class Solution {public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {if(root==null) return null;if(p.right!=null){//存在右子树TreeNode cur = p.right;while(cur.left!=null){cur = cur.left;}return cur;}else{//去找左父树HashMap<TreeNode,TreeNode> fatherMap = new HashMap<>();fatherMap.put(root, root);findFather(root, fatherMap);TreeNode cur = p;//6TreeNode curFather = fatherMap.get(p);//5while(curFather.left!=cur){if(cur!=root){cur = fatherMap.get(cur);//5curFather = fatherMap.get(cur);}else{curFather=null;break;}}return curFather;}}public void findFather(TreeNode root,HashMap<TreeNode,TreeNode> fatherMap){if(root==null) return;fatherMap.put(root.left, root);fatherMap.put(root.right, root);findFather(root.left, fatherMap);findFather(root.right, fatherMap);}
}

注意点

注意第一版代码和第二版代码的while循环不太一样。

//第一版的while循环
Node cur = node;
Node curFather = node.parent;while(curFather!=null&&curFather.left!=cur){cur = cur.parent;curFather = cur.parent;}return curFather;//第二版的while循环TreeNode cur = p;TreeNode curFather = fatherMap.get(p);while(curFather.left!=cur){if(cur!=root){cur = fatherMap.get(cur);curFather = fatherMap.get(cur);}else{curFather=null;break;}}return curFather;

因为第一版代码直接包含了父节点指针,所以如果对于如下情况,求6的后继结点,实际上是不存在。
在这里插入图片描述
那么其curFather是可以直接遍历到5的父节点,空节点的
但是如果第二版代码也改成了

TreeNode cur = p;//6
TreeNode curFather = fatherMap.get(p);//5
while(curFather!=null&&curFather.left!=cur){cur = fatherMap.get(cur);//5curFather = fatherMap.get(cur);
}
return curFather;

因为HashMap中是无法存储空节点的,就会导致curFather是遍历不到5的父节点,也就是空节点,从而超出时间限制。

相关题目

LeetCode 面试题04.06 后继者
LeetCode 285.二叉搜索树中的中序后继
LeetCode LCR 053.二叉搜索树中的中序后继
(以上三题都是一模一样的题面,都是二叉树结构中没有父节点指针的)

LeetCode 510.二叉搜索树中的中序后继Ⅱ
(二叉树结构中有父节点指针的)

二叉树的序列化和反序列化

就是内存里的一颗树如何变成唯一的字符串形式,又如何从字符串形式变成树的过程。
这里的话以先序遍历来做序列化,遇到空节点就用“#”代替,每个节点之后都以“_”作为结尾。

注意因为保存了空节点的信息,所以只需要先序遍历本身就能确定树的唯一结构。
如果没有保存空节点的信息,那么就需要先序遍历+中序遍历才能确定树的唯一结构。

public class Codec {public String serialize(TreeNode root) {if(root==null) return "#_";String res = root.val+"_";res+=serialize(root.left);res+=serialize(root.right);return res;}// Decodes your encoded data to tree.public TreeNode deserialize(String data) {String[] values = data.split("_");Queue<String> queue = new LinkedList<>();int size = values.length;for(int i=0;i<size;i++){queue.add(values[i]);}return recodeByPreOrder(queue);}public TreeNode recodeByPreOrder(Queue<String> queue){String value = queue.poll();if(value.equals("#")) return null;TreeNode head = new TreeNode(Integer.valueOf(value));head.left = recodeByPreOrder(queue);head.right = recodeByPreOrder(queue);return head;}
}

相关题目

LeetCode 297.二叉树的序列化与反序列化
LeetCode 449.序列化和反序列化二叉搜索树
LeetCode LCR 048.二叉树的序列化与反序列化
LeetCode LCR 156.序列化与反序列化二叉树
(以上四题都是同一个题面)

LeetCode 428.序列化和反序列化N叉树

折纸问题

把纸条竖着放在桌⼦上,然后从纸条的下边向上方对折,压出折痕后再展开。此时有1条折痕,突起的方向指向纸条的背面,这条折痕叫做“下”折痕 ;突起的⽅向指向纸条正面的折痕叫做“上”折痕。如果每次都从下边向上方对折,对折N次。请从上到下计算出所有折痕的方向,且时间复杂度和空间复杂度就为O(N)

实际上就是满二叉树的中序遍历。

    //i是当前递归到的层数,N是一共的层数,down==true意味着凹,否则为凸public static void printProcess(int i,int N,boolean down){if(i>N) return;printProcess(i+1,N,true);System.out.println(down?"凹":"凸");printProcess(i+1,N,false);}

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

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

相关文章

数据库数据加密的 4 种常见思路的对比

应用层加解密方案数据库前置处理方案磁盘存取环节&#xff1a;透明数据加密DB 后置处理 最近由于工作需要&#xff0c;我对欧洲的通用数据保护条例做了调研和学习&#xff0c;其中有非常重要的一点&#xff0c;也是常识性的一条&#xff0c;就是需要对用户的个人隐私数据做好加…

webpack实际实践优化项目

参考&#xff1a; 如何通过性能优化&#xff0c;将包的体积压缩了62.7% 雅虎35条 20210526-webpack深入学习&#xff0c;搭建和优化react项目 本文只专注于性能优化的这个部分。 总体来说分为两个方面&#xff1a;第一是开发环境中主要优化打包速度&#xff0c;第二是线上环境…

BIG DATA —— 大数据时代

大数据时代 [英] 维克托 迈尔 — 舍恩伯格 肯尼斯 库克耶 ◎ 著 盛杨燕 周涛◎译 《大数据时代》是国外大数据研究的先河之作&#xff0c;本书作者维克托迈尔舍恩伯格被誉为“大数据商业应用第一人”&#xff0c;他在书中前瞻性地指出&#xff0c;大数据带来的信息…

python-自动化篇-运维-网络-IP

文章目录 IP自我介绍IPy安装模块windowsLinux IPy介绍支持大多数 IP 地址格式IPv4 地址IPv6 地址网络掩码和前缀 派生网络地址将地址转换为字符串使用多个网络多网络计算方法 IP自我介绍 IP地址规划是网络设计中非常重要的一个环节&#xff0c;规划的好坏会直接影响路由协议算…

2024年【天津市安全员B证】考试技巧及天津市安全员B证复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年天津市安全员B证考试技巧为正在备考天津市安全员B证操作证的学员准备的理论考试专题&#xff0c;每个月更新的天津市安全员B证复审模拟考试祝您顺利通过天津市安全员B证考试。 1、【多选题】《建设行政处罚决定…

力扣刷题之旅:进阶篇(六)—— 图论与最短路径问题

力扣&#xff08;LeetCode&#xff09;是一个在线编程平台&#xff0c;主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目&#xff0c;以及它们的解题代码。 --点击进入刷题地址 引言 在算法的广阔天地中&#xff0c;图论是一个非常重要的领域。…

《Go 简易速速上手小册》第8章:网络编程(2024 最新版)

文章目录 8.1 HTTP 客户端与服务端编程 - Go 语言的网络灯塔与探航船8.1.1 基础知识讲解服务端编程客户端编程 8.1.2 重点案例&#xff1a;简易博客服务服务端实现客户端实现运行示例 8.1.3 拓展案例 1&#xff1a;增加文章评论功能功能描述服务端实现客户端实现 8.1.4 拓展案例…

MATLAB导出图程序

本文将以代码的形式快速介绍MATLAB导出图到Paper 1 从simulation导出数 2 与simulation同源文件夹下创建导图m文件 代码如下&#xff1a; % 实验后的数据处理用 M-文件 % clear all % 清空工作空间 % close all      % 关闭所有图形窗口 % load adp.mat …

二叉树前序中序后序遍历(非递归)

大家好&#xff0c;又和大家见面啦&#xff01;今天我们一起去看一下二叉树的前序中序后序的遍历&#xff0c;相信这个对大家来说是信手拈来&#xff0c;但是&#xff0c;今天我们并不是使用常见的递归方式来解题&#xff0c;我们采用迭代方式解答。我们先看第一道前序遍历 1…

把Activity当做dialog对话框使用

1、引言 在安卓开发中对话框的使用是不可避免的&#xff0c;但是原生的对话框用起来总感觉差点意思&#xff0c;而且位置不好控制&#xff0c;在与界面的交互上也不够灵活&#xff0c;没有像activity那样的生命周期方法&#xff0c;以至于某些特殊的功能无法实现。此时我们就希…

MySQL 基础知识(八)之用户权限管理

目录 1 MySQL 权限管理概念 2 用户管理 2.1 创建用户 2.2 查看当前登录用户 2.3 修改用户名 2.4 删除用户 3 授予权限 3.1 授予用户管理员权限 3.2 授予用户数据库权限 3.3 授予用户表权限 3.4 授予用户列权限 4 查询权限 5 回收权限 1 MySQL 权限管理概念 关于 M…

太以假乱真了,大家小心

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…