zoukankan      html  css  js  c++  java
  • 题解-Linked List Cycle II

    这篇文章最初发表在http://www.meetqun.com/thread-821-1-1.html ,此处略有改动。

    建议想要认真准备的同学自己先尝试,看完分析尝试,然后把自己写的代码和我写的对比,看看有哪些地方做得比我好(比如我的单行if语句不套大括号,可能对代码修改的时候就不方便),哪些地方做得比我差(比如我用了fast,slow,而你用了没意义的命名),多总结,多反思。面试中代码写得如何也是考察的重中之重,我的感觉甚至比算法想没想出来还重要,毕竟算法工作之后基本不再需要,而面试官作为你未来的同事,可是要经常review你的代码的哟。总而言之,只是看水平是提升不了的。

    题目

    Leetcode: https://oj.leetcode.com/problems/linked-list-cycle-ii/

    Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

    Follow up:
    Can you solve it without using extra space?

    解法一:

    1.1 分析

    使用额外存储
    用一个hashtable存储访问过的指针,若当前指针之前也被访问过,则为环入口点。

    1.2 复杂度

    时间O(n),空间O(n)

    1.3 代码

    ListNode *detectCycle(ListNode *head) {
    	unordered_set<ListNode*> visited;
    	while (head != nullptr) {
    		if (visited.find(head) != visited.end()) 
    							return head;
    		visited.insert(head);
    		head = head->next;
    	}
    	return nullptr;
    }
    

    解法二:

    2.1 分析

    使用快慢两个指针,快的指针一次走两步,慢的一次一步。若无环,快的会走到链表末尾。若有环,则快的先进入环内循环走圈,直到慢的进入之后,每同时走一次,快的就接近慢的一步,最终快的肯定会遇到慢的。
    假设从链表到环入口点距离为K,环的长度为L。当慢的走到环入口点时,慢的前进了K,快的前进了2K,此时慢的距离快的K%L,快的距离快的L-K%L,再接着走L-K%L次,快的追上慢的,此时距离入口点L-K%L。也就是说,慢的再走K%L就又回到环入口点。

    如果我们在快慢指针相遇时,让快的回到起始点,再跟慢的一起走,每次一步,那么快的走K步到环入口点,慢的此时也走了K步,由于相遇时慢的距离入口点K%L,所以此时慢的也在入口点,快的和慢的相遇在入口点!

    2.2 复杂度

    时间:O(n),空间O(1)

    2.3 代码

    ListNode *detectCycle(ListNode *head) {
    	ListNode* fast = head;
    	ListNode* slow = head;
    	while (fast != nullptr && fast->next != nullptr) {
    		fast = fast->next->next;
    		slow = slow->next;
    		if (fast == slow) break;
    	}
    	if (fast == nullptr || fast->next == nullptr)
    		return nullptr;
    	fast = head;
    	while (slow != fast) {
    		slow = slow->next;
    		fast = fast->next;
    	}
    	return slow;
    }
    

    解法三:

    3.1 分析

    同解法二,使用快慢两个指针,相遇时说明有环。如何找到环入口点呢?假如我们知道环的长度,我们可以设两个指针,让第一个指针先走环的长度这么多步,另一个指针指向链表头,然后两个指针一起走,那么慢的指针走到环入口点时,快的正好走过环入口点之后又走了环的长度,第二次到达环入口点,两个指针相遇在入口点!
    那么如何获得环的长度呢?在检测是否有环的那个步骤中,快慢两个指针在环中相遇之后,继续每次快的走两步,慢的走一步,那么当它们再次相遇时,慢的正好走过一个环的长度。每走一次,快的接近慢的一步,可以认为开始的时候快的距离慢的环的长度那么多步。

    3.2 复杂度

    时间O(n),空间O(1)

    3.3 代码

    ListNode *detectCycle(ListNode *head) {
    	ListNode* fast = head;
    	ListNode* slow = head;
    	while (fast != nullptr && fast->next != nullptr) {
    		fast = fast->next->next;
    		slow = slow->next;
    		if (fast == slow) break;
    	}
    	if (fast == nullptr || fast->next == nullptr)
    		return nullptr;
    	int cycleLength = 0;
    	do {
    		slow = slow->next;
    		fast = fast->next->next;
    		++cycleLength;
    	} while (slow != fast);
    	fast = slow = head;
    	while (cycleLength > 0) {
    		fast = fast->next;
    		--cycleLength;
    	}
    	while (fast != slow) {
    		fast = fast->next;
    		slow = slow->next;
    	}
    	return slow;
    }
    

    相关题目

    Linked List Cycle:
    https://leetcode.com/problems/linked-list-cycle/

  • 相关阅读:
    Android——ArrayList 、LinkList、List 区别 & 迭代器iterator的使用 & HashMap、Hashtable、LinkedHashMap、TreeMap
    Android--List与ArrayList区别(转)
    android——inflater 用法(转)
    Android——列表视图 ListView(二)SimpleAdapter
    Odoo 仓库扫码打包方案
    Odoo 仓库调拨移动过程中 单位不允许错误的分析及解决方案
    Ubuntu FTP 配置
    attrs 中的 uid
    GitLab安装手记
    [转载]How To Add Swap on Ubuntu 12.04
  • 原文地址:https://www.cnblogs.com/ridershen/p/4474672.html
Copyright © 2011-2022 走看看