在链表中按顺序插入节点时,为什么要使用双指针?

时间:2019-04-04 07:42:41

标签: c pointers linked-list

<c:if test="${parameter.label == '&lt;year of case&gt;'}" >

对我来说,令人困惑的部分是当您取消引用双指针typedef struct node node; struct node { int data; node *next; }; int insert_asc(node **phead, int data) { node **traser; node *newnode = malloc(sizeof(node)); if (newnode == 0) return 0; newnode->data = data; for (traser = phead; *traser != 0; traser = &(*traser)->next) if (data <= (*traser)->data) break; newnode->next = *traser; *traser = newnode; return 1; } 时。 traser为何持有下一个节点的地址? (*traser)->next到底是什么?

2 个答案:

答案 0 :(得分:0)

您在这里使用双指针来跟踪列表的开头。

如果您在此处使用简单的指针交换了节点,则可能会失去列表中某些节点的地址。

这是因为,如果您要传递一个简单的指针指向列表的开头,那么您将在函数中操作标题地址的副本,因此,当您在函数中交换位置时,​​标题地址仍然会同样,如果将头替换为另一个节点,则在函数修改列表后,旧头之前的所有节点的地址都会丢失。

编辑:pythontutor.com是一个工具,由于其出色的可视化工具,它可以帮助我非常轻松地了解链表的行为,我强烈建议您使用它。

答案 1 :(得分:0)

在发布的代码中将双指针用于两个单独的目的:

  • node **phead:列表的开头通过引用传递,因此如果必须在列表的开头插入新节点,则insert_asc可以对其进行更新。在C中不可能通过引用传递,实现它的惯用方式是将指针传递给要由函数更新的值,因此传递双指针phead

  • node **traser:为避免对空列表和插入列表的开头进行特殊处理,程序员使用指针来跟踪插入新节点的位置。 traser首先指向列表的开头,在这种情况下为phead的值,并且在指向节点之间的链接时进行更新,即当前节点的next成员。确定必须在当前节点之后插入新节点。这是无需特殊情况即可实现插入的一种优雅方式。 C允许使用指针,这在Java或javascript中都是不可能的,因为这些语言没有通用的指针。

但是请注意,在比较指针时,可以使用NULL而不是0来使代码更具可读性:

typedef struct node node;
struct node {
    int data;
    node *next; 
};

int insert_asc(node **phead, int data) {
    node **traser;
    node *newnode = malloc(sizeof(node));
    if (newnode == NULL)
        return 0;
    newnode->data = data;

    for (traser = phead; *traser != NULL; traser = &(*traser)->next) {
        if (data <= (*traser)->data)
            break;
    }
    newnode->next = *traser;
    *traser = newnode;
    return 1;
}

还要注意,将具有给定值data的新节点插入具有相同data值的节点之前。在这种情况下,这没有什么区别,对于具有许多重复项的列表而言,可能会更快一些,但是如果有效负载更加复杂,则此插入方法将实现非稳定排序,而使用<而不是{ {1}}可使排序稳定。

为说明起见,这是替代实现,该实现不使用双指针作为插入点,并且在特殊情况下需要额外的测试:

<=