每日一题--相交链表

离思五首-元稹

曾经沧海难为水,除却巫山不是云。
取次花丛懒回顾,半缘修道半缘君。

目录

题目描述:

思路分析:

方法及时间复杂度:

[法一 计算链表长度(暴力解法)](#法一 计算链表长度(暴力解法))

[法二 栈](#法二 栈)

[法三 哈希集合](#法三 哈希集合)

[法四 map或unordered_map](#法四 map或unordered_map)

[法五 双指针(经典解法)](#法五 双指针(经典解法))

[法六 递归(烧脑解法)](#法六 递归(烧脑解法))

个人总结


题目描述:

相交返回相交结点,不相交返回空。

160. 相交链表 - 力扣(LeetCode)

思路分析:

要找相交的那个结点,可以让两个起始位置相同的指针遍历两个链表,当指针所指结点位置相同时,就是相交结点。简单粗暴。

也可以用哈希,栈等方法,详情如下。

方法及时间复杂度:

法一 计算链表长度(暴力解法)

计算两个链表长度m,n。再定义两个指针,要使两个指针在同一起始位置,需要使长链表的那个指针那个先走abs(m-n)步

双指针同时走,相同则为相交。否则同时走到空。代码如下:

cpp 复制代码
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA=0,lenB=0;
        ListNode* cur=headA;
        while(cur){
            ++lenA;
            cur=cur->next;
        }
        cur=headB;
        while(cur){
            ++lenB;
            cur=cur->next;
        }
        int gap=abs(lenA-lenB);//两链表的差距
        ListNode* fast=nullptr,*slow=nullptr;
        if(lenA>lenB){
            fast=headA;
            slow=headB;
        }else{
            fast=headB;
            slow=headA;
        }
        while(gap--){
            fast=fast->next;//fast先走gap步
        }
        while(fast){
            if(fast==slow){
                return fast;
            }
            fast=fast->next;
            slow=slow->next;
        }
        return nullptr;
    }
};

时间复杂度O(m+n),m,n为两个链表长度

空间复杂度O(1)

法二 栈

设两个栈,将两个链表的结点分别入栈,依次访问栈顶元素,如果相交,则栈顶元素相等的最后一个结点为相交结点。不相交说明栈顶元素不相同,直接返回空。

代码如下:

cpp 复制代码
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        stack<ListNode*> stA,stB;
        ListNode* cur=headA;
        while(cur){
            stA.emplace(cur);
            cur=cur->next;
        }
        cur=headB;
        while(cur){
            stB.emplace(cur);
            cur=cur->next;
        }
        while(!stA.empty()&&!stB.empty()&&stA.top()==stB.top()){
            cur=stA.top();
            stA.pop();
            stB.pop();
        }
        return cur;
    }
};

时间复杂度O(m+n)

空间复杂度O(m+n)

法三 哈希集合

将第一个链表放入集合中,在遍历第二个链表的结点,然后在集合中查找,如果查找到的第一个结点,就是相交结点,直接返回,否则返回空。代码如下:

cpp 复制代码
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode*> hash;
        ListNode* cur=headA;
        while(cur){
            hash.insert(cur);
            cur=cur->next;
        }
        cur=headB;
        while(cur){
            if(hash.find(cur)!=hash.end()){
                return cur;
            }
            cur=cur->next;
        }
        return nullptr;
    }
};

时间复杂度O(m+n)

空间复杂度O(m)

法四 map或unordered_map

与法三思路相同,都用的是这几个容器的查找功能,不多解释。

cpp 复制代码
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_map<ListNode*, int> hash;
        ListNode* cur = headA;
        while (cur) {
            hash[cur]++;
            cur = cur->next;
        }    
        cur = headB;
        while (cur) {
            if (hash.find(cur) != hash.end()) {
                return cur;
            }
            cur = cur->next;
        }
        return nullptr;
    }
};

时间复杂度O(m+n)

空间复杂度O(m)

map还可以这样:

cpp 复制代码
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        map<ListNode*,int> nodes;
        ListNode* cur=headA;
        while(cur){
            nodes[cur]++;
            cur=cur->next;
        }
        cur=headB;
        while(cur){
            nodes[cur]++;
            cur=cur->next;
        }
        for(auto node:nodes){
            if(node.second==2){
                return node.first;
            }
        }
        return nullptr;
    }
};

时间复杂度O(m+n)

空间复杂度O(m+n)

法五 双指针(经典解法)

"来时路上不见你,于是便走你来时的路,相遇时才发现你也走过我曾走过的路💓"

cpp 复制代码
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
      ListNode *Girl = headA, *Boy = headB;
      while (Girl!=Boy) {
          Girl=Girl==nullptr ? headB:Girl->next;
          Boy = Boy==nullptr ? headA:Boy->next;
      }
     return Boy;
   }
};

时间复杂度O(m+n)

空间复杂度O(1)

利用抽象思维和数学思维,可以将两条链表连接成一条带环链表,让两个指针同时走,环的入口就是相交结点,相逢何必曾相识,同是天涯沦落人,相交就一定会相逢,不相交都走向空。

原理:两指针相遇时,走的距离是相同的。设两条链表未相交部分长度为a,b。相交部分的长度为c。则有a+c+b==b+c+a。当一指针走向空时,则走到另一个链表的开始。

法六 递归(烧脑解法)

首先定义一个递归函数`dfs`,该函数接收3个指针参数:指向链表A当前节点的指针`curA`,指向链表B当前节点的指针`curB`,以及链表B的头节点指针`headB`。

如果A链表和B链表相交,即相交节点之后的节点全部相同,则直接返回相交节点 `(curA==curB)`,表示两个指针指向的节点相同,那么这个节点就是相交节点,直接返回即可。

如果两个指针都没有遍历到链表的结尾,则继续往下遍历,即更新指向链表B节点的指针,直到有一个链表遍历结束,进入下一步操作。

如果B链表已经到达了链表末尾,那么就把`curB`指针指向 `headB`,即链表B的头节点,同时把`curA`指针指向下一个节点,继续查找。这样做的本质是相当于把链表B的指针接到了链表A上,形成了一条新的链表,重新遍历该新链表是否存在相交节点。

如果A链表已经到达了链表末尾,那么就把`curA`指针指向`headB`,即链表B的头节点,同时把`curB`指针指向下一个节点,继续查找。这样做的本质也是相当于把链表A的指针接到了链表B上,形成了一条新的链表,重新遍历该新链表是否存在相交节点。

如果上面的步骤都没有找到相交节点,则表明两个链表不相交,返回空指针即可。

最后,在`getIntersectionNode`函数中,调用`dfs`递归函数,传入链表A的头节点`headA`,链表B的头节点`headB`,以及链表B的头节点指针`headBref`,作为参数。函数返回的即为两个链表的相交节点。代码如下:

cpp 复制代码
struct ListNode *dfs(struct ListNode *curA, struct ListNode *curB,struct ListNode *headB){
    if(curA==curB){
        return curB;
    }

    if(curA!=NULL&&curB!=NULL){
        return dfs(curA,curB->next,headB);
    }
    else if(curB==NULL&&curA!=NULL){
        return dfs(curA->next,headB,headB);
    }else{
        return NULL;
    }
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    return dfs(headA,headB,headB);
}

时间复杂度O(m+n)

空间复杂度O(max(m,n))

用c++超时,用c语言不会。毕竟c语言是世界上最好的语言。

个人总结

俗话说,温故而知新。第一次看这道题时,没有想到那么多解法。随着知识面的扩张,对旧的东西又有了新的感悟。

相关推荐
Beauty.56841 分钟前
P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布
数据结构·c++·算法
爱棋笑谦43 分钟前
二叉树计算
java·开发语言·数据结构·算法·华为od·面试
jimmy.hua1 小时前
C++刷怪笼(5)内存管理
开发语言·数据结构·c++
Freak嵌入式1 小时前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
java·开发语言·数据结构·python·接口·抽象基类
MogulNemenis2 小时前
力扣春招100题——队列
数据结构·算法·leetcode
学java的小菜鸟啊2 小时前
第五章 网络编程 TCP/UDP/Socket
java·开发语言·网络·数据结构·网络协议·tcp/ip·udp
菜鸟求带飞_3 小时前
算法打卡:第十一章 图论part01
java·数据结构·算法
是小Y啦3 小时前
leetcode 106.从中序与后续遍历序列构造二叉树
数据结构·算法·leetcode
万河归海4284 小时前
C语言——二分法搜索数组中特定元素并返回下标
c语言·开发语言·数据结构·经验分享·笔记·算法·visualstudio
秋夫人5 小时前
B+树(B+TREE)索引
数据结构·算法