Cracking the Coding Interview,第6版,2.8

时间:2015-09-10 05:31:10

标签: java algorithm linked-list big-o time-complexity

问题陈述:给定一个循环链表,实现一个在循环开始时返回节点的algoirthm。

答案密钥提供了比我建议的更复杂的解决方案。我的错误是什么?:

public static Node loopDetection(Node n1) {
    ArrayList<Node> nodeStorage = new ArrayList<Node>();

   while (n1.next != null) {
       nodeStorage.add(n1);
       if (nodeStorage.contains(n1.next)) {
           return n1;
       }
       else {
           n1 = n1.next;
       }
   }

   return null;
}

3 个答案:

答案 0 :(得分:6)

您的解决方案为O(n^2)时间(contains()ArrayList O(n)时间O(n)}和nodeStorage空间(用于存储O(n)),而#34;更复杂&#34;解决方案是O(1)时间和O(n)空间。

本书为感兴趣的人提供了以下解决方案,即O(1)时间和LinkedListNode FindBeginning(LinkedListNode head) { LinkedListNode n1 = head; LinkedListNode n2 = head; // Find meeting point while (n2.next != null) { n1 = n1.next; n2 = n2.next.next; if (n1 == n2) { break; } } // Error check - there is no meeting point, and therefore no loop if (n2.next == null) { return null; } /* Move n1 to Head. Keep n2 at Meeting Point. Each are k steps /* from the Loop Start. If they move at the same pace, they must * meet at Loop Start. */ n1 = head; while (n1 != n2) { n1 = n1.next; n2 = n2.next; } // Now n2 points to the start of the loop. return n2; } 空间:

  

如果我们移动两个指针,一个速度为1,另一个速度为2,   如果链表有一个循环,它们将结束会议。为什么?认为   大约两辆汽车在赛道上行驶 - 速度更快的汽车将永远通过   慢一点!这里棘手的部分是找到循环的开始。   想象一下,作为一个类比,两个人围着一个赛道跑,一个跑   比另一个快两倍。如果他们在同一个地方开始,那么   他们下次会见吗?接下来他们将在下一圈开始时见面。   现在,让我们假设Fast Runner在n上有一个k米的起点   一圈。他们什么时候下次见面?他们将在之前达到k米   下一圈的开始。 (为什么?快跑者会做出k + 2(n - k)   步骤,包括它的开头,慢跑者会做n - k   脚步。在循环开始之前,两者都将是k步。)现在,继续   回到问题,当Fast Runner(n2)和Slow Runner(n1)是   在我们的循环链表中移动,n2将有一个良好的开端   n1进入时的循环。具体来说,它将以k为首,   其中k是循环之前的节点数。由于n2有头   k个节点的开始,n1和n2将在开始之前满足k个节点   环。所以,我们现在知道以下内容:   1. Head是来自LoopStart的k个节点(根据定义)。   2. MeetingPoint for n1和n2是来自LoopStart的k个节点(如上所示)。因此,如果我们将n1移回Head并在MeetingPoint保持n2,   并以相同的速度移动它们,它们将在LoopStart会面。

{{1}}

答案 1 :(得分:0)

amit给出了解决方案。问题是你要么知道要么不知道,但是你不能在面试中弄明白。由于我从不需要在链表中找到一个循环,除了通过面试之外,了解它对我来说毫无意义。所以对于面试官来说,这是一个面试问题,并期待amir的回答(这很好,因为它有线性时间和零额外空间),这是非常愚蠢的。

所以你的解决方案很好,除了你应该使用哈希表,你必须确保哈希表哈希对节点的引用而不是节点。假设您有一个包含字符串的节点和一个&#34; next&#34;如果字符串相等,则指针和散列函数散列字符串并将节点比较为相等。在这种情况下,除非您小心,否则您将找到第一个带有重复字符串的节点,而不是循环开始时的节点。

(amir的解决方案在语言中有一个非常类似的问题,其中==比较对象,而不是引用。例如在Swift中,你必须使用===(比较引用)和不是==(比较对象))。

答案 2 :(得分:0)

我无法直观地看到这个算法发生了什么。希望这有助于其他人。

在时间t = k(3),p2是距离头部(0)的距离的两倍,因此为了使它们重新排成一行,我们需要p2来“追赶”到p1并且需要L - k(8)再发生5个步骤。 p2的速度是p1的2倍。

在时间t = k +(L-k)(8),p2需要向前行进k步以返回k。如果我们将p1重置回头部(0),我们知道如果p2以与p1相同的速度行进,则p1和p2都将在k(3,19分别)处相遇。

Linked List graphic