算法体系-11 第十一节:二叉树基本算法(上)

一 两链表相交

1.1 题目描述

给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null

【要求】

如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。

1.2 判断一个单链表是否有环

情况 :假如一个单链表,给你一个头节点,如果有环的话返回第一个入环的节点,如果无环的话返回null?

1.2.1 方案一 容器的办法 Set<>集合

1.2.1.1 假如链表无环,走到null的时候,在hashset里面都没有找到重复的

假如链表是有环的,遍历链表的判断当前链表每个节点,判断当前节点是否在hashset里面,如果在就说明有环,如果不在将该节点放入set里面,如果遍历到最后一个null后m都没找到有在hash里面的节点说明无环

1.2.1.2 代码
//快慢指针public boolean hasCycle(ListNode head) {if (head == null) {return false;}ListNode slow = head;ListNode fast = head.next;while (slow != fast) {if (fast == null || fast.next == null) {return false;}slow = slow.next;fast = fast.next.next;}return true;}
}
/*** 通过Set集合记录值的方式,如果有重复的数据,就代表有环* @param node* @return*/private boolean hasCycle2(Node node) {Set<Node> nodeSet = new HashSet<>();//此字段仅用来记录遍历次数int traverseCount = 0;while (node != null) {if (nodeSet.contains(node)) {Log.d(TAG, "hasCycle2==>有环...traverseCount="+traverseCount);return true;}traverseCount ++;Log.d(TAG, "hasCycle2==>traverseCount="+traverseCount);nodeSet.add(node);node = node.next;}Log.d(TAG, "hasCycle2==>无环");return false;}
1.2.1.3 总结

链表的环可能有很多,但单链表只有一个next指针,不会出现分叉的情况

1.2.2 使用快慢指针遍历链表

思路:使用快慢指针遍历链表,快指针一旦走到

null,则该单链表一定无环(快指针一次走两步),如果存在环的话,快指针与慢指针一定会在环上相遇。

当快慢指针相遇时,让快指针重新指向初始节点;慢指针原地不变;两个指针都每次走一步,一定会在链表的入环节点相遇

1.2.2.1 分析

情况一 如果快指针走到null了说明无环

如下情况图解找到相遇的点,但不是相交的入口节点,再做图二的解析,当相遇的时候,快指针去到链表的头,慢指针留在原地继续走,这个时候快慢指针都只走一步,这样再次往前走的话他两一定会在入口节点相遇,这个相遇的点就为入口的交点

1.2.2.2 代码
public static class Node {public int value;  public Node next;    public Node(int data) {this.value = data;   }
}
// 找到链表第一个入环节点,如果无环,返回nullpublic static Node getLoopNode(Node head) {if (head == null || head.next == null || head.next.next == null) {return null;}// n1 慢  n2 快Node slow = head.next; // n1 -> slowNode fast = head.next.next; // n2 -> fastwhile (slow != fast) {if (fast.next == null || fast.next.next == null) {return null;}fast = fast.next.next;slow = slow.next;}// slow fast  相遇fast = head; // n2 -> walk again from headwhile (slow != fast) {slow = slow.next;fast = fast.next;}return slow;}

1.2.2.3 总结

快慢指针找链表入环的节点

1、找到相遇点

2、快指针去到链表头再都分别往前走就能找到

1.3 本题分析

情况一 两个链表都无环的情况-存在相交,

情况二 如果两个链表一个无环,一个有环-不存在相交,因为相交后只存在一个next,只有一个有环需要两个next

情况三 两个都有环 的情况,这个环在相交节点后成一个环

情况一

情况二

情况三两个链表有环的情况

1.3.1 情况一 两个链表都无环的情况-存在相交,

1.3.1.1 hashset

先判断两个链表都没环的情况,先通过上面的方法判断每个链表是否有环,再利用hashset,先将一个链表放入hashset里面,再遍历宁一个链表是否有节点在haseset里面

1.3.1.2 二不使用hashset

分析

先都让两个无环的链表走到最后,看是最后一个节点的内存地址是否相等,不相等一定不相交,end1==end2那么他们是一定相交的,end1和end2是他们相交的最后一个节点,要求的是第一个相交的节点,让长的一个走先他多出来的节点,再让短的和他们一起走,当有相等的时候那么这个点就是他们相交的第一个节点;

代码

// 如果两个链表都无环,返回第一个相交节点,如果不想交,返回nullpublic static Node noLoop(Node head1, Node head2) {if (head1 == null || head2 == null) {return null;}Node cur1 = head1;Node cur2 = head2;int n = 0;while (cur1.next != null) {n++;cur1 = cur1.next;}while (cur2.next != null) {n--;cur2 = cur2.next;}//判断是否相交if (cur1 != cur2) {return null;}// n  :  链表1长度减去链表2长度的值cur1 = n > 0 ? head1 : head2; // 谁长,谁的头变成cur1cur2 = cur1 == head1 ? head2 : head1; // 谁短,谁的头变成cur2n = Math.abs(n);while (n != 0) {n--;cur1 = cur1.next;}while (cur1 != cur2) {cur1 = cur1.next;cur2 = cur2.next;}return cur1;}

1.3.2 情况二

分析 如果两个链表一个无环,一个有环-不存在相交,因为相交后只存在一个next,要一个有环需要两个next

1.3.3 情况三

情况三 两个都有环 的情况,这个环在相交节点后成一个环

当loop1 和loop2相等的化就是情况三的第二种

当loop1 和loop2不相等的化就是情况三的第一种和第三种

1.3.3.1 loop1 == loop2

当loop1 和loop2相等的化、话就是情况三的第二种,求第一个交点

分析 当loop1 和loop2终止点时,就和情况一中的求无环链表的第一个交点一样

1.3.3.2 loop1 != loop2

当loop1 和loop2不相等的化就是情况三的第一种和第二种

分析 loop1环节点开始,我转一圈的过程中如果越到loop2说明是情况三, loop1 和 loop2都是他两的相交节点

没有越到说明是情况1,没有相交节点情况1返回null

1.3.3 代码
// 两个有环链表,返回第一个相交节点,如果不想交返回nullpublic static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {Node cur1 = null;Node cur2 = null;if (loop1 == loop2) {cur1 = head1;cur2 = head2;int n = 0;while (cur1 != loop1) {n++;cur1 = cur1.next;}while (cur2 != loop2) {n--;cur2 = cur2.next;}cur1 = n > 0 ? head1 : head2;cur2 = cur1 == head1 ? head2 : head1;n = Math.abs(n);while (n != 0) {n--;cur1 = cur1.next;}while (cur1 != cur2) {cur1 = cur1.next;cur2 = cur2.next;}return cur1;} else {cur1 = loop1.next;while (cur1 != loop1) {if (cur1 == loop2) {return loop1;}cur1 = cur1.next;}return null;}}
1.3.4 主函数调用
public static Node getIntersectNode(Node head1, Node head2) {if (head1 == null || head2 == null) {return null;    }Node loop1 = getLoopNode(head1);   Node loop2 = getLoopNode(head2);   if (loop1 == null && loop2 == null) {return noLoop(head1, head2);   }if (loop1 != null && loop2 != null) {return bothLoop(head1, loop1, head2, loop2);   }return null;}

二 二叉树的先序、中序、后序遍历

2.1 递归实现

2.1.1 前中后遍历的讲解

先序 中序 后序 都可以由以下函数改出来,把打印放在第一次,第二次,第三次就是对应的前中后遍历;

递归序

充分理解递归点过程,根据递归的实现原理,        

2.1.2 代码

package class10;public class Code02_RecursiveTraversalBT {public static class Node {public int value;public Node left;public Node right;public Node(int v) {value = v;}}public static void f(Node head) {if (head == null) {return;}// 1f(head.left);// 2f(head.right);// 3}// 先序打印所有节点public static void pre(Node head) {if (head == null) {return;}System.out.println(head.value);pre(head.left);pre(head.right);}public static void in(Node head) {if (head == null) {return;}in(head.left);System.out.println(head.value);in(head.right);}public static void pos(Node head) {if (head == null) {return;}pos(head.left);pos(head.right);System.out.println(head.value);}public static void main(String[] args) {Node head = new Node(1);head.left = new Node(2);head.right = new Node(3);head.left.left = new Node(4);head.left.right = new Node(5);head.right.left = new Node(6);head.right.right = new Node(7);pre(head);System.out.println("========");in(head);System.out.println("========");pos(head);System.out.println("========");}}

2.2 非递归实现-

2.2.1 前序遍历

2.2.1.1 分析
2.2.1.2 代码
public static class Node {public int value;public Node left;public Node right;public Node(int v) {value = v;}}public static void pre(Node head) {System.out.print("pre-order: ");if (head != null) {Stack<Node> stack = new Stack<Node>();stack.add(head);while (!stack.isEmpty()) {head = stack.pop();System.out.print(head.value + " ");if (head.right != null) {stack.push(head.right);}if (head.left != null) {stack.push(head.left);}}}System.out.println();}

2.2.2 中序遍历


2.2.2.1 分析

2.2.2.2 代码
public static void in(Node cur) {System.out.print("in-order: ");if (cur != null) {Stack<Node> stack = new Stack<Node>();//cur == null 还需要继续保证往下,通过!stack.isEmpty()while (!stack.isEmpty() || cur != null) {if (cur != null) {stack.push(cur);cur = cur.left;//1、假如上面的最后一个节点 cur = d.left;} else {//2、cur == null ,弹出  cur = stack.pop(); 为d,//3、找cur = cur.right;还是空,还是来到这个循环,//4、弹出的就是b了,cur = stack.pop();为e了继续往下走//5、e的左右都为空再弹出的就是a了cur = stack.pop();System.out.print(cur.value + " ");cur = cur.right;}}}System.out.println();}

2.2.3 后续遍历

2.2.3.1 在先序遍历的基础上进行修改, 先改出一个头右左去压栈(压栈的时候先左再右,弹出来就对了),从新栈出来就是左右头就是后续遍历

先在压栈头左右基础上改出一个压栈为头右左的形式,弹出的时候不立马打印,而是先压入一个栈里面最后再弹出就是后续遍历

左右头

2.2.3.2 代码
public static void pos1(Node head) {System.out.print("pos-order: ");if (head != null) {Stack<Node> s1 = new Stack<Node>();Stack<Node> s2 = new Stack<Node>();s1.push(head);while (!s1.isEmpty()) {head = s1.pop(); // 头 右 左s2.push(head);if (head.left != null) {s1.push(head.left);}if (head.right != null) {s1.push(head.right);}}// 左 右 头while (!s2.isEmpty()) {System.out.print(s2.pop().value + " ");}}System.out.println();}

扩展见代码 用一个栈来实现

三 附加题 X 祖先节点 交集 后边再看

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

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

相关文章

强化PaaS平台应用安全:关键策略与措施

PaaS&#xff08;平台即服务&#xff0c;Platform-as-a-Service&#xff09;是一种云计算服务模式&#xff0c;可以为客户提供一个完整的云平台&#xff08;硬件、软件和基础架构&#xff09;以用于快捷开发、运行和管理项目&#xff0c;从而降低了企业云计算应用的高成本和复杂…

.NET高级面试指南专题十七【 策略模式模式介绍,允许在运行时选择算法的行为】

介绍&#xff1a; 策略模式是一种行为设计模式&#xff0c;它允许在运行时选择算法的行为。它定义了一系列算法&#xff0c;将每个算法封装到一个对象中&#xff0c;并使它们可以互相替换。这使得算法可独立于使用它的客户端变化。 原理&#xff1a; 策略接口&#xff08;Strat…

源码部署LAMP架构

LAMP 文章目录 LAMP1. lamp简介2. web服务器工作流程2.1 cgi与fastcgi2.2 httpd与php结合的方式2.3 web工作流程 3. LAMP平台构建3.1 安装httpd3.2 安装mysql3.3 安装php3.4 验证 1. lamp简介 有了前面学习的知识的铺垫&#xff0c;今天可以来学习下第一个常用的web架构了。 …

软件工程-第三版王立福-第1章 绪论

本书结合IEEE最新发布的软件工程体系SWEBOK&#xff0c;和IEEE/ACM软件工程学科小组公布的软件工程教育知识体系SEEK&#xff0c;北大本科生指定教材。注重基础知识的系统性&#xff0c;选材的先进性及知识的应用。2009年出版 软件开发本质的认识&#xff0c;两大技术问题&…

自存vue3 ts jump start-3 computed

computed是一个方法&#xff0c;参数可以接收一个函数&#xff0c;最终返回需要的。获取最新的因数据改变而得到的数据。

《汽车数据安全若干问题合规实践指南》正式发布(百度盘下载)

指南针对汽车数据安全的重要合规内容&#xff0c;结合汽车行业特有场景&#xff0c;参考行业最佳实践&#xff0c;提出合规实践建议。指南旨在进一步提高汽车行业数据安全保护水平&#xff0c;增强汽车企业数据安全合规保障能力&#xff0c;推动汽车数据价值安全使用&#xff0…

京津冀太阳能光伏展

京津冀太阳能光伏展是一个专门展示和推广太阳能光伏技术和产品的展览活动。该展览主要面向京津冀地区的企业、科研院校、政府机构以及普通市民&#xff0c;旨在促进太阳能光伏在京津冀地区的推广应用&#xff0c;推动清洁能源的发展。 在京津冀太阳能光伏展上&#xff0c;参展的…

Zerotier 异地组网方案初探

前言 我之前想要异地组网的话&#xff0c;一般都采用内网穿透的方法&#xff0c;但是这个内网穿透有弊端就是都是要通过公网服务器转发流量&#xff0c;对于大流量的传输就比较不方便&#xff0c;我发现了Zerotier 这个工具非常的好用&#xff0c;是基于p2p的 这是一个类似于…

高效使用 JMeter 生成随机数:探索 Random 和 UUID 算法

在压力测试中&#xff0c;经常需要生成随机值来模拟用户行为。JMeter 提供了多种方式来生成随机值&#xff0c;本文来具体介绍一下。 随机数函数 JMeter 提供了多个用于生成随机数的函数&#xff0c;其中最常用的是__Random函数。该函数可以生成一个指定范围内的随机整数或浮…

【哈希表】算法例题

目录 五、哈希表 39. 赎金信 ① 40. 同构字符串 ① 41. 单词规律 ① 42. 有效的字母异位词 ① 43. 字母异位词分组 ② 44. 两数之和 ① 45. 快乐数 ① 46. 存在重复元素 ① 47. 最长连续序列 ② 五、哈希表 39. 赎金信 ① 给你两个字符串&#xff1a;ransomNote 和 m…

Linux入门-常见指令及权限理解

目录 1、Linux背景 1.1、发展历史 1.2、开源 1.3Linux企业应用现状 2、Linux下的基本命令 2.1、ls 指令 2.2、pwd 命令 2.3、cd 命令 2.4、touch命令 2.5、mkdir 命令 2.6、rmdir 指令和 rm指令 2.7 man 指令 2.8、cp指令 2.9、mv 指令 2.10 cat 2.11 more 2…

网络学习:IPV6基础配置

目录 一、配置接口的全球单播地址 二、配置接口本地链路地址 三、配置接口任播地址 四、配置接口PMTU 配置静态PMTU&#xff1a; 配置动态PMTU&#xff1a; 五、接口配置IPV6地址示例&#xff1a; 一、配置接口的全球单播地址 全球单播地址类似于IPv4公网地址&#xff0…

个人微信开发API

安卓微信的api&#xff0c;个人微信开发API协议&#xff0c;微信 ipad &#xff0c;微信ipad协议&#xff0c;微信web版接口api&#xff0c;微信网页版接口&#xff0c;微信开发sdk&#xff0c;微信开发API&#xff0c;微信协议&#xff0c;微信接口文档&#xff0c;网页个人微…

FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+HLS图像缩放+多路视频拼接,提供4套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博主所有FPGA工程项目-->汇总目录本博已有的 SDI 编解码方案本方案的SDI接收发送本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收OSD动态字符叠加输出应用本方案的SDI接收HLS…

docker小白第十四天之Portainer与CIG

Portainer简介 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 Portainer命令安装 # 一个容器可以同时起多个-p端口&#xff0c;restartalways表示随时在线&#xff0c;重启机器后也…

麒麟系统Redis7.2哨兵集群部署

redis哨兵集群部署 1、原理 Redis 哨兵模式是指在 Redis 集群中,有一组专门的进程(即哨兵进程)负责监控主节点和从节点的状态,并在发现故障时自动进行故障转移,以保证 Redis 集群的高可用性。 Redis 提供了哨兵的命令,哨兵命令是一个独立的进程,哨兵进程会周期性地向主…

Flutter开发入门——路由

什么是路由&#xff1f; 移动端应用开发中&#xff0c;路由技术是一个非常重要的组成部分。路由技术负责管理应用中各个页面之间的跳转、导航以及参数传递等关键功能。在移动端应用中&#xff0c;一个高效、易于维护的路由系统对于提高开发效率和用户体验具有重要意义。 Flut…

MySQL 多表查询与事务的操作

一,多表联查 有些数据我们已经拆分成多个表,他们之间通过外键进行连接.当我们要查询两个表的数据,各取其中的一列或者多列. 这时候就需要使用多表联查. 数据准备: # 创建部门表 create table dept(id int primary key auto_increment,name varchar(20) ) insert into dept (n…

【机器学习系列】M3DM工业缺陷检测部署与训练

一.基础资料 1.Git 地址 地址 2.issues issues 3.参考 参考 csdn 二.服务器信息 1.GPU 服务器 GPU 服务器自带 CUDA 安装(前提是需要勾选上)CUDA 需要选择大于 11.3 的版本登录服务器后会自动安装 GPU 驱动 2.CUDA 安装 GPU 服务器自带 CUDA CUDA 版本查看 3.登录信…

软件杯 深度学习 python opencv 实现人脸年龄性别识别

文章目录 0 前言1 项目课题介绍2 关键技术2.1 卷积神经网络2.2 卷积层2.3 池化层2.4 激活函数&#xff1a;2.5 全连接层 3 使用tensorflow中keras模块实现卷积神经网络4 Keras介绍4.1 Keras深度学习模型4.2 Keras中重要的预定义对象4.3 Keras的网络层构造 5 数据集处理训练5.1 …