算法热门:链表相交问题(LeetCode 160)

Posted 白龙码~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法热门:链表相交问题(LeetCode 160)相关的知识,希望对你有一定的参考价值。

Part I、何谓相交?

1、 文字解析

我们知道,链表可以分为单链表和双链表,前者有唯一的next指针指向下一个节点,而后者在此基础上新增了一个prev指针指向它的前一个节点,这是区别。但是共同点是,无论是next指针还是prev指针,它们指向的对象具有唯一性,这也是链表作为线性表最大的特点之一。
当我们知道链表的线性特性后,我们可以再来想:一个指针指向的节点确实具有唯一性,但是指向这个节点的指针并不具有唯一性。换句话说,可以同时有一个、两个甚至更多的指针同时指向一个节点(不过本篇文章我们只讨论一个,其余可以类推)。当出现这种情况时,我们可以说,这写指针所在的链表是相交的,交点就是这个唯一指向的节点。

2、图形解析

看完链表相交的文字解析,如果还有点懵懵的,不妨看看下面的图形示意:

可以看到,相交链表的图中,上下两个链表的第二个节点同时都指向同一个三号节点,因此说它们是相交的。

Part II、 进阶!


我们刚刚看了两链表在相交前节点数相等的情况,但事实上两链表长度是未知的,相交节点前的总结点数也是未知的。

方法一

如果节点长度相等,那么我们可以采取两链表同时从头到尾遍历的方式,当两个遍历指针相等且不为NULL时,代表相交节点找到了。
如果不相等,那么我们可以就需要一个m*n的嵌套循环遍历两个链表。什么意思?直接来看代码:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //方法一、O(m*n)的遍历
    struct ListNode* cur1 = headA, *cur2 = headB;
    while(cur1)
    {
        cur2 = headB;
        while(cur2)
        {
            if(cur1 == cur2 && cur1)
            {
                return cur1;
            }
            cur2 = cur2->next;
        }
        cur1 = cur1->next;
    }
    return NULL;
}

外循环由cur1控制,内循环由cur2控制。cur1每走一步,cur2就需要从头到尾遍历一次链表,在遍历的过程中寻找非空的共同结点。
这种方法非常简单易懂且好想,但唯一的问题就是:时间复杂度为O(m*n),消耗较大,有没有什么效率更高的解法?

方法二

我们在前面的讨论中强调了的某一点非常重要:两个链表的长度。
为什么不能采取两链表同时从头到尾遍历的方式?因为它们的长度不同,为了走到相交的节点需要移动的次数不同。
那么,突破点就来了:只要让这两个链表在相交节点前的节点数相同就行了!

为什么这么说?
还是因为链表的线性特性——
如果两个链表相交了,那么相交节点后的部分是完全相同的,唯一不同的就是相交节点前的那些,因此可以断定:两个链表的长度差存在于相交节点前。

于是我们可以采取一种方法,就是:给两个链表中较短的那一个头插一些结点使其和长链表的长度相同,不过这也带来了O(N)的内存消耗。
所以我们不妨换一个思路:
让长的链表先遍历n个结点,其中n就是两个链表的长度差,然后再让两个链表同时遍历,不就可以达到目的了吗?
此时两个遍历的指针就是站在同一起跑线了,因为它们距离相交节点的距离是相同的。
代码实现一下:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    //方法二、求出两链表的长度差x,让长的链表先走x,然后两链表再一起走,找出相同的节点
    struct ListNode* cur1 = headA, *cur2 = headB;
    int len1 = 0, len2 = 0;
    while(cur1)
    {
        cur1 = cur1->next;
        len1++;
    }//求链表A的长度
    while(cur2)
    {
        cur2 = cur2->next;
        len2++;
    }//求链表B的长度
    struct ListNode* longList = headA, *shortList = headB;
    if(len2 > len1)
    {
        longList = headB;
        shortList = headA;
    }//找出长的那个链表
    int n = abs(len1-len2);//求出长度差
    while(n--)
    {
        longList = longList->next;//让长的链表先走n个结点
    }
    while(longList && shortList)//遍历,找到相同的节点
    {
        if(longList == shortList)
            return longList;
        longList = longList->next;
        shortList = shortList->next;
    }
    return NULL;
}

Part III、总结

一个链表相交的问题可以有多种思路,各有特色也各有优点,不过这里还是比较看好最后实现的这种,以O(n)的时间复杂度和O(1)的空间复杂度很好地解决了相交的问题!

如果对链表相交还有其他见解,欢迎评论区指教哦!

欢迎关注博主,定期分享各种题解!

以上是关于算法热门:链表相交问题(LeetCode 160)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode(算法)- 160. 相交链表

LeetCode(算法)- 160. 相交链表

leetcode算法160.相交链表

LeetCode 160. 相交链表

LeetCode 160.相交链表

Leetcode 160. 相交链表