剑指offer-36、两个链表的第⼀个公共节点

2025-12-12 0 338

题⽬描述

输⼊两个链表,找出它们的第⼀个公共结点。(注意因为传⼊数据是链表,所以错误测试数据的提示是⽤其他⽅式显示的,保证传⼊数据是正确的)

思路及解答

HashSet包含法

第⼀种做法,直接依赖于 HashSet ,遍历第⼀个链表的时候,将所有的节点,添加到 hashset 中,

遍历第⼆个链表的时候直接判断是否包含即可,属于空间换时间的做法。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) return null;
        
        // 使用HashSet存储第一个链表的所有节点
        HashSet visited = new HashSet();
        
        // 遍历第一个链表,将所有节点加入集合
        ListNode current = pHead1;
        while (current != null) {
            visited.add(current);
            current = current.next;
        }
        
        // 遍历第二个链表,检查节点是否在集合中
        current = pHead2;
        while (current != null) {
            if (visited.contains(current)) {
                return current; // 找到第一个公共节点
            }
            current = current.next;
        }
        
        return null; // 没有公共节点
    }
  • 时间复杂度​:O(m+n),需要遍历两个链表各一次
  • 空间复杂度​:O(min(m,n)),存储较短链表的节点

双栈法

利用栈的后进先出特性,从链表尾部开始比较,找到最后一个相同的节点。公共节点之后的节点都是相同的,所以从后往前比较,最后一个相同的节点就是第一个公共节点

import java.util.Stack;

public class Solution {
    /**
     * 使用双栈查找两个链表的第一个公共节点
     * 思路:将两个链表分别压入栈中,然后同时出栈比较
     * 时间复杂度:O(m+n),空间复杂度:O(m+n)
     */
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if (pHead1 == null || pHead2 == null) return null;
        
        Stack stack1 = new Stack();
        Stack stack2 = new Stack();
        
        // 将两个链表的所有节点分别压入栈中
        ListNode current = pHead1;
        while (current != null) {
            stack1.push(current);
            current = current.next;
        }
        
        current = pHead2;
        while (current != null) {
            stack2.push(current);
            current = current.next;
        }
        
        ListNode commonNode = null;
        
        // 同时从两个栈弹出节点进行比较
        while (!stack1.isEmpty() && !stack2.isEmpty()) {
            ListNode node1 = stack1.pop();
            ListNode node2 = stack2.pop();
            
            if (node1 == node2) {
                commonNode = node1; // 记录公共节点
            } else {
                break; // 遇到不同节点,停止比较
            }
        }
        
        return commonNode;
    }
}
  • 时间复杂度​:O(m+n),需要遍历两个链表各两次(压栈和出栈)
  • 空间复杂度​:O(m+n),需要两个栈存储所有节点

长度差法(推荐)

可以将两个链表想象为两段路程,公共节点是终点。让长的链表先走多出的距离,然后同时前进,就能同时到达公共节点

譬如现在有⼀个链表 1->2->3->6->7 ,另外⼀个链表 4->5->6->7 ,明显可以看出第⼀个公共节点是6 。

最直接的⽅法,每⼀个链表都遍历⼀次,计算链表中的个数,⽐如 1->2->3->6->7 个数为5, 4->5->6->7 个数为4,两者相差1(设为k)个。

我们可以使⽤两个指针,分别指向链表的头部。然后让第⼀个链表的指针先⾛ k=1 步。

这样就相当于指针后⾯的两个链表等⻓了。

就可以开始⽐较,如果不相等,则两个指针都往后移动即可,知道节点为null。

/*
public class ListNode {
 int val;
 ListNode next = null;
 ListNode(int val) {
 this.val = val;
 }
}*/
public class Solution {
     public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         // 只要有⼀个为空,就不存在共同节点
         if (pHead1 == null || pHead2 == null) {
         	return null;
         }
         // 计算链表1中的节点个数
         int numOfListNode1 = 0;
         ListNode head1 = pHead1;
         while (head1 != null) {
             numOfListNode1++;
             head1 = head1.next;
         }
         
         // 计算链表2中节点个数
         int numOfListNode2 = 0;
         ListNode head2 = pHead2;
         while (head2 != null) {
             numOfListNode2++;
             head2 = head2.next;
         }
         
         // ⽐较两个链表的⻓度
         int step = numOfListNode1 - numOfListNode2;
         if (step > 0) {
             // 链表1更⻓,链表1移动
             while (step != 0) {
                 pHead1 = pHead1.next;
                 step--;
             }
         } else {
             // 链表2更⻓,链表2移动
             while (step != 0) {
                 pHead2 = pHead2.next;
                 step++;
             }
         }
         
         // 循环遍历后⾯的节点,相等则返回
         while (pHead1 != null && pHead2 != null) {
             if (pHead1 == pHead2) {
             	return pHead1;
             } else {
                 pHead1 = pHead1.next;
                 pHead2 = pHead2.next;
             }
         }
         return null;
     }
}
  • 时间复杂度​:O(m+n),需要遍历链表三次(两次计算长度,一次查找)
  • 空间复杂度​:O(1),只使用常数级别额外空间

但是上⾯的做法,如果公共节点在最后⼀个,假设⼀个链表⻓度为 n ,⼀个为 m ,那么计算个数就要全部遍历,需要 n+m 。两个链表都移动,到最后⼀个节点的时候才相等,也是 n+m ,也就是 O(2*(n+m)) 。

双指针遍历法(最优)

有没有更加好⽤的做法呢?肯定有,我们来看:

两个链表分别是:

如果我在第⼀个链表后⾯拼接上第⼆个链表,第⼆个链表后⾯拼接上第⼀个链表,就会变成下⾯的样⼦:

发现了⼀个规律,也就是拼接之后的链表,是等⻓度的,第⼀个和第⼆个链表都从第⼀个开始⽐较,只要相等,就说明是第⼀个公共节点。也就是上⾯被圈起来的 6 节点。

原理如下:

  • 设链表1独有部分长度为a,链表2独有部分长度为b,公共部分长度为c
  • 指针p1路径:a + c + b
  • 指针p2路径:b + c + a
  • 两个指针路径长度相同,会在公共节点相遇

特殊情况处理:​​当两个链表没有公共节点时,两个指针会同时变为null,退出循环

public class Solution {
     public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         // 只要有⼀个为空,就不存在共同节点
         if (pHead1 == null || pHead2 == null) {
         	return null;
         }
         
         ListNode head1 = pHead1;
         ListNode head2 = pHead2;
         while (head1 !=head2) {
             // 如果下⼀个节点为空,则切换到另⼀个链表的头节点,否则下⼀个节点
             head1 = (head1 == null) ? pHead2 : head1.next;
             head2 = (head2 == null) ? pHead1 : head2.next;
         }
         return head1;
     }
}
  • 时间复杂度​:O(m+n),每个指针遍历两个链表各一次
  • 空间复杂度​:O(1),只使用两个指针

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 剑指offer-36、两个链表的第⼀个公共节点 https://www.zuozi.net/35962.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务