【问题标题】:linkedlist without self referential structures [closed]没有自引用结构的链表[关闭]
【发布时间】:2019-09-27 10:07:59
【问题描述】:

我正在学习数据结构,在做链表时,我在想除了 C 中的自引用结构之外,是否还有另一种类型的声明来实现链表。

这是我第一次需要在 C 中使用结构,每个人都只使用自引用结构来实现。我想知道是否有其他可能的方式来实现没有自引用结构的链表。

【问题讨论】:

  • C 中没有“引用”。对于链表,“节点”结构将至少有一个自身类型的指针。
  • 我不认为有因为列表必须有一个数据元素“next”指向下一个列表元素,所以“列表指向列表指向...”所以它需要引用自身(也称为递归数据结构)。
  • 有很多方法(我想到的第一个是在@RishikeshRaje 链接的问题中讨论的那个),但我怀疑有任何好的方法。使用自引用结构是一种强大而有用的技术,几乎每个人都同意这是实现链接数据结构(如树和列表)的正确方法。您是出于好奇而问,还是出于某种原因您觉得不能使用自引用结构?
  • @MayurK 在问题中,“自我参照”的含义与您说“自我类型指针”的含义相同。也就是说,结构的定义包括对结构的提及(即,它在声明自身时引用自身)。它显然不是用来表示 C++“参考”。

标签: c data-structures


【解决方案1】:

我在想除了 C 中的自引用结构之外,是否还有其他类型的声明来实现链表。

不行,链表只能通过显式或隐式指针来实现。

使用void* 指针屏蔽这个自引用是可能的,但它只会使代码复杂化,使自引用隐含并且使代码更难阅读。

数组

当然,您可以使用无序数组并通过看起来像链表的内部索引方案将其“排序” - 但它仍然是一个数组,并且不会具有与经典链表相同的行为。

例如,基于数组的链表可能会绑定到预先选择的元素数量(在编译期间选择),当列表未满时会导致更高的内存消耗,并且无法在某个点之后添加元素.

另一方面,如果基于数组的链表是动态的,则在 realloc 期间复制项目可能会带来性能成本(例如,使 O(1) 操作在 O(n) 时执行)。

另一件要考虑的事情是,基于动态数组的链表会导致每次数组更改其地址时指向数据的指针都会失效(realloc)。

【讨论】:

  • 我赞成答案,但你的最后一段是错误的。您可以在摊销 O(1) 中实现分配。毕竟,这就是malloc 所做的。通过数组实现链表实际上是在幕后发生的事情,其中​​“数组”只是堆内存空间。
  • @KonradRudolph,感谢您的评论。主要区别之一是经典链表允许不连续的内存片段,而基于数组的链表需要单个连续内存块。但是,我看不到 realloc 承诺 O(1),因此,我看不出将数组扩展到其初始容量之外将是 O(1)。
  • 对,你的链表大小是有界的。就像每个链表一样:按堆的大小(即可寻址的虚拟内存)。堆和你的数组一样连续。连续列表元素里面不是。
  • @KonradRudolph - 我试图澄清我的陈述,我希望这会有所帮助。我认为与虚拟地址相关的细节变化不大(绝对不是 OP 的问题)。此外,经典链表就像一粒沙子,而基于数组的链表更类似于分页大小的岩石。堆将适合更多的发送(假设分配器开销并不愚蠢)。由于单独的内存管理,经典链表的性能将不同于基于数组的链表的性能。 realloc 优化(避免复制)有帮助,但不要改变它。
【解决方案2】:

你想要的是实现一个带有 3 个键的地图:

  • 当前节点
  • 下一个节点
  • 上一个节点

类似这样的:

struct {
    int currentNode;
    int nextNode;
    int prevNode;
} MapEntry_t;

然后有一个动态(或静态)数组,例如:

struct MapEntry_t myLinkedList[10];

编辑: 我应该澄清一下,整数用作索引而不是指针。

【讨论】:

  • 不,绝对不是...int 不一定允许所有指针类型偏移。例如,在我的 macOS 机器上,int 的长度为 4 个字节,void * 的长度为 8 个字节。使用int,您将丢失信息并可能导致数据结构损坏。
  • @Myst 我想我把你弄糊涂了。这些整数并不打算用作指针。它们只是索引。
  • 你说得对,我对你的回答感到困惑,因为问题是关于 linked list,而不是 array,而你的回答是关于一个具有内部排序结构的无序数组。
  • @Myst OP 试图实现一个没有自引用结构的链表,这有点违背链表的含义,所以它会变得相当混乱。
  • 这就是为什么您的 OP 问题的答案应该是“不,链表只能通过使用指向其自身类型的显式或隐式指针来实现。使用 void* 指针屏蔽这种自引用是可能的,但它只会使代码复杂化,使自引用变得隐式。”
猜你喜欢
  • 1970-01-01
  • 2016-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-06
  • 2013-08-12
相关资源
最近更新 更多