用于在链表中查找联结的生产代码

时间:2010-02-09 16:36:10

标签: c++ c algorithm linked-list

在一些采访中我被问到了这个问题。

在O(1)空间和线性时间的生产环境中,我被要求编写用于在链表中找到连接的代码(其形式为Y,双臂不一定相等)。 我想出了这个解决方案(我以前见过的地方):

1. Measure lengths of both lists, let them be l1 and l2 
2. Move the pointer of larger list by |(l1-l2)|.
3. Now move together both the pointers, if they point to same location,
that is the junction.
采访者:你的代码将如何处理?

案例1.Y格式链表在结点后最后有循环。
情况2.任何一个输入列表都是循环的,它们不会合并。
情况3.Y格式列表在结点之前有一个循环。

对于案例1,我的答案是:

我将使用两个指针(一个快速和慢速)找到列表中的循环,测量两个指针所遇到的节点的长度然后继续前一个案例。
然而,对于情况2和3,我能够找出没有比检测到循环时优雅退出更好的解决方案(使用双指针技术)。


我相信这个问题有更好的答案。请放下你的:)。

谢谢,

1 个答案:

答案 0 :(得分:4)

采访者(看似)的解释使问题变得困难,以下形状也被视为有效:

A\  _____     A\              ___
  \/     \      \            /   \
   \     /       \           \   /
    +---'         +-------------'
   / P           / P
  /             /
B/            B/

即。有一个交汇点,然后列表循环回到交界处之前或之后的地方。计算列表长度的过程没有直接帮助,因为没有定义循环列表的长度。

首先请注意,循环列表末尾的循环长度可以通过此O(1)内存/ O(n)时间过程计算:

int loop_length(List *n) {
  Node *hare = n, *tortoise = n;
  int phase = 0, cnt = 0;
  while (true) {
    hare=hare->next; hare=hare->next; tortoise=tortoise->next;
    if (hare==tortoise) phase++; 
    if (phase==1) cnt++;
    if (phase==2) return cnt;
  }
}

例如,考虑循环列表

(1)-->(2)-->(3)-->(4)
             |     |
            (6)<--(5)

该算法的工作原理如下(T =龟,H =野兔):

       /--------\
 1--2--3--4--5--6    phase  cnt
 HT                  0      0
    T  H             0      0
       T     H       0      0
          HT         1      1
             T  H    1      2
           H    T    1      3
       T        H    1      4
          HT         2      4 : TERMINATED, cnt=4

现在,如果在形成循环结点的节点之前有X个节点(在示例节点(3)中),即X = 2,并且循环由C个节点组成(在示例中C = 4)当乌龟在X步后第一次进入连接点时,兔子处于位置(2X-X)%C的循环中,即(X%C)(在该例子中,乌龟在2步后进入(3)然后第3只野兔位于L =(2%4 = 2)位置,即在节点(5)中(指数从零开始)。现在将采取(CL-1)步骤使野兔到达乌龟(1因为野兔具有L步的“优势”,这意味着算法直到野兔第一次遇到乌龟的步数

  X + (C - X % C - 1)     ; in the example 2 + (4 - 2 - 1) = 3

C是已知的(由算法计算),并且可以计算总步数(由S表示),即我们有

  S + 1 - C = X - X % C

现在假设野兔作为Q步骤的额外优势,即野兔在算法开始之前先取下一个Q下一个指针;然后当乌龟进入交界点时,野兔就在位置((X + Q)%C),我们得到了

  S + 1 - C = X - (X + Q) % C

现在给出计算从“A”和“B”到公共连接点P的路径长度差异的过程(表示长度a和b以及它们的差异因此ab)(假设a> b,不失一般性)。

首先从起点A运行算法,计算周期长度C并存储步骤数S_A。然后运行它以便乌龟从A开始并且野兔在B 并计算步数S_X。这意味着兔子现在具有(a-b)节点的优点,即

  S_X + 1 - C = a - (a + (a - b)) % C = a - (2a - b) % C

因此

  S - S_X == (a - b)   modulo   C

即。差值给出模C的长度差;通过C计算长度差的商通常从起点B运行算法,得到步数S_B,即全部一起

  S_A + 1 - C = a - a % C
  S_B + 1 - C = b - b % C
  S_X - S_A == (a - b) % C

减去前两个方程式得到

  S_A - S_B = (a - b) + [-1 * (a % C) + b % C]

方括号中的术语是] -C,+ C [,所以

  (S_A - S_B) - C < (a - b) < (S_A - S_B) + C

在这个区间中,最多有两个相等的差(S-S_X)模C;用它们来试图发现连接点---问题解决了。

示例:

 A(1)--(2)
        |
 B(3)--(4)--(5)--(6)
        \_________/

在S_A的计算中,野兔和乌龟在(5)处的3个步骤之后相遇并且返回循环长度3。在S_B的计算中,野兔和乌龟在(6)处的3个步骤之后相遇并且返回循环长度3。对于S_X,野兔进入B和龟在A;他们在(4)的两个步骤后见面。这给了

  0 - 3 < (a - b) < 0 + 3
  (3 - 2) == (a - b)  modulo  3

即。 (a-b)之间的长度差为1模3;这给出了可能的长度差异{-2,+ 1};假设a>&gt;忽略-2。 b,所以我们得到a = b + 1.然后通过从A向前遍历到(2)的第一个+1节点找到连接点,然后以相同的速度从两个臂前进直到找到连接点。

与非共享循环和/或没有循环的情况集成,作为练习给读者。