2012年真题-链表的共同后缀

145 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情

Day44 2023/02/19

难度:简单

题目

image.png

示例1

输入:str1 = {l, o, a, d, i, n, g}; str2 = {b, e, i, n, g}
输出:节点i

思路


本题若直接从头遍历两个链表并比较节点的话是不可行的,因为两个链表的长度不一定相同,若一长一短的话,则较长的链表存在节点无法与较短链表进行比较,导致错过公共节点,所以一开始我们要对齐两个链表,如图所示:

3847290_1492600644222_03A6DDE6181554AFDBEFBD3E10100A14.png 为什么可以这样直接对齐,是由于两个链表从第一个公共结点到链表的尾结点都是重合的,所以公共节点一定不会在长链表与短链表对齐之外的部分。
具体步骤:

  1. 分别求出 str1 和 str2 所指的两个链表的长度 m 和 n 。
  2. 将两个链表以表尾对齐:令指针 p 、 q 分别指向 str1 和 str2 的头结点,若 m>=n ,则使 p 指向链表中的第 m-n+1 个结点;若 m<n ,则使 q 指向链表中的第 n-m+1 个结点,即使指针 p 和 q 所指的结点到表尾的长度相等。
  3. 指针 p 和 q 同步向后移动,并判断它们是否指向同一结点。若 p 和 q 指向同一结点,则该点即为所求的共同后缀的起始位置。

关键点


  • 这里的公共节点不是指节点的数据域相同,而是指它们未同一个节点

算法实现


#include <iostream>
using namespace std;

// 定义单链表
struct LNode {
    int val;                                // 数据域
    LNode* next;                            // 指针域
    LNode(int x) : val(x), next(nullptr){}; // 构造函数
};

class Solution {
  public:
    LNode* FindCommonNode(LNode* str1, LNode* str2) {
        int m = GetLengthOfLink(str1);
        int n = GetLengthOfLink(str2);
        int time = 0; // 对齐次数
        // 对齐两链表
        if (m >= n) {
            time = m - n + 1;
            while(time--) str1 = str1->next;                
        }else {
            time = n - m + 1; 
            while (time--) str2 = str2->next;
        }
        // 同时出发遍历,比较节点,若相同就返回,否则没有公共节点
        while (str1 && str2) {
            if(str1 == str2) return str1; // 注意是节点相同
        }
        return nullptr;    
    }

  private:
    // 获取链表长度
    int GetLengthOfLink(LNode* link) {
        int length = 0;
        while(link) {
            link = link->next;
            length++;
        }
        return length;
    }

};

c++代码实现

  • 时间复杂度 O(n)O(n)--- 最坏情况下p ,q一共访问了2n个节点,其中n为较短链表的长度
  • 空间复杂度 O(1)O(1)--- 两个链表为必要空间,除此无额外的辅助空间

总结

  • 这里一定要理解公共节点是指为同一个节点,而不是单纯的数据域相同,不然不太好理解为什么可以这样对齐