【问题标题】:Having trouble understanding the definition of a linked list无法理解链表的定义
【发布时间】:2019-04-11 21:34:44
【问题描述】:

我对链接列表的定义有一些疑问,因为它是在我的课堂上定义的。

这是使用的:

typedef struct node_t {
int x;
struct node_t *next;
} *Node;

现在,我了解到,通过这种方式,我们创建了一种更短的方法来使用指向结构 node_t 的指针。 Node 将用作struct node_t*

现在,假设我们要创建一个链表。例如:

Node node1 = malloc(sizeof(*node1));
Node node2 = malloc(sizeof(*node2));
Node node3 = malloc(sizeof(*node3));
node1->x = 1;
node1->next = node2;
node2->x = 4;
node2->next = node3;
node3->x = 9;
node3->next = NULL;

这大概是我的想象(圆圈代表结构):

现在我知道这是错的,但我不明白为什么。我们有一个指针node1,它指向我们的结构。然后,我们指向node2,它指向另一个结构等等。

另一件事是,我无法理解图片中的箭头怎么可能更长。我们不应该只能从圆的每个下部指向一个结构,而不是指向一个结构的指针吗?这怎么可能?

如果这里有人能让事情变得更清楚一点,将不胜感激。非常感谢。

【问题讨论】:

  • Node 定义为指针类型确实会引起混淆。在 C 中,约定是定义普通类型并让指针显而易见,可以是 Node*(指针),也可以是 Node_ptr 等简写形式。这是因为The Principle of Least Surprise.

标签: c pointers struct linked-list


【解决方案1】:

您有三个链接节点,以及指向它们的附加本地指针。
尽管使用它们的名称来引用节点通常很方便,但节点对这些本地指针一无所知。
相反,它们知道序列中的下一个节点,而最后一个节点不知道。

换句话说,你的图像完全错误。

          +---+------+
node1 --> | 1 | next |
          +---+-|----+
                |
                v
          +---+------+
node2 --> | 4 | next |
          +---+-|----+
                |
                v
          +---+------+
node3 --> | 9 | NULL |
          +---+------+

【讨论】:

  • 这区分node1恰好是一个指针的变量和它指向的数据。
  • 非常感谢,我有两个问题。首先,我根本无法理解为什么 next 不会指向 next 指针。如果是类似 node1->next = *(node2);我会理解,但事实并非如此。其次,如果这就是它的样子,为什么我们首先需要指向每个结构的指针?
  • node1->next = node2存储在node2标识的对象中的值复制到node1->next标识的对象中。这类似于int x = 1, y; y = x; 不会使y 成为x 的别名——它们仍然是不同的变量,但它们包含相同的值。在指针的情况下,你最终会得到两个指针对象,它们的值都指向同一个对象,而不是一个指向另一个对象,或者在某种程度上等同于另一个对象。
  • 我想我明白了,感谢您的帮助。
【解决方案2】:

赋值是传递操作。所以,

node1->next = node2;

意味着node1->next 指向node2 所指向的任何东西。而且,特别是,node1->next 并不指向 node2 本身。

node1node2node3 中的每一个都将变量命名为指针。

node1       node2       node3
+---+       +---+       +---+
| * |       | * |       | * |
+ | +       + | +       + | +
  v           v           v
+---+---+   +---+---+   +---+---+
| 1 | * --> | 4 | * --> | 9 | * --> NULL
+---+---+   +---+---+   +---+---+

【讨论】:

  • 我不明白为什么使用 node1->next = node2; 是正确的而不是 node1->next = *(node2);
  • *node2 在指针上应用解引用运算符。这是获取对象值的方式,而不是指针所包含的地址。
  • 或者换个角度看,node1->next的类型是struct node_t *。这与node2的类型相同,但与*node2的类型不同。
【解决方案3】:
typedef struct node_t {
    int x;
    struct node_t *next;
} *Node;                   /* <-- don't typedef pointers */

只需使用Node 而不是Node *,然后分配:

Node *node1 = malloc(sizeof(*node1));

为什么?在typedef 声明下方查看您的代码 100 行的人不会天生就知道 Node 是否是一个类型,或者它是否是一个指向类型的指针。这种类型的混淆只会随着代码大小的增加而增加。审核:Is it a good idea to typedef pointers?.

注意:很好地使用解引用的指针来设置sizeof中的类型大小)

链接列表

链表只是一种巧妙的数据结构,它允许您遍历多个独立分配的节点。每个节点包含一些data,然后是指向列表中下一个节点的指针,如果该节点是列表中的最后一个节点,则为NULL

(对于双向链表,您只需添加一个prev 指针,该指针也指向列表中当前节点之前的节点。您还有最后一个节点指向的 循环 列表回到第一个允许从列表中的任何节点迭代到任何其他节点的迭代,无论您从哪个节点开始迭代。对于双向循环列表,您可以从任何节点在两个方向上迭代整个列表)

在你的情况下,你的清单很简单:

     node1    +-> node2    +-> node3
    +------+  |  +------+  |  +------+
    | data |  |  | data |  |  | data |
    |------|  |  |------|  |  |------|
    | next |--+  | next |--+  | next |---> NULL
    +------+     +------+     +------+

您的data 是一个整数值,而您的next 指针仅保存列表中下一个节点的地址,或者NULL 如果它是列表中的最后一个节点。添加您的数据,您的列表将是:

     node1    +-> node2    +-> node3
    +------+  |  +------+  |  +------+
    |   1  |  |  |   4  |  |  |   9  |
    |------|  |  |------|  |  |------|
    | next |--+  | next |--+  | next |---> NULL
    +------+     +------+     +------+

创建列表时,第一个节点通常称为列表的head,最后一个节点称为列表的tail。您必须始终保留指向列表头部的指针,因为该指针包含起始列表地址。为了有效地插入列表,保留一个指向 tail 节点的指针也是一个好主意,这样您就可以简单地插入新节点,而无需每次遍历列表以找到最后一个节点,例如:

Node *newnode = malloc(sizeof(*newnode));   /* allocate */
newnode->next = NULL;                       /* initialize next NULL */
tail->next = newnode;                       /* assign to tail */
tail = newnode;                             /* set new tail at newnode */

列表是 C 语言的基础,在 Linux 内核本身中有很多使用。花时间理解它们以及如何用不同的变体编写它们。你会很高兴你做到了。最后,别忘了写一个简单的函数来在你完成后释放你的列表(如果分配了data,也释放它)。一个简单的 free_list 函数是:

void free_list (Node *list) 
{
    while (list) {
        Node *victim = list;    /* separate pointer to node to free    */
        list = list->next;      /* can you see why you iterate next... */
        free (victim);          /* before you free the victim node?    */
    }
}

如果您还有其他问题,请告诉我。

【讨论】:

    猜你喜欢
    • 2017-04-25
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-01
    • 1970-01-01
    • 2019-08-22
    • 2022-01-12
    相关资源
    最近更新 更多