[LeetCode][初级算法][链表]46 环形链表

https://leetcode-cn.com/explore/interview/card/top-interview-questions-easy/6/linked-list/46/

这题不要求返回环的入口,算是比较初级的题,然而我自己还是没能想出来。

判断是否成环,主要是采用了“相遇法”。

也就是两个指针同时从head遍历,只不过一个步长为1,一个步长为2,如果成环,则快的那个总会在环中与慢的那个相遇。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode *slow,*fast;
    slow=fast= head;

    while(fast&&fast->next){
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
            return true;        
    }
    return false;
}

我在思考时,最原始的想法是为每一个node打上“已阅”的标记,这样遍历时如果遇到已经遇到的node,则判断为成环了。

也许可以通过使用hashmap把链表中每一个node的地址保存下来,但是题目要求不使用额外空间,ListNode结构体又无法扩展,所以放弃了。

补充

如果要求返回环的入口?

我们可以通过双指针法来操作,目标是让双指针相遇在环的入口。


好了,画这个图让我仿佛回到了小学的快乐时光。

主要是为了说明,a是head到入口的距离,b是入口到相遇点的距离,黄色是相遇点。再假设环周常是L。

在之前判断是否成环的算法中,慢指针走过的路程是 a+b,快指针走过了a+b+k*L 。

同时,快指针走过的路程是慢指针的2倍。

也就是a+b+k*L = 2*(a+b),  得到 a+b = k*L 。

让p1从头开始,p2从相遇点开始,同时走a+b步,可以知道他们终将都回到相遇点。

而这个过程中,他们必然会在入口相遇,再携手走完b这段路程。

经过上述的思考,我们就可以知道如何找出环路的入口节点了。

摘抄一段代码吧。

//找到环的入口点
Node* findLoopPort(Node *head)
{
    //如果head为空,或者为单结点,则不存在环
    if(head == NULL || head->next == NULL) return NULL;
    Node *slow,*fast;
    slow = fast = head;
    //先判断是否存在环
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
            break;
    }
    if(fast != slow) return NULL;    //不存在环
    fast = head;                //快指针从头开始走,步长变为1
    while(fast != slow)            //两者相遇即为入口点
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页