每日一题--相交链表

离思五首-元稹

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

每日一题--相交链表_第1张图片

目录

题目描述:

思路分析:

方法及时间复杂度:

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

法二 栈

法三 哈希集合

法四 map或unordered_map

法五 双指针(经典解法)

法六 递归(烧脑解法)

个人总结


题目描述:

每日一题--相交链表_第2张图片

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

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

思路分析:

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

每日一题--相交链表_第3张图片

每日一题--相交链表_第4张图片 也可以用哈希,栈等方法,详情如下。

方法及时间复杂度:

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

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

每日一题--相交链表_第5张图片

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

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)

法二 栈

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

每日一题--相交链表_第6张图片

 代码如下:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        stack 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)

法三 哈希集合

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

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set 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

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

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_map 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还可以这样:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        map 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)

法五 双指针(经典解法)

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

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)

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

每日一题--相交链表_第7张图片

每日一题--相交链表_第8张图片

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

 每日一题--相交链表_第9张图片

法六 递归(烧脑解法)

首先定义一个递归函数`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`,作为参数。函数返回的即为两个链表的相交节点。代码如下:

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语言是世界上最好的语言。

个人总结

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

你可能感兴趣的:(日拱一卒,功不唐捐,链表,数据结构)