【问题标题】:inserting a node at the end of linked list recursivly递归地在链表的末尾插入一个节点
【发布时间】:2016-10-11 13:39:01
【问题描述】:
typedef struct node
{
    int a;
    struct node *next;
}node;
void generate(struct node **head)
{
    int num = 10, i; //the num here is the length
    struct node *temp;

    for (i = 0; i < num; i++)
    {
        temp = (struct node*)malloc(sizeof(struct node));
        temp->a = 10-i;
        if (*head == NULL)
        {
            *head = temp;  //each time add another node to the start
            (*head)->next = NULL;
        }
        else
        {
            temp->next = *head;
            *head = temp;
        }
    }
}
void addSpecific(node* head,int n)
{
    node* temp = NULL;
    if (head->next == NULL)
    {
        temp = (node*)malloc(sizeof(node*)); //allocating memory
        (temp)->a = n;  //adding the wanted value
        (temp)->next = NULL; //making the new node to point to the end
        head->next = temp; //and the previous one to point to temp
    }
    else
    {
        addSpecific(head->next, n); //if this is not the wanted node we need to move to the next node
    }
}
void deleteNode(struct node **head)
{
    struct node *temp;
    while (*head != NULL)
    {
        temp = *head;
        *head = (*head)->next; //going to the next node
        free(temp); //free the allocated memory
    }
}
int main()
{
    struct node *head = NULL;

    generate(&head);
    addSpecific(head, 7);
    display(head);
    deleteNode(&head);
    system("PAUSE");
    return 0;
}

我试图在末尾使用递归插入新节点,但是空闲内存(删除)函数进行了扩展,我找不到问题。我尝试了生成函数并在最后添加节点,它工作但编译器提醒我“堆损坏”。

【问题讨论】:

  • 分配head-&gt;next = &amp;temp 并没有按照您的想法进行。如果您没有从该行收到编译器警告,则需要启用更多警告。至于为什么错了,想想head-&gt;next是什么类型,temp是什么类型,最后&amp;temp是什么类型?
  • 没有直接关系,但在这里使用递归是个糟糕的主意。
  • 老师告诉我的,我别无选择
  • 也不相关:不要写(temp)-&gt;a,你应该写temp-&gt;a,这样更标准,纯粹是装饰性的,生成的代码是一样的。
  • 想想如果列表有几千个节点会发生什么......所有普通的标准计算机和编译器都使用堆栈进行函数调用,并且堆栈是有限的资源(Windows 上的默认堆栈大小使用 Visual Studio 编译器是一个兆字节)。

标签: c pointers recursion linked-list singly-linked-list


【解决方案1】:

函数可以如下所示,如下面的演示程序所示

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

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

void addSpecific( node **head, int n )
{
    if ( *head == NULL )
    {
        *head = malloc( sizeof( node ) ); //allocating memory
        ( *head )->a    = n;  //adding the wanted value
        ( *head )->next = NULL; //making the new node to point to the end
    }
    else
    {
        addSpecific( &( *head )->next, n ); //if this is not the wanted node we need to move to the next node
    }
}

void display( node *head )
{
    for ( ; head != NULL; head = head->next ) printf( "%d ", head->a );
    printf( "\n" );
}

int main( void ) 
{
    const int N = 10;
    node *head = NULL;

    for ( int i = 0; i < N; i++ ) 
    {
        addSpecific( &head, i );

        display( head );
    }

    return 0;
}

程序输出是

0 
0 1 
0 1 2 
0 1 2 3 
0 1 2 3 4 
0 1 2 3 4 5 
0 1 2 3 4 5 6 
0 1 2 3 4 5 6 7 
0 1 2 3 4 5 6 7 8 
0 1 2 3 4 5 6 7 8 9 

至于函数deleteNode则可以看如下方式

void deleteNode( node **head )
{
    for ( node *current = *head; current != NULL; )
    {
        node *temp  = current;
        current = current->next; //going to the next node
        free( temp ); //free the allocated memory
    }

    *head = NULL;
}

至于这个函数的实现

void deleteNode( node **head )
{
    while ( *head != NULL )
    {
        node *temp  = *head;
        head = &( *head )->next; //going to the next node
        free( temp ); //free the allocated memory
    }
}

然后它具有未定义的行为,因为它试图访问已被删除的结构对象的数据成员。

或者你可以使函数递归。例如

void deleteNode( node **head )
{
    if ( *head != NULL )
    {
        deleteNode( &( *head )->next );
        free( *head );
        *head = NULL;
    }
}

【讨论】:

  • 你能解释一下显示功能中的for吗?因为我不明白它的工作方式。当我编译这段代码时,输​​出是 0 1 2 3 4 5 6 7 8 9
  • @TalSokolinsky 我认为这是所示代码中最简单的部分。:) for 循环只是遍历列表,直到下一个节点等于 NULL。你可以把函数写成递归的。
  • @TalSokolinsky 我认为如果你为 ( node *tmp = head; tmp != NULL; tmp = tmp->next ) printf( "%d ", tmp->a);
  • 为什么当我尝试释放内存时(通过使用问题中的函数 deleteNode())会导致堆损坏扩展而在您的代码中却没有?
  • @TalSokolinsky 你必须写 head = &( *head )->next;
【解决方案2】:

有点跑题了,但是用两个指针而不是一个指针来维护单链表是很方便的。这样,您可以在 O(1) 中添加和添加列表,而无需使用递归。例如:

struct Node
{
    struct Node* next;
};

struct List
{
    struct Node *head, **tail;
};

void List_init(struct List* list) {
    list->head = 0;
    list->tail = &list->head;
}

void List_append(struct List* list, struct Node* node) {
    node->next = 0;
    *list->tail = node;
    list->tail = &node->next;
}

void List_prepend(struct List* list, struct Node* node) {
    node->next = list->head;
    list->head = node;
    if(list->tail == &list->head)
        list->tail = &node->next;
}

void List_remove(struct List* list, struct Node* node) {
    struct Node *cur = list->head, **prev = &list->head;
    while(cur && cur != node) {
        prev = &cur->next;
        cur = cur->next;
    }
    if(cur) {
        if(!(*prev = node->next))
            list->tail = prev;
    }
}

【讨论】:

  • @MaximEgorushkin 我被要求做一个递归函数(我的老师),但感谢迭代解决方案:)
【解决方案3】:
# For reference:
#
# SinglyLinkedListNode:
#     int data
#     SinglyLinkedListNode next


def insertNodeAtTail(node, data):
    if node is None:
        last = SinglyLinkedListNode(node_data=data)
        last.next = None
        return last
    
    if node.next is None:
        last = SinglyLinkedListNode(node_data=data)
        last.next = None
        node.next = last
        return node
    
    node.next = insertNodeAtTail(node.next, data)

    return node

【讨论】:

    猜你喜欢
    • 2020-11-15
    • 2015-02-08
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 2013-11-13
    相关资源
    最近更新 更多