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

C 如何在不使用临时节点的情况下在单链表中插入新节点?,c,pointers,data-structures,singly-linked-list,C,Pointers,Data Structures,Singly Linked List,我刚刚开始学习数据结构,并一直在尝试用C逐步实现一个简单的单链表。我一直在关注一个在线教程,在演示如何将节点插入列表的开头时,它在函数参数中使用了双星号,我无法理解,教程也没有详细解释 然后我解决了这个问题,在没有双指针的情况下实现了这个函数(通过使用临时节点),但我很想了解和学习我看到的实现是如何工作的,以及我自己是如何实现的 我最近自学了指针,所以我不能说我对它们很满意。事实上,这就是我开始学习数据结构的原因之一,以便更多地实践它们。我通常理解指针的解引用是如何工作的,但在这种情况下,我不确

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

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

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

如果您能解释一下以下功能的工作方式,我将不胜感激

这是我无法完全理解的函数

以下是我的实现:

/*
// 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);

}
/*
//单链表的实现
// 2018/01/10
*/
#包括
#包括
类型定义结构节点{
int-val;
结构节点*下一步;
}节点t;
void addNodeBegin(节点头,int val);
void addNodeEnd(节点头,int val);
void printLinkedList();
无效自由列表(结构节点*头部);
内部主(空){
节点头=空;
水头=malloc(节点的尺寸);
如果(head==NULL){return 1;}
head->val=1;
head->next=NULL;
head->next=malloc(sizeof(node_t));
head->next->val=2;
head->next->next=NULL;
Addnodeen(头,5);
addNodeBegin(头,10);
打印链接列表(头);
自由名单(标题);
}
//将节点插入到链接列表的开头
void addNodeBegin(节点头,int val){
node_t*newNode=NULL;
newNode=malloc(sizeof(node_t));
newNode->val=val;
新建节点->下一步=头部->下一步;
head->next=newNode;
node_t*tmp=NULL;
tmp=malloc(sizeof(node_t));
tmp->val=头部->val;
head->val=newNode->val;
head->next->val=tmp->val;
免费(tmp);
}
//将节点插入到链接列表的末尾
void addNodeEnd(节点头,int val){
节点_t*电流=头部;
while(当前->下一步!=NULL){
当前=当前->下一步;
}
当前->下一步=malloc(sizeof(node_t));
当前->下一步->val=val;
当前->下一步->下一步=空;
}
//打印链接列表
无效打印链接列表(节点头){
节点_t*电流=头部;
while(当前!=NULL){
printf(“%d\n”,当前->值);
当前=当前->下一步;
}
}
//删除列表(并释放占用的内存)
无效自由列表(结构节点*头){
结构节点*tmp;
while(head!=NULL){
tmp=头部;
头部=头部->下一步;
免费(tmp);
}
}

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

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


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

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

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

/*
// 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”是construct的缩写,这是一个传统的名称,用于以这种方式构造单链接列表。

将双星号视为双指针或对指针的引用。。。。问题基本上是,在C语言中,不能向函数传递变量参数(或通过引用传递参数),因此,唯一的方法是显式传递要修改的指针的地址。这就是第二个星号的原因,它使对引用指针所做的修改对应用程序可用。这两个层次的参考都有不同的含义。。。不能将要更新的变量作为参数传递给函数,但可以传递其地址。传递双指针可以实现这一点,您可以使用以下内容更新指针:

*ptr_reference = &node_to_be_referenced;
function_to_be_called(&pointer_to_first_node, node_to_be_inserted);
调用函数时,向其传递对要修改的指针的引用,如:

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

(请参见指针的地址是如何引用要修改的指针的)

您基本上需要一个关于指针的教程。堆栈溢出是n