【问题标题】:Linked List removeNode C Programming链表 removeNode C 编程
【发布时间】:2015-10-17 13:06:06
【问题描述】:

我在 ListNode 和 LinkedList 之间有些混淆。基本上我的问题分为两部分。对于第一部分,我应该使用 ListNode。函数原型如下:

int removeNode(ListNode **ptrHead, int index);

ListNode 部分的所有功能都运行良好。然后至于第二部分,我应该把上面的函数改成这样:

int removeNode(LinkedList *11, int index);

我的第 1 部分代码运行良好,如下所示:

int removeNode(ListNode **ptrHead, int index) {
ListNode *pre, *cur;
if (index == -1)
    return 1;
else if (findNode(*ptrHead, index) != NULL) {
    pre = findNode(*ptrHead, index - 1);
    cur = pre->next;
    pre->next = cur->next;
    return 0;
}
else
    return 1;
}

ListNode *findNode(ListNode *head, int index) {
    ListNode *cur = head;
    if (head == NULL || index < 0)
        return NULL;
    while (index > 0) {
        cur = cur->next;
        if (cur == NULL) return NULL;
        index--;
    }
    return cur;
}

这是我第 2 部分的全部代码,但无法正常工作:

#include "stdafx.h"
#include <stdlib.h>

typedef struct _listnode {
    int num;
    struct _listnode *next;
}ListNode;

typedef struct _linkedlist {
    ListNode *head;
    int size;
}LinkedList;

void printNode2(ListNode *head);
int removeNode2(LinkedList *ll, int index);

int main()
{
    int value, index;
    ListNode *head = NULL, *newNode = NULL;
    LinkedList *ptr_ll = NULL;

    printf("Enter value, -1 to quit: ");
    scanf("%d", &value);
    while (value != -1) {
        if (head == NULL) {
            head = malloc(sizeof(ListNode));
            newNode = head;
        }
        else {
            newNode->next = malloc(sizeof(ListNode));
            newNode = newNode->next;
        }
        newNode->num = value;
        newNode->next = NULL;
        scanf("%d", &value);
    }

    printNode2(head);

    printf("\nEnter index to remove: ");
    scanf("%d", &index);
    removeNode2(ptr_ll, index);
    printNode2(head);

    return 0;
}

void printNode2(ListNode *head) {
    printf("Current list: ");
    while (head != NULL) {
        printf("%d ", head->num);
        head = head->next;
    }
}

int removeNode2(LinkedList *ll, int index) {
    ListNode *head = ll->head;
    if (head == index)
    {
        if (head->next == NULL)
        {
            printf("There is only one node. The list can't be made empty ");
            return 1;
        }

        /* Copy the data of next node to head */
        head->num = head->next->num;

        // store address of next node
        index = head->next;

        // Remove the link of next node
        head->next = head->next->next;

        return 0;
    }


    // When not first node, follow the normal deletion process

    // find the previous node
    ListNode *prev = head;
    while (prev->next != NULL && prev->next != index)
        prev = prev->next;

    // Check if node really exists in Linked List
    if (prev->next == NULL)
    {
        printf("\n Given node is not present in Linked List");
        return 1;
    }

    // Remove node from Linked List
    prev->next = prev->next->next;

    return 0;
}

当我尝试运行第 2 部分时,cmd 只是没有响应,过了一会儿,它就自行关闭了,我不知道哪个部分出了问题。我在想我是在正确的轨道上还是整个 LinkedList 部分都错了?

当我尝试在调试模式下运行时,弹出以下错误消息:

Exception thrown at 0x01201FD1 in tut.exe: 0xC0000005: Access violation reading location 0x00000000.

If there is a handler for this exception, the program may be safely continued.

提前致谢。

【问题讨论】:

  • 这不是软件开发的工作方式。你能想象如果每次一个程序不工作时,开发人员将整个程序上传到堆栈溢出并请求“帮助”?您需要立即学习基本的调试技能。从简单的调试消息开始,让您知道发生了什么。如果这不起作用,是时候启动调试器了。
  • 我粘贴了整个代码,因为我在 ListNode 和 LinkedList 之间有些混淆。我在想它们是否相同,因为结构完全相同。从笔记中,我被告知我必须创建另一个 C 结构,它是 LinkedList,所以我在想我是否走在正确的轨道上,因为它看起来有点奇怪。
  • 您可能希望重新编译代码并启用警告。节点本身与您应该查看的列表索引之间存在一些不匹配。当它是NULL 时,您也可以用ptr_ll 调用removeNode2。这是ptr_ll的第一次使用,所以它不可能知道之前插入的节点。列表结构的全部意义在于您不需要单独的头,因为头是列表结构的一部分。
  • ListNode 保存实际数据。在一个真正的程序中,它可能不仅仅包含一个整数。 LinkedList 是数据结构的根。
  • @MOehm 例如?您是否介意指出错误,以便我可以抢先一步。我在想是不是因为我将值存储到 ListNode 并传递了一个 NULL LinkedList 并导致了问题?

标签: c linked-list nodes


【解决方案1】:

你说你让链表在链表只通过头指针定义的情况下工作。在这个设置中,当列表可能被更新时,你必须传递一个指向头指针的指针,而当你只检查列表而不修改时,你必须传递一个头指针,例如:

int removeNode(ListNode **ptrHead, int index);
ListNode *findNode(ListNode *head, int index);

这里,头指针是客户端代码可见的列表句柄。

使用列表结构的方法为链表定义了一个新接口。虽然头节点就足够了,但可能还需要跟踪尾节点以便于附加或节点数量。这些数据可以是链表结构中的捆绑包。

这意味着节点的处理留给链表,客户端代码只使用链表结构,例如:

typedef struct ListNode ListNode;
typedef struct LinkedList LinkedList;

struct ListNode {
    int num;
    ListNode *next;
};

struct LinkedList {
    ListNode *head;
    ListNode *tail;
    int size;
};

void ll_print(const LinkedList *ll);
void ll_prepend(LinkedList *ll, int num);
void ll_append(LinkedList *ll, int num);
void ll_remove_head(LinkedList *ll);

int main()
{
    LinkedList ll = {NULL};

    ll_append(&ll, 2);
    ll_append(&ll, 5);
    ll_append(&ll, 8);
    ll_print(&ll);

    ll_prepend(&ll, 1);
    ll_prepend(&ll, 0);
    ll_print(&ll);

    ll_remove_head(&ll);
    ll_print(&ll);

    while (ll.head) ll_remove_head(&ll);

    return 0;
}

还有一个区别:在头节点设置中,头节点可能为空。在这里,列表不能为空,它必须存在。 (但它的头和尾成员可以为空。)这里的列表是在堆栈上分配的,它的地址&amp;ll 必须传递给函数。

在链表设置中,修改和只读访问的区别是通过const关键字来完成的:

void ll_print(const LinkedList *ll);
void ll_prepend(LinkedList *ll, int num);

在您的示例中,您采用了两种独立结构的混合方法,即头节点和列表。那是行不通的,一个列表由一个结构描述,选择一个。

链表结构方法的优点是所有必需的数据,如头、尾和大小,总是一起传递给函数。您还可以通过不公开结构成员来向用户隐藏实现,以便用户只能处理指向该结构的指针。

最后,这里有一个上述接口的示例实现供您使用:

void ll_print(const LinkedList *ll)
{
    ListNode *node = ll->head;

    while (node != NULL) {
        printf("%d ", node->num);
        node = node->next;
    }

    putchar('\n');
}

void ll_prepend(LinkedList *ll, int num)
{
    ListNode *nnew = malloc(sizeof *nnew);

    nnew->next = ll->head;
    nnew->num = num;

    ll->head = nnew;
    if (ll->tail == NULL) ll->tail = ll->head;
    ll->size++;
}

void ll_append(LinkedList *ll, int num)
{
    ListNode *nnew = malloc(sizeof *nnew);

    nnew->next = NULL;
    nnew->num = num;

    if (ll->tail == NULL) {
        ll->tail = ll->head = nnew;
    } else {
        ll->tail->next = nnew;
        ll->tail = nnew;
    }
    ll->size++;
}

void ll_remove_head(LinkedList *ll)
{
    if (ll->head) {
        ListNode *ndel = ll->head;

        ll->head = ll->head->next;
        ll->size--;

        free(ndel);
    }
}

【讨论】:

  • 你的解释很好,非常感谢他们,但我不应该改变原型的结构,因为这是一个初学者练习。因此,我根据您的解释将我的代码修改为更简单的版本,pastebin.com/Dr3P6QMp,它现在可以运行。但是,当我尝试根据索引位置删除节点时,它不起作用,它仍在打印整个链表。我在想哪里出了问题。
  • 发布的代码有效吗?您仍然有两个独立的结构:您添加到节点并尝试从列表中删除。这就像在 A 银行拥有一个 bakning 帐户并试图从 B 银行取款。您还应该启用警告:您传递给删除函数的指针不兼容。您必须(1)创建一个结构列表对象,而不是空指针,并且(2)通过获取其地址将其作为指针传递。你只要做(2)。
猜你喜欢
  • 1970-01-01
  • 2022-01-02
  • 2012-04-21
  • 2019-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多