判断两个链表是否相交
题目描述:
判断两个链表(可能有环可能无环)之间是否有相交(此处相交指链表经过了同一个地址的节点(与节点的值无关)) 。如果相交,则返回相交的第一个节点。否则,返回null
前置知识点:判断链表是否有环,并找出第一个成环节点:
法1:使用HashSet的方法来判断是否经过了重复节点
代码及解析:
1 public static Node getLoopList1(MyLink list) { 2 Node p = list.head; 3 HashSet<Node> set = new HashSet<>();//利用HashSet中元素不可重复的特性 4 while (p != null) { 5 if (set.contains(p) == false) { 6 set.add(p); 7 } 8 else {//出现重复节点,说明有环结构 9 return p; 10 } 11 p = p.next; 12 } 13 return null; 14 }
法2:使用快慢指针的方法来判断
代码及解析:
1 public static Node getLoopList2(MyLink list) { 2 //因为快指针第一次要走两步,为了避免越界,先讨论掉这些情况 3 if (list.head == null || list.head.next == null || list.head.next.next == null) { 4 return null; 5 } 6 //建立一个快慢指针 7 Node fast = list.head, slow = list.head; 8 boolean isChainList = false; 9 while (fast.next != null && fast != null) {//如果有环,快慢指针一定会相遇 10 fast = fast.next.next; 11 slow = slow.next; 12 if (slow == fast) { 13 isChainList = true; 14 break; 15 } 16 } 17 if (isChainList == false) { 18 return null; 19 } 20 //将快指针移回起点,慢指针原地不动,两者一次移动一格,则相遇的点为环的起点(证明过程复杂) 21 fast = list.head; 22 while (fast != slow) { 23 fast = fast.next; 24 slow = slow.next; 25 } 26 return fast; 27 }
在前置知识点的基础上,接下来来解决这个问题
分三类情况讨论:
1.两个链表都是无环的:
思路:分别得到两个链表的长度差值,然后让长的那条链表指针走差值步后,让两个链表的指针同时向右走,先判断尾节点是否相同,如果相同则有相交节点,防止则无相交节点
当两个节点指向同一个节点时,则返回这个所要的节点
代码及解析:
1 public static Node noLoop(MyLink list1,MyLink list2) { 2 int n = 0;//获得两链表长度的差值 3 Node cur1 = list1.head, cur2 = list2.head; 4 while (cur1.next != null) { 5 n++; 6 cur1 = cur1.next; 7 } 8 while (cur2.next != null) { 9 n--; 10 cur2 = cur2.next; 11 } 12 if (cur1 != cur2) {//两链表尾节点不同,两者一定没有相交节点 13 return null; 14 } 15 //已经确定一定有相交节点,现在找出第一个相交节点 16 cur1 = n > 0 ? list1.head : list2.head;//cur1为长链表的指针 17 cur2 = n > 0 ? list2.head : list1.head;//cur2为短链表的指针 18 //让长链表先走n步 19 n = Math.abs(n); 20 for (int i = 0; i < n; i++) { 21 cur1 = cur1.next; 22 } 23 //之后让两链表指针同时右走,直到指向相同节点为止 24 while (cur1 != cur2) {// 25 cur1 = cur1.next; 26 cur2 = cur2.next; 27 } 28 return cur1; 29 }
2.一个链表有环一个链表无环:一定没有相交的节点(可通过画图分析证明)
3.两个链表都有环:
思路:在有相交情况下,要分两种情况考虑:
(1).在成环之前就已经相交:则将其转为无环的情况,把第一个成环节点作为尾节点即可
(2).在环中有相交:则遍历其中一个链表(直到转了一圈为止),如果经过了另一个链表的环起始点则有相交
代码及解析:
1 public static Node bothLoop(MyLink list1,Node loop1,MyLink list2,Node loop2) { 2 Node cur1 = list1.head; 3 Node cur2 = list2.head; 4 if (loop1 == loop2) {//在成环前就相交的情况下,寻找相交的第一个节点(转成无环模型) 5 int n = 0; 6 while (cur1.next != loop1) { 7 n++; 8 cur1 = cur1.next; 9 } 10 while (cur2.next != loop1) { 11 n--; 12 cur2 = cur2.next; 13 } 14 cur1 = n > 0 ? list1.head : list2.head; 15 cur2 = n > 0 ? list2.head : list1.head; 16 n = Math.abs(n); 17 for (int i = 0; i < n; i++) { 18 cur1 = cur1.next; 19 } 20 while (cur1 != cur2) { 21 cur1 = cur1.next; 22 cur2 = cur2.next; 23 } 24 return cur1; 25 } 26 else {//说明就算有相交节点也在环后 或 根本没有相交节点 27 cur1 = loop1.next;//直接从环中去寻找有没有相交节点 28 while (cur1 != loop1) { 29 if (cur1 == loop2) { 30 return loop2;//也可以return loop1,两者都可认为是相交的第一个节点 31 } 32 cur1 = cur1.next; 33 } 34 return null;//否则就没有公共节点 35 } 36 }
最后对这三种情况进行总结,得到一个总结性函数:
1 //最后对三种情况进行总结得到一个函数: 2 public static Node getIntersectNode(MyLink list1,MyLink list2) { 3 Node head1 = list1.head; 4 Node head2 = list2.head; 5 if (head1 == null || head2 == null) { 6 return null; 7 } 8 Node loop1 = ChainList.getLoopList2(list1); 9 Node loop2 = ChainList.getLoopList2(list2); 10 if (loop1 == null && loop2 == null) { 11 return noLoop(list1,list2); 12 } 13 else if (loop1 != null && loop2 != null) { 14 return bothLoop(list1,loop1,list2,loop2); 15 } 16 else { 17 return null; 18 } 19 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」