※剑指offer系列29:两个链表的第一个公共结点

       这个题我拿到的第一个想法确实是遍历两个链表,先遍历第一个链表,在遍历内部再遍历第二个链表。这样两个for循环就可以计算出一样的结点了。剑指offer这一章讲的是算法的效率问题,所以我想这个题一定有更快捷的方法。但是我想不出什么快速的办法,o(╥﹏╥)o看了剑指offer上的解法,主要其实就一句话。两个存在共同结点的链表在共同结点后的结点全是共同结点,因为我们计算的链表是单链表。所以这两个链表的拓扑形状应该是Y型,而不是X型。开始我自己想的时候就是想成了X型,因此想了半天没想到什么方案。有了这个思路之后,想到更快捷的方法就是顺其自然的事情了。

       剑指offer上提供了两种做法,首先是从后往前输出,因为链表的后半部分是一样的,但是前面却长短不一。这个剑指offer上的图画的很清楚。可是链表是一个从前向后的结构,没法从后向前输出,这个数据的特点“先进后出”,于是想到了栈。用栈做辅助结构,将链表的数据放进去,然后两个栈同时弹出相同数据,一直到找到最后一个相同的结点。这个思路听起来不错,但是最后剑指offer没有用它,我说一下原因。首先它需要两个辅助的栈的结构,增加了空间的开销来换时间的减少。实在不能说是上策。其次这个思路等于把链表里的数据放进栈里去操作,而这个题目最后返回的是链表的结点,也就是说在栈中找到了第一个相同的结点之后还要再去链表里找到,所以并不简便。

      第二种做法是先求出两个链表的长度,然后根据长度把更长的链表截成和短的一样长,然后从两个链表连个指针一起走,直到遇见相同的结点。这个结点无论是思路还是做法都很新颖简便,所以答案也采用了这种方法。

#include<iostream>
using namespace std;

struct ListNode {
int val;
struct ListNode *next;
/*
ListNode(int x) :
val(x), next(NULL) {
}
*/
};
class Solution {
public:
    ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
        int len1 = getlen(pHead1);
        int len2 = getlen(pHead2);
        int gap=0;

        //这段不能写成if,else。因为if-else中是程序的一个小子块,这样内部定义的变量外面不能用
        ListNode* plong = pHead1;//默认第一个长
        ListNode* pshort = pHead2;
        gap = len1 - len2;
        if(len2>len1)//如果第二个长,修改值
        {
            ListNode* plong = pHead2;
            ListNode* pshort = pHead1;
            gap = len2 - len1;
        }
        for (int i = 0; i < gap; i++)//将两个链表拉到一样长
        {
            plong = plong->next;
        }
        while (plong != NULL&&pshort != NULL&&plong != pshort)//寻找一样的结点
        {
            plong = plong->next;
            pshort = pshort->next;
        }
        ListNode* commonnode = plong;
        return commonnode;
    }
    int getlen(ListNode* ln)
    {
        if (ln == NULL)
            return 0;
        int count = 0;
        while (ln)
        {
            count++;
            ln = ln->next;
        }
        return count;
    }
};
int main()
{
    Solution so;
    ListNode common[2];
    common[0].val = 6;
    common[0].next = &common[1];
    common[1].val = 7;
    common[1].next = NULL;

    ListNode left[3];
    left[0].val = 1;
    left[0].next = &left[1];
    left[1].val = 2;
    left[1].next = &left[2];
    left[2].val = 3;
    left[2].next = &common[0];

    ListNode right[2];
    right[0].val = 4;
    right[0].next = &right[1];
    right[1].val = 5;
    right[1].next = &common[0];

    Solution solu;
    ListNode *node = solu.FindFirstCommonNode(left, right);
    while (node != NULL)
    {
        cout << node->val << " ";
        node = node->next;
    }
    cout << endl;

    return 0;
}

在LeetCode上写的时候,这个题目要求不能修改原来的链表结构,所以衍生出了另一个解法。使用两个指针分别从两个链表的头结点开始查找那个相同的节点,由于两个链表的长度不同所以会有一个链表走完了,另一个链表还有元素。此时将这个走完了的链表的指针指向另一个长链表的头部,而让长链表结束后指向短链表的头部,相当于将这个两个链表走了2次,以均衡两个链表的长度差,这样,就可以在相同的节点相遇。代码如下,没有想到我会继续更新这个系列,最近在不遗余力的找工作,代码语言也变成了python。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        a = headA
        b = headB
        while a != b:
            a = a.next if a else headB
            b = b.next if b else headA
        return a

时隔三年重新做这道题,还是有新的思路出现。

posted @ 2019-07-13 15:20  妮妮熊  阅读(104)  评论(0编辑  收藏  举报