剑指 Offer 52. 两个链表的第一个公共节点

题目

输入两个链表,找出它们的第一个公共节点。
在这里插入图片描述
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

思路
因为单链表的每个节点都只有一个next域。那么在两个单链表存在公共节点的情况下,在两个链表的第一个公共节点之后,后面的所有节点就都是重合的,不可能再出现分叉。
  1. 利用两个辅助栈
    当两个单链表存在公共节点的情况下,从它们的公共尾节点开始向前遍历,最后一个相同的节点,就是它们的第一个公共节点。
    当两个单链表不存在公共节点的情况下,它们的尾节点就不是相同的。
    ==>定义两个辅助栈,分别遍历两个单链表,并将它们各自的节点压入栈中。最后,由两个堆栈的栈顶元素开始进行对比,来判断两个单链表是否有公共节点,以及有公共节点的情况下,对应的第一个公共节点。

时间复杂度O(m+n)
空间复杂度O(m+n)

  1. 双指针(一前一后)
    1)先获取两个链表的长度l1、l2,计算出它们的长度的差值distance。
    2)然后让较长的链表,先走出distance个节点(先向前遍历distance个节点)。
    3)让指向两个链表的指针同步遍历,遍历时的条件为:
    while(l1 != null && l2 != null && l1 != l2)
    其中,l1 != null && l2 != null可以只写一个。因为它们的长度差已经先一步遍历了,从两个指针同时开始遍历的时候,两个链表所剩下的未遍历的节点数就是相等的。所以,只要一个链表遍历到尾节点,另一个链表必然也是遍历到了尾节点。
    =>当退出循环的时候,对应着两种情况:
    <1>l1 == null,即:两个链表遍历完成。
    这说明,两个链表遍历完,也没有找到相同的节点,说明两个链表没有公共节点。直接return l1就可以(因为此时 l1 == null,正好对应两个链表没有尾节点时,应该返回的值)
    <2>l1 == l2,即:找到了两个链表的第一个公共节点。
    直接return l1即可。

时间复杂度O(m+n)
空间复杂度O(1)

  1. 双指针(浪漫相遇)
    定义两个指针l1、l2,分别指向两个链表的头节点headA、headB,然后开始进行链表的 同步 遍历。
    其中,l1在遍历完headA的链表之后,就接着遍历headB的链表;
    l2在遍历完headB的链表之后,就接着遍历headA的遍历。
    如果两个链表有公共节点,那么在遍历过程中,它们出现的第一个相同的节点,就是第一个公共节点;
    如果两个链表没有公共节点,那么在遍历过程中,它们出现的第一个晓彤的节点,就是在l1、l2 同时完成了两条链表的遍历之后,所指向的相同的 == null的节点。
    ==>分析如下:
    假设两链表存在公共节点,则公共节点所在的链表段为c,headA自己所特有的链表段为a,headB自己所特有的链表段为b。
    则有:headA所在的链表为:a+c
    headA所在的链表为:b+c。
    那么,当l1遍历完headA之后,开始遍历headB;
    l2遍历完headB之后,开始遍历headA;
    两个指针,将在各自第二次遍历c段的时候相遇(因为此时,它们所遍历过的节点数相等):
    a+c+b == b+c+a

while(listA != listB){
listA = (listA != null)? listA.next : headB ;
listB = (listB != null)? listB.next : headA ;
}
//需要注意的是:这里的循环内部的两个三元运算符用的很巧妙
//当headA和headB的长度不相等时,只有两个指针均把两条链表都遍历了一遍的情况下,才可能出现listA、listB均为null的情况,这时,他们会退出while循环---->正好,当两个链表没有公共节点。
//当headA和headB的长度相等时,两个指针都是 只遍历完一条链表都遍历时,就会出现listA、listB均为null的情况,---->正好也对应了两个链表没有公共节点的情况(因为两条链表的长度是相同的,如果两个链表有公共节点,在各自遍历第一条链表的时候,就会出现两个指针所指向的链表相同的情况,但是并没有出现这种情况,说明两个链表没有公共节点)


时间复杂度O(m+n)
空间复杂度O(1)

实现
  1. 利用两个辅助栈
    因为这种方法对应的空间复杂度,不满足题意。所以,这里就省去了代码实现。
  2. 双指针(一前一后)
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //先获取两个链表的长度
        int lengthA = getLength(headA);
        int lengthB = getLength(headB);
        //定义两个指针,分别指向两个链表
        ListNode listLong = headA;
        ListNode listShort = headB;
        //获取长度差
        int distance = lengthA - lengthB;//假设A比B长
        if(lengthA < lengthB){
            //实际的长度,与假设的情况相反
            listLong = headB;
            listShort = headA;
            distance = lengthB - lengthA;
        }

        //注意:如果distance==0的话,表明一样长,是不用先走的---->应该进不到for循环内,执行不了先走的语句才对
        for(int i = 0;i < distance;i++){//这里用的很巧妙!!!!!!
            listLong = listLong.next;
        }
        
        //开始正式,两个链表一起进行遍历操作
        while(listLong != null && listLong != listShort){//不会有空指针异常,因为“&&”是短路与,当listLong为null的时候,就不会再去执行后面你的语句了
            listShort = listShort.next;//往后遍历
            listLong = listLong.next;//往后遍历
        }
        //退出while循环只有两种情况:
        //1、两个链表都遍历完了,但是仍然没有找到第一个公共节点,=>说明两个链表没有交点
        //此时,listLong、listShort的值,均为null(正好满足没有交点时 的返回值情况)
        //2、在遍历的过程中,找到了第一个公共节点=>退出while循环
        //此时,listLong、listShort指向的就是它们的第一个公共节点(正好满足它们有公共节点时 的返回值情况)
        return listLong;


    }

    //获取链表长度的方法(从1开始计数)
    public static int getLength(ListNode list){

        ListNode cur = list;
        int count = 0;
        while(cur != null ){
            count++;//计数
            cur = cur.next;//完成链表的遍历
        }
        return count;
    }
}
  1. 双指针(浪漫相遇)
//双指针(浪漫相遇)
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //开始时,指针listA、listB分别指向headA、headB链表
        ListNode listA = headA;
        ListNode listB = headB;
        while(listA != listB){
            listA = (listA != null)? listA.next : headB ;
            listB = (listB != null)? listB.next : headA ;
            //需要注意的是:这里的循环内部的两个三元运算符用的很巧妙
            //当headA和headB的长度不相等时,只有两个指针均把两条链表都遍历了一遍的情况下,才可能出现listA、listB均为null的情况,这时,他们会退出while循环---->正好,当两个链表没有公共节点。
            //当headA和headB的长度相等时,两个指针都是 只遍历完一条链表都遍历时,就会出现listA、listB均为null的情况,---->正好也对应了两个链表没有公共节点的情况(因为两条链表的长度是相同的,如果两个链表有公共节点,在各自遍历第一条链表的时候,就会出现两个指针所指向的链表相同的情况,但是并没有出现这种情况,说明两个链表没有公共节点)
        }
        return listA;
    }
}

不断地往前走,不断地往前走,等有一天,你会突然发现:原来已经离起点那么远了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值