如何只用一个指针实现双链表?

时间:2009-11-18 10:29:02

标签: algorithm data-structures

如何使用一个指针实现双链表?

找到prev和next节点需要O(1)时间。

struct Node
{
   int val;
   Node* p;
};

6 个答案:

答案 0 :(得分:13)

这听起来似乎不可能,就像它说的那样。通常,您不能仅使用一个指针来实现两个指针。

您可能能够将两个16位偏移量压缩到单个(假定的32位)指针所使用的空间中,或者其他一些“聪明的黑客”,但通常这听起来不可能。

This article描述了一个基于XOR的技巧:指针值,但我认为这是一个hack(它对指针值进行逐位算术)。

答案 1 :(得分:10)

有一个经典的黑客:存储2个指针(上一个和下一个)的XOR,当你在列表中旅行时,你手边总有一个(你刚从那里来)你可以用存储值对它进行异或得到另一个指针。

毋庸置疑,这在GC环境中无效。

答案 2 :(得分:9)

也许使用XOR linked list

答案 3 :(得分:7)

已经提出的一个解决方案是XOR solution

另一种解决方案是“翻转侧”解决方案: 如果你的问题用以下方式表达:

您将获得指向第一个元素的指针,并且您希望:

  1. 按照O(i)
  2. 中的步骤进入链表
  3. 返回链接列表中的步骤O(i)
  4. 在O(1)
  5. 的当前位置添加或删除项目

    因此总是只有一个指向链表的指针,并且只有一个入口点(只需返回和转发,如1和2),您可以执行以下操作:

    • 保存两个指示: p1 p2
    • 从第一个指针 p1 ,您可以返回,从第二个指针 p2 开始。
    • p1 之前的链接列表项向后,而 p2 之后的项目指向前方。

    所以你的清单看起来像这样:

                      p1 p2
                      |  |
                      V  V
    i1 <- i2 <- i3 <- i4 i5 -> i6 -> i7
    

    p1指向当前元素,p2指向下一个元素,i1 ... i7是列表中的项目

    前进是在O(1)中完成的,因此通过翻转指针向后移动:

    Forward one step:
                            p1 p2
                            |  |
                            V  V
    i1 <- i2 <- i3 <- i4 <- i5 i6 -> i7
    
    
    Backward one step: 
                p1 p2
                |  |
                V  V
    i1 <- i2 <- i3 i4 -> i5 -> i6 -> i7
    

    这种解决方案在可读性方面优于XOR解决方案,对人类来说更容易理解。缺点是您不能在链接列表中有多个入口点。

答案 4 :(得分:1)

如果sizeof(int) == sizeof(Node *)有一个包含后向指针的中间节点。

E.g。

(real node) -> (intermediate node) -> (read node) -> (etc)

其中(real node)包含一个值和一个指向以下(intermediate node)的指针,(intermediate node)在val中包含一个指向前一个中间节点的后向指针,以及一个指向下一个{前向指针的转发指针{ {1}}。

顺便说一下,这是一个愚蠢而愚蠢的问题。我无法看到它传授任何有价值的东西。

答案 5 :(得分:1)

我最近在Niklaus Wirth的一本书中找到了解决这个问题的好方法(&#34; Algorithms + Data Structures = Programs&#34;)。它是这样的...你有一个节点,类似于你建议的节点,除了它没有聚合指向下一个(也不是前一个)Node的指针。相反,您有一个成员link,表示从链中的前一个节点(由sizeof(Node)指向)到下一个节点(指向的距离(例如,以Node* pPrev为单位)通过链中的Node* pNext):

size_t link = pNext - pPrev;

所以节点看起来像这样:

struct Node {
    int val;
    size_t link;
}

然后,通过写一下,从当前节点pCurrent与前一个节点pPrev结合,前往下一个Node* pNext

pNext = pPrev + pCurrent->link;

同样,你可以通过重新排列这个等式来反向移动:

pPrev = pNext - pCurrent->link;

然而,这种方法在某种程度上受到C / C ++指针算法的限制,因为如果两个指针都指向同一个内存块内部,那么两个指针的差异就很明确。基本上,所有节点都必须包含在一个巨大的Node s。

数组中