如何使用一个指针实现双链表?
找到prev和next节点需要O(1)时间。
struct Node
{
int val;
Node* p;
};
答案 0 :(得分:13)
这听起来似乎不可能,就像它说的那样。通常,您不能仅使用一个指针来实现两个指针。
您可能能够将两个16位偏移量压缩到单个(假定的32位)指针所使用的空间中,或者其他一些“聪明的黑客”,但通常这听起来不可能。
This article描述了一个基于XOR的技巧:指针值,但我认为这是一个hack(它对指针值进行逐位算术)。
答案 1 :(得分:10)
有一个经典的黑客:存储2个指针(上一个和下一个)的XOR,当你在列表中旅行时,你手边总有一个(你刚从那里来)你可以用存储值对它进行异或得到另一个指针。
毋庸置疑,这在GC环境中无效。
答案 2 :(得分:9)
也许使用XOR linked list?
答案 3 :(得分:7)
已经提出的一个解决方案是XOR solution。
另一种解决方案是“翻转侧”解决方案: 如果你的问题用以下方式表达:
您将获得指向第一个元素的指针,并且您希望:
因此总是只有一个指向链表的指针,并且只有一个入口点(只需返回和转发,如1和2),您可以执行以下操作:
所以你的清单看起来像这样:
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。