刷题笔记26——链表是否带环、环入口、链表(有环无环)相交问题分析与总结


一、判断一个链表是否成环

解法1:哈希表

使用哈希表,在遍历结点的过程中把结点放到哈希表中,每一次都去查结点有没有在哈希表,
如果查到某个结点在哈希表中(这里find如果没找到,会返回end,此时继续放结点到哈希表),如果查到了,就返回那个结点。
这里可以将flag改成结点的形式,找到了返回那个结点,找不到就返回NULL

时间复杂度:O(n), 对于含 n 个元素的链表,我们访问每个元素最多一次。添加一个结点到哈希表中只需要花费 O(1) 的时间。
空间复杂度:O(n), 空间取决于添加到哈希表中的元素数目,最多可以添加 n 个元素。

在这里插入图片描述

解法2:快慢指针

就像跑四百米,跑得快的人一定能追上跑的慢的人。

时间复杂度:

  • 若不存在环,则时间取决于链表的长度,也就是O(N)
  • 如果存在环,则时间复杂度为O(N+K),N为非环部分长度,K为环形部分长度

空间复杂度:O(1)
在这里插入图片描述

二、判断两个无环链表是否相交并返回第一个相交结点

如果通过中得到的两个loop环入口结点均为空(NULL),那么这两个链表无环,两个无环链表即可能相交,也可能不相交

2.1 解法1 - 哈希表

思路

  • 首先遍历链表A,把链表A中所有结点放到哈希set中
  • 然后遍历链表B,去查这个哈希表,看哈希表中是否有B的结点,如果有,就相交,没有则不相交
    在这里插入图片描述

2.2 解法2 - 不使用哈希表

思路
两个无环链表若相交,它们的尾结点的地址一定是相同的,于是分别统计两个链表的长度,获得两个链表的最后一个结点
在这里插入图片描述

2.3 无环链表相交情况测试

在这里插入图片描述
在这里插入图片描述

三、判断两个有环链表是否相交并返回第一个相交结点

如果通过中得到的两个loop环入口结点均不为空(NULL),那么这两个链表有环,两个有环链表既可能相交,也可能不相交

那么,会有下面的这三种情况

  • 两有环链表先相交,后共享环(loop1 == loop2)
  • 两有环链表各自成环,不相交(loop1 != loop2)
  • 两有环链表“野马”成环(loop1 != loop2)

3.1 两有环链表先相交,后共享环

这种情况下,两个链表的环入口结点是相同的地址,于是,这种情况跟两个无环链表相交问题是类似的
只不过我们需要把原来的NULL作为最后改为loop作为最后(否则不就死循环了嘛)
在这里插入图片描述

两有环链表先相交,后共享环测试
在这里插入图片描述

3.2 两有环链表各自成环不相交及野马成环

这种情况下,获取的两个链表的环入口结点loopA和loopB是不相等的
于是可以从loopA的下一个结点开始,一直往下走

  • 如果一直到转回自己还没有遇到loopB,就说明不相交,返回NULL
  • 如果在转回自己的过程中遇到了loopB,就说明相交,此时可以返回loopA(离链表A的头近),也可以返回looB(离链表B的头近)

在这里插入图片描述

两有环链表各自成环,不相交测试
在这里插入图片描述

两有环链表“野马”成环测试
在这里插入图片描述

测试代码

#include <iostream>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <queue>
#include <climits>
#include <iomanip>
#include <stack>
#include <set>
#include <unordered_set>
using namespace std;

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
    ListNode* GetIntersectListNode(ListNode *headA, ListNode *headB) {
        if (headA == NULL || headB == NULL)
            return NULL;

        ListNode* loopListNodeA = GetLoopListNode(headA);
        if (loopListNodeA == NULL)
            cout << " - 链表A无环\n";
        else 
            cout << " - 链表A的环入口结点 = " << loopListNodeA->val << endl;
        ListNode* loopListNodeB = GetLoopListNode(headB);
        if (loopListNodeB == NULL)
            cout << " - 链表B无环\n";
        else 
            cout << " - 链表B的环入口结点 = " << loopListNodeB->val << endl;

        if (loopListNodeA == NULL && loopListNodeB == NULL) {
            cout << " - 两条链表均无环,现在找第一个相交结点...\n";
            return noLoopMode2(headA, headB);
        }
        else if (loopListNodeA != NULL && loopListNodeB != NULL) {
            cout << " - 两条链表均有环,现在找第一个相交结点...\n";
            return bothLoopMode(headA, loopListNodeA, headB, loopListNodeB);
        }
        else {
            cout << " - 一个链表有环,另一个链表无环,一定不相交(单链表)\n";
            return NULL;
        }
    }

    // 两个链表无环时,可以使用哈希表
    ListNode* noLoopMode1(ListNode *headA, ListNode *headB) {
        ListNode *cur = headA;
        unordered_set<ListNode*> SetNodes;
        unordered_set<ListNode*>::iterator iter;
        while (cur != NULL) {
            if ((iter = SetNodes.find(cur)) == SetNodes.end()) {
                SetNodes.insert(cur);
                cur = cur->next;
            } else {
                break;
            }
        }
        cur = headB;
        while (cur != NULL) {
            if ((iter = SetNodes.find(cur)) == SetNodes.end()) {
                cur = cur->next;
            } else {
                return cur;
            }
        }
        return NULL;    // 一直到headB到头了还没查到,就不相交
    }

    // 两个链表无环时,也可以不使用哈希表
    // 方法是分别统计两个链表的长度,分别获得两个链表的最后一个结点
    ListNode* noLoopMode2(ListNode *headA, ListNode *headB) {
        ListNode *curA = headA;
        ListNode *curB = headB;
        int n = 0;  // 两链表长度的差值
        while (curA->next != NULL) {    // curA的next为空,说明curA是最后一个结点
            n++;
            curA = curA->next;
        }
        while (curB->next != NULL) {
            n--;
            curB = curB->next;
        }
        if (curA != curB)   // 此时curA和curB是最后一个结点,不等就不可能相交
            return NULL;
        
        // 此时curA和curB虽然相等了,但并不是第一个相交的结点
        // 于是让curA指向长的链表,curB指向短的链表
        curA = ((n > 0) ? headA : headB);
        if (curA == headA)
            curB = headB;
        else
            curB = headA;

        n = fabs(n);
        while (n > 0) {
            curA = curA->next;
            n--;
        }
        while (curA != curB) {
            curA = curA->next;
            curB = curB->next;
        }
        return curA;
    }

    ListNode* bothLoopMode(ListNode *headA, ListNode *loopA, ListNode *headB, ListNode *loopB) {
        if (loopA == loopB) {
            ListNode *curA = headA;
            ListNode *curB = headB;
            int n = 0;
            while (curA->next != loopA) {   // 注意这里是loopA而不再是NULL,因为有环
                n++;
                curA = curA->next;
            }
            while (curB->next != loopB) {   // 同理
                n--;
                curB = curB->next;
            }
            curA = ((n > 0) ? headA : headB);
            if (curA == headA)
                curB = headB;
            else
                curB = headA;
            while (n > 0) {
                n--;
                curA = curA->next;
            }
            while (curA != curB) {
                curA = curA->next;
                curB = curB->next;
            }
            return curA;
        } else {
            ListNode *cur = loopA->next;
            while (cur != loopA) {
                if (cur == loopB)
                    return loopA;
                cur = cur->next;
            }
            return NULL;
        }
    }

    ListNode* GetLoopListNode(ListNode *head) {
		if (head == NULL || head->next == NULL || head->next->next == NULL)
            return NULL;
        
        ListNode *quick = head->next->next;
        ListNode *slow = head->next;
        while (quick != slow) {
            if (quick->next == NULL || quick->next->next == NULL)
                return NULL;
            quick = quick->next->next;
            slow = slow->next;
        }
        quick = head;
        while (quick != slow) {
            quick = quick->next;
            slow = slow->next;
        }
        return quick;
    }

    void print(ListNode *head) {
        ListNode *cur = head;
        ListNode *loop = GetLoopListNode(head);
        if (loop == NULL) {
            while (cur != NULL) {
                cout << cur->val << "-->";
                cur = cur->next;
            }
            cout << "null" << endl;
            return;
        } else {
            int n = 0;
            while (n < 3) {
                if (cur == loop) {
                    n++;
                }
            cout << cur->val << "-->";
            cur = cur->next;
            }
            cout << "...";
        }
        cout << endl;
    }
};

int main(int argc, char *argv[]) {
    Solution s;

    cout << "===================== Part 1 : 两条无环链表 =====================\n\n";

    cout << "1. ==== 两个无环链表相交测试 ====\n";
    ListNode *head1 = new ListNode(1);
    head1->next = new ListNode(2);
    head1->next->next = new ListNode(3);
    head1->next->next->next = new ListNode(4);
    head1->next->next->next->next = new ListNode(5);
    head1->next->next->next->next->next = new ListNode(6);
    head1->next->next->next->next->next->next = new ListNode(7);
    cout << " - 无环链表head1初始化完毕:";
    s.print(head1);

    ListNode *head2 = new ListNode(0);
    head2->next = new ListNode(9);
    head2->next->next = new ListNode(8);
    head2->next->next->next = head1->next->next->next->next->next; 
    cout << " - 无环链表head2初始化完毕:";
    s.print(head2);

    cout << " - 两个无环链表的第一个相交结点为:" << s.GetIntersectListNode(head1, head2)->val << endl;
    cout << "1. ============ end =============\n\n";

    cout << "2. ==== 两无环链表不相交测试 ====\n";
    ListNode *head3 = new ListNode(1);
    head3->next = new ListNode(2);
    head3->next->next = new ListNode(3);
    cout << " - 无环链表head3初始化完毕:";
    s.print(head3);

    ListNode *head4 = new ListNode(0);
    head4->next = new ListNode(2);
    head4->next->next = new ListNode(1);
    cout << " - 无环链表head4初始化完毕:";
    s.print(head4);

    if (s.GetIntersectListNode(head3, head4) == NULL)
        cout << " - 两条无环链表不相交\n";
    cout << "2. ============ end =============\n\n";


    cout << "\n===================== Part 2 : 两条有环链表 =====================\n\n";
    cout << "1. ==== 两有环链表不相交测试 ====\n";
    ListNode *head5 = new ListNode(1);
    head5->next = new ListNode(2);
    head5->next->next = new ListNode(3);
    head5->next->next->next = new ListNode(4);
    head5->next->next->next->next = new ListNode(5);
    head5->next->next->next->next->next = head5->next;
    cout << " - 有环链表head5初始化完毕:";
    s.print(head5);

    ListNode *head6 = new ListNode(1);
    head6->next = new ListNode(2);
    head6->next->next = new ListNode(3);
    head6->next->next->next = new ListNode(4);
    head6->next->next->next->next = new ListNode(5);
    head6->next->next->next->next->next = head6->next->next->next;
    cout << " - 有环链表head6初始化完毕:";
    s.print(head6);

    if (s.GetIntersectListNode(head5, head6) == NULL)
        cout << " - 两条有环链表不相交\n";
    cout << "1. ============ end =============\n\n";

    cout << "2. ==== 两有环链表相交测试 ====\n";
    ListNode *head7 = new ListNode(1);
    head7->next = new ListNode(2);
    head7->next->next = new ListNode(3);
    head7->next->next->next = new ListNode(4);
    head7->next->next->next->next = new ListNode(5);
    head7->next->next->next->next->next = new ListNode(6);
    head7->next->next->next->next->next->next = new ListNode(7);
    head7->next->next->next->next->next->next->next = head7->next->next->next->next;
    cout << " - 有环链表head7初始化完毕:";
    s.print(head7);

    ListNode *head8 = new ListNode(1);
    head8->next = new ListNode(2);
    head8->next->next = head7->next->next->next;
    cout << " - 有环链表head7初始化完毕:";
    s.print(head8);
    cout << " - 两个有环链表的第一个相交结点为:" << s.GetIntersectListNode(head7, head8)->val << endl;
    cout << "2. ============ end =============\n\n";

    cout << "3. ==== 两有环链表相交测试 ====\n";
    ListNode *head9 = new ListNode(1);
    head9->next = new ListNode(2);
    head9->next->next = new ListNode(3);
    head9->next->next->next = new ListNode(4);
    head9->next->next->next->next = head9->next;
    cout << " - 有环链表head9初始化完毕:";
    s.print(head9);

    ListNode *head10 = new ListNode(1);
    head10->next = head9->next->next->next;
    cout << " - 有环链表head10初始化完毕:";
    s.print(head10);
    cout << " - 两个有环链表的第一个相交结点为:" << s.GetIntersectListNode(head9, head10)->val << endl;
    cout << "3. ============ end =============\n\n";

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值