【剑指offer】37- 两个链表的第一个公共节点

题目描述

题目:输入两个链表,找出他们的第一个公共节点。链表的节点定义如下:

struct ListNode {
     int val;
     ListNode *next;
     ListNode(int x) 
         : val(x), next(NULL) 
    {}
};
  • 首先理解一下题意:两条链表有公共节点是什么情况?
  • 公共节点即它们的next指向相同,值相同,所以若两条链表有公共节点的话,它们的形状会是一个 Y 型,而非 X 型,如下图所示:
    两条链表有公共节点
解题思路
  • 我们很容易想到的解题思路是:遍历两条链表,遍历第一条链表的时候,每遍历一个节点都要和第二条链表的每一个节点去比较。若有一个节点一样则说明两条链表在该节点处重合,于是就找到了它们的公共节点。此时分析这个算法的复杂度:若两条链表的长度分别为m,n,则该方法的时间复杂度为:O(mn)
  • 如何降低我们的时间复杂度呢?我们可以换一种思路,如果两条链表有公共节点,则它们的尾部一定是重合的,若我们从后向前比较,则最后一个重合的节点即为我们的第一个重合节点。我们遍历链表只能从前向后遍历,但是我们比较却是从后向前比较,我们很容易将其与栈”后进先出”的特点相对应。将两条链表的节点依次入栈,每次比较栈顶元素,若相同,则弹出继续比较下一个,直到找到最后一个相同节点
  • 我们分析上述算法的复杂度:我们借助了两个辅助栈,若两条链表的长度分别为m,n,则空间复杂度为O(m+n),时间复杂度也为O(m+n),和第一种方法比,时间复杂度降低,但是增加了空间的消耗
  • 第三种思路:联想我们做过的关于链表的面试题中,譬如:找倒数第k个节点、判断链表是否带环等,解决这些问题我们采用的思路是双指针法,我们可以应用一下这个思路,假设两条链表的长度分别为m,n (m > n),那么我们可以让第一条链表先走(m-n)步,此时这两条链表到达尾节点的长度相同,此时指向两条链表的指针同时走,走一步,进行比较,直到找到第一个公共节点
  • 第三种思路的前提是:我们必须要知道两条链表的长度,这不难,分别遍历即可。此时的时间复杂度为O(m+n),与第二种思路比,我们的空间效率得到了提升
  • 整理好思路后我们就可以开始写代码了,此处我们采用第三种思路:
代码实现
int LengthOfList(ListNode* pHead)
{
    if (pHead == NULL)
        return 0;
    int length = 0;
    while (pHead != NULL)
    {
        ++length;
        pHead = pHead->next;
    }
    return length;
}

ListNode* WalkStep(ListNode* pHead, int step)
{
    while (step--)
    {
        pHead = pHead->next;
    }
    return pHead;
}

ListNode* FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2)
{
    int l1 = LengthOfList(pHead1);
    int l2 = LengthOfList(pHead2);

    int step = l1 - l2;

    if (l1 > l2)
        pHead1 = WalkStep(pHead1, l1 - l2);
    else
        pHead2 = WalkStep(pHead2, l2 - l1);

    while (pHead1 != NULL)
    {
        if (pHead1 == pHead2)
            return pHead1;
        pHead1 = pHead1->next;
        pHead2 = pHead2->next;
    }
    return NULL;
}

void PrintList(ListNode* head)
{
    while (head != NULL)
    {
        printf("%d ->", head->val);
        head = head->next;
    }
    printf("over\n");
}

int main()
{
    ListNode* node1 = new ListNode(1);
    ListNode* node2 = new ListNode(2);
    ListNode* node3 = new ListNode(3);
    ListNode* node4 = new ListNode(4);
    ListNode* node5 = new ListNode(5);
    ListNode* node6 = new ListNode(6);
    ListNode* node7 = new ListNode(7);

    node1->next = node2;
    node2->next = node3;
    node3->next = node6;
    node6->next = node7;
    node4->next = node5;
    node5->next = node6;

    PrintList(node1);
    PrintList(node4);
    ListNode* node = FindFirstCommonNode(node1, node4);
    cout << "第一个公共节点为:" << node->val << endl;
    return 0;
}
  • 程序运行结果为:
    第一个公共节点

猜你喜欢

转载自blog.csdn.net/Aurora_pole/article/details/81380396