c - 如何在不使用临时节点的情况下在单链表中插入新节点?

标签 c pointers data-structures singly-linked-list

我刚开始学习数据结构,一直在尝试用 C 语言逐步实现一个简单的单向链表。我一直在关注一个在线教程,在展示如何将节点插入到列表的开头时,它在我无法理解的函数参数中使用了双星号,教程也没有详细解释。

然后我解决了这个问题并在没有双指针的情况下实现了该功能(通过使用临时节点),但我很想了解和学习我看到的实现是如何工作的以及我自己是如何实现它的。

我最近自学了指针,所以我不能说我对它们很满意。实际上,这就是我开始学习数据结构并进行更多练习的原因之一。我通常理解取消引用指针的工作原理,但在这种情况下,我不确定它在函数头中的使用方式和目的。

如果您能解释下面函数的工作方式,我将不胜感激。

这是我不太了解的功能。 (source)

void push(node_t ** head, int val) {
    node_t * new_node;
    new_node = malloc(sizeof(node_t));

    new_node->val = val;
    new_node->next = *head;
    *head = new_node;
}

这是我的实现:

/*
// Singly Linked List Implementation
// 2018/01/10
*/

#include <stdio.h>
#include <stdlib.h>

typedef struct node {
        int val;
        struct node *next;
    } node_t;

void addNodeBegin (node_t *head, int val);
void addNodeEnd (node_t *head, int val);
void printLinkedList();
void freeList(struct node* head);

int main(void) {

    node_t *head = NULL;
    head = malloc(sizeof(node_t));

    if (head == NULL) { return 1; }

    head->val = 1;
    head->next = NULL;

    head->next = malloc(sizeof(node_t));
    head->next->val = 2;
    head->next->next = NULL;

    addNodeEnd(head, 5);
    addNodeBegin(head, 10);
    printLinkedList(head);

    freeList(head);

}

// Insert a node to the beginning of the linked list
void addNodeBegin (node_t *head, int val) {
    node_t *newNode = NULL;
    newNode = malloc(sizeof(node_t));
    newNode->val = val;

    newNode->next = head->next;
    head->next = newNode;

    node_t *tmp = NULL;
    tmp = malloc(sizeof(node_t));

    tmp->val = head->val;
    head->val = newNode->val;
    head->next->val = tmp->val;

    free(tmp);

}

// Insert a node to the end of the linked list
void addNodeEnd (node_t *head, int val) {
    node_t *current = head;

    while (current->next != NULL) {
        current = current->next;
    }

    current->next = malloc(sizeof(node_t));
    current->next->val = val;
    current->next->next = NULL;

}

// Print the linked list
void printLinkedList(node_t *head) {
    node_t *current = head;

    while (current != NULL) {
        printf("%d\n", current->val);
        current = current->next;
    }
}

// Remove the list (and free the occupied memory)
void freeList(struct node* head) {
   struct node* tmp;

   while (head != NULL) {
       tmp = head;
       head = head->next;
       free(tmp);
    }
}

最佳答案

嗯,不做某事的最简单方法就是不去做。

通过您的实现追踪事物的值(value):

// Insert a node to the beginning of the linked list
void addNodeBegin (node_t *head, int val) { // assume head = [val1, ...]
    node_t *newNode = NULL;
    newNode = malloc(sizeof(node_t));
    newNode->val = val;                     // newNode=[val, null]

    newNode->next = head->next;             // newNode=[val, ...]
    head->next = newNode;                   // head = [val1, [val, ...]]

    node_t *tmp = NULL;
    tmp = malloc(sizeof(node_t));

    tmp->val = head->val;                   // tmp = [val1, undefined]
    head->val = newNode->val;               // head = [val, [val, ...]]
    head->next->val = tmp->val;             // head = [val, [val1, ...]]

    free(tmp);

}

首先,temp 唯一做的就是存储 head->val 的副本,因此您可以通过在覆盖 head->val 之前设置 newNode->val 来直接存储值:

// Insert a node to the beginning of the linked list
void addNodeBegin (node_t *head, int val) { // assume head = [val1, ...]
    node_t *newNode = malloc(sizeof(node_t)); // no need to set to NULL first
    newNode->val = head->val;               // newNode=[val1, undefined]
    newNode->next = head->next;             // newNode=[val1, ...]
    head->next = newNode;                   // head = [val1, [val1, ...]]
    head->val = val;                        // head = [val, [val1, ...]]
}

其次,这与在列表开头添加节点不同。它在开始后插入一个节点,然后交换第一个和第二个节点之间的值。这很重要,因为非常普遍的单向链表有多个头部,无论是出于性能原因还是为了实现特定算法,因此操作在添加到列表之前时不应更改列表的尾部。 这也意味着您必须进行两种不同的操作 - 附加到空列表 (NULL) 和附加到非空列表。

push() 的示例实现采用指向列表头部指针的指针,创建一个新节点并改变该指针以指向新节点。这似乎比必要的更难使用,因为它需要获取指向头部的指针的地址。

node_t* head = NULL;

push ( &head, 1 );
push ( &head, 2 );
push ( &head, 3 );
push ( &head, 4 );

node_t* head2 = head;

push ( &head2, 5 );

我个人只是返回指向新节点的指针:

node_t* cons (int val, node_t* next) {
    node_t * new_node = malloc(sizeof(node_t));

    new_node->val = val;
    new_node->next = next;

    return new_node;
}

node_t* head = cons(4, cons(3, cons(2, cons(1, NULL))));
node_t* head2 = cons(5, head); 

'Cons' 是 construct 的缩写,以这种方式构造单链表的传统名称。

关于c - 如何在不使用临时节点的情况下在单链表中插入新节点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48188418/

相关文章:

c - 我在分配内存时遇到问题。(也许)

c++ - C 预处理文件中这些不熟悉的行是什么?

c - 在 Linux 中使用 c 私有(private)内存使用量不断增加

c++ - C++ 程序中的指针大小

c++ - 删除动态 vector 数组

python - 3D 数据的最佳数据结构?

java - 在 Java 中处理大型字符串列表

c - 用于获取输入的 If 循环在 C 中不起作用

c - mmap(),内存驻留在虚拟空间的哪里?

c++ - 存储和重用 vector 迭代器值是否安全?