繁体   English   中英

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

[英]How to insert a new node in a singly linked list without using a temporary node?

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

然后,我解决了这个问题并实现了没有双指针的函数(通过使用临时节点),但我很想了解并了解我看到的实现如何工作以及我如何自己实现它。

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

如果你能解释下面这个函数的工作方式,我将非常感激。

这是我无法得到的功能。 (资源)

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);
    }
}

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

通过实施跟踪事物的价值:

// 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'是构造的缩写,是以这种方式构建单链表的传统名称。

把双星号想象成一个双指针或一个指针的引用....问题基本上是,在C中,你不能传递一个函数一个变量参数(或参数引用),所以,唯一的方法要做的是通过显式传递要修改的指针的地址。 这就是第二个星号的原因,即对应用程序可用的引用指针进行修改。 两个引用级别具有不同的含义...您不能将要更新的变量传递给函数作为参数,但您可以传递其地址。 传递双指针使这成为可能,您可以使用以下内容更新指针:

*ptr_reference = &node_to_be_referenced;

当你调用该函数时,你传递一个对要修改的指针的引用,如:

function_to_be_called(&pointer_to_first_node, node_to_be_inserted);

或者,如果你想传递一个中间指针:

function_to_be_called(&previous_node_of_inserted->next, node_to_be_inserted);

(看看指针的地址如何用于引用要修改的指针)

好吧,如果我理解你的问题,那么:

在push函数中,您实际上替换了内存中的原始节点指针。 例如,如果你有以下列表:1-> 2-> 3-> 5,你有指向1的指针,在带有val 6的push函数之后,列表将包含:6-> 1-> 2-> 3- > 5。 但是您的头节点将指向6(指针的实际地址将被更改。头指针将指向内存中的另一个位置)。

在第二个选项中,您不会更改头部的指针。 您只需更改结构的值(实际上是下一个)。 对于前面的示例,在插入之后,头指针仍将指向内存中的相同位置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM