JZ36 两个链表的第一个公共结点

JZ36 两个链表的第一个公共结点

描述

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。

数据范围:n ≤ 1000;

要求:空间复杂度O(1),时间复杂度O(n)。

示例1

输入:

{1,2,3},{4,5},{6,7}

返回值:

{6,7}

**说明:**第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分,这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的。

示例2

输入:

{1},{2,3},{}

返回值:

{}

**说明:**2个链表没有公共节点 ,返回null,后台打印{}

解析

由于要求空间复杂度O(1),时间复杂度O(n),则如遍历两个链表的所有节点(时间复杂度O(n²) )、利用栈装入两个链表再从尾部开始找(空间复杂度O(1) )的方法都不符合要求。这里使用两种稍微偏数学一点的方法解决。

差值法

由于两个链表不一定等长,所以不能直接在两个链表从头进行同步遍历时进行比较,不过由此可以产生一种思路:让长的链表先走 n 步,其中 n 为长链表比短链表多出的节点数,使得他们长度相等,再进行同步遍历比较。

代码清单

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode node1 = pHead1;
        ListNode node2 = pHead2;
        int num1 = 0, num2 = 0;
        // 先求两个链表的长度
        while(node1 != null){
            num1++;
            node1 = node1.next;
        }
        while(node2 != null){
            num2++;
            node2 = node2.next;
        }
        // 长的先把多余部分走了
        int sub = 0;
        ListNode temp = null, temp2 = null;
        if(num1 > num2){
            sub = num1 - num2;
            temp = pHead1;
            temp2 = pHead2;
        }else{
            sub = num2 - num1;
            temp = pHead2;
            temp2 = pHead1;
        }
        while(sub != 0){
            temp = temp.next;
            sub--;
        }
        // 长度相同了,一起走进行对比即可
        while( temp != null && temp2 != null){
            if( temp == temp2)
                return temp;
            temp = temp.next;
            temp2 = temp2.next;
        }
        return null;
    }
}

这种方式思路简单,但实现时的步骤比较复杂,下面换一种实现更简单的方法。

等值法

由于比较时的关键在于两个链表长度不一定相等,所以我们可以采取操作使两个链表的长度相等。

如果有公共部分

设链表1的特有部分的长度为 A,公共部分长度为 C,链表2的特有部分长度为 B,公共部分长度也是 C。

首先 ,将链表1与链表2组合起来,作为链表 a,将链表2与链表1组合起来,作为链表 b。

可以知道链表 a 和 b 的长度是相同的,所以A + C + B + C = B + C + A + C 成立。同时可以发现,在遍历完 A C BB C A 后,两个链表已经经过相同的长度,接下来他们都将遍历到相同部分 C 的头结点,也就是说,只要进行链表 a 和 b 的遍历,就能让遍历指针同步到达公共部分的头节点,此时两个遍历指针相同,这就是我们要找的返回值。

如果没有公共部分

即链表1的特有部分为 A 和 C,链表2的特有部分为 B 和 D,同样将它们组合为链表 a 和 b。

则进行链表 a 和 b 的遍历时,有 A + C + B + D = B + D + A + C 成立,也就是说,遍历完链表 a 和 b 后,遍历指针会同时指向空。此时它们同时为空,符合相同的条件,对应的返回值也为 null,即不存在公共部分。

若两链表长度相同也是符合这个规律的,不过是 1+2=3 与 2+1=31+1=2 与 1+1=2 的区别罢了。

代码清单

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode node1 = pHead1;
        ListNode node2 = pHead2;
        // 循环到两个指针相同为止
        // 找到相同的就停在相同的,找不到最后会同时到空(无论长度是否相等)
        while(node1 != node2){
            // 链表1走完了就继续走链表2
            if(node1 != null )
                node1 = node1.next;
            else
                node1 = pHead2;
            // 链表2走完了就继续走链表1
            if(node2 != null )
                node2 = node2.next;
            else
                node2 = pHead1;
        }
        return node1;
    }
}

总结

由于本题限制了时间复杂度和空间复杂度,所以需要采用巧妙一点的方法,不然直接暴力破解或者借助其他数据结构也能完成(= =),不过多看点巧妙的方法也是挺好的。

从写完这题到完成这篇笔记用了3天,属实够摸。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值