📜  检测链表中的循环(1)

📅  最后修改于: 2023-12-03 15:40:37.099000             🧑  作者: Mango

检测链表中的循环

在处理链表的过程中,很可能会遇到链表中存在循环的情况。循环链表使得链表的遍历变得比较棘手,若不处理好就会导致程序陷入死循环,进而影响程序的正确性和效率。在这篇文章中,我们将探讨如何检测链表是否存在循环。

链表的定义

链表是数据结构中非常基础和重要的一种,如下所示:

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

ListNode 表示链表的节点,含有一个 val 变量用来存储该节点的数值,含有一个 next 指针,指向下一个节点。若 next = NULL,则表示该节点为链表的尾节点。

暴力解法

最直接的办法就是采用双重循环,即针对每个节点都遍历一遍链表,看看是否有节点指向已经遍历过的节点,如果存在这种情况,则说明链表中存在循环。

代码如下:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(!head || !head->next) return false;
        ListNode *p = head;
        while(p) {
            ListNode *q = head;
            while(q != p) {
                if(q == p->next) return true;
                q = q->next;
            }
            p = p->next;
        }
        return false;
    }
};

时间复杂度:$O(n^2)$

空间复杂度:$O(1)$

快慢指针解法

用两个指针,一个走得快,一个走得慢,来判断链表中是否存在循环。这种方法很巧妙,快指针每次走两步,慢指针每次走一步,如果两个指针相遇,则说明链表中存在循环,反之则不存在。

代码如下:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(!head || !head->next) return false;
        ListNode *p = head, *q = head->next;
        while(p != q) {
            if(!q || !q->next) return false;
            p = p->next;
            q = q->next->next;
        }
        return true;
    }
};

时间复杂度:$O(n)$

空间复杂度:$O(1)$

总结

相对于第一种解法,第二种解法要更加高效。在面试中,程序员可能会被问道这个问题。因此了解上述两种方法是非常有意义的。