【问题标题】:How to delete a node from circular singly linked list如何从循环单链表中删除一个节点
【发布时间】:2020-04-12 16:57:50
【问题描述】:

我在以递归方式从循环单链表中删除单个节点/值时遇到了一些问题(当然,如果可能的话)。我的代码仅从中间删除,而不是从第一个或最后一个位置删除。

在以递归方式删除其中一个后,我无法弄清楚如何建立连接。我的意思是,如果我要删除第一个元素,那么我需要将最后一个节点连接到下一个节点。

这是我的代码:

Node *ListDelete(Node *list, Node *tail, int val, Node **deleted) {
    if (!list || (list == tail && list->value != val)) {
        *deleted = NULL;
        return list;
    }

    if (list->value == val) {
        *deleted = list;
        return list->next;
    }

    list->next = ListDelete(list->next, tail, val, deleted);
    return list;
}

参数和返回:

        case MENU_DELETE:
            val = GetValue("Enter value");
            if (list) tail = FindTail(list);
            list = ListDelete(list, tail, val, &node);
            ListPrintNode(node, "Deleted");
            free(node);

寻尾功能:

Node* FindTail(Node* list)
{
    Node* temp = list;
    while(temp->next != list)
        temp = temp->next;
    return temp;
}

【问题讨论】:

  • 这是可能的,但我没有看到任何尝试重新连接上一个和下一个元素。您需要将前一个节点传递给递归函数,否则无法将其链接到已删除节点之后的元素。请展示一个失败的最小示例以及所需的结果应该是什么。谢谢。
  • 您可能应该具体说明如果您尝试删除例如 2 元素列表的第一个或最后一个元素会发生什么。你还没有发布完整的代码,所以我无法重现任何东西,而且我也不知道我试图重现什么行为。
  • 仅当节点位于中间或末尾时才删除,当我尝试删除第一个节点时,它只会将节点传递到末尾..如果有单个或 2-元素程序崩溃。
  • 参数和返回值应该是什么?特别是tail 是什么? 我的代码只从中间删除,而不是从第一个或最后一个位置删除。这是一个要求吗?循环列表实际上没有尾巴。
  • tail 它的最后一个节点,这是我的停止条件,当 tail == list 我知道我现在在最后一个节点。如果您需要删除第一个节点,您需要知道谁是最后一个节点,因为您需要建立该连接。

标签: c linked-list


【解决方案1】:

ListDelete 的 API 有一个不需要的 tail 参数。

这是一个没有这样参数的实现。它返回一个指向循环列表的指针,第一个具有value == val 的节点从list->next 开始,并将指向该节点的指针存储到*deleted

Node *ListDelete(Node *list, int val, Node **deleted) {
    // special case empty and singleton lists
    if (!list || (list == list->next && list->value != val)) {
        *deleted = NULL;
        return list;
    }
    if (list == list->next) {
        *deleted = list;
        return NULL;
    }
    for (Node *node = list;; node = node->next) {
        if (node->next->value == val) {
            *deleted = node->next;
            node->next = node->next->next;
            if (list == *deleted)
                list = node->next;
            return list;
        } else
        if (node->next == list)
            *deleted = NULL;
            return list;
        }
    }
}

使用您的 API,这里是您的代码的更正版本:

Node *ListDelete(Node *list, Node *tail, int val, Node **deleted) {
    if (!list || (list == list->next && list->value != val)) {
        *deleted = NULL;
        return list;
    }
    if (list == list->next) {
        *deleted = list;
        return NULL;
    ]
    if (list->next->value == val) {
        *deleted = list->next;
        list->next = list->next->next;
        return list;
    }
    if (list == tail) {
        *deleted = NULL;
        return list;
    }
    ListDelete(list->next, tail, val, deleted);
    if (list == *deleted)
        list = list->next;
    return list;
}

这种递归实现的缺点是递归的深度是列表的长度。此递归不是尾递归,因此足够长的列表会导致堆栈溢出

为避免这种情况,您可以将函数转换为非递归函数:

Node *ListDelete(Node *list, Node *tail, int val, Node **deleted) {
    // special case empty and singleton lists
    if (!list || (list == list->next && list->value != val)) {
        *deleted = NULL;
        return list;
    }
    if (list == list->next) {
        *deleted = list;
        return NULL;
    }
    for (Node *node = list;; node = node->next) {
        if (node->next->value == val) {
            *deleted = node->next;
            node->next = node->next->next;
            if (list == *deleted)
                list = node->next;
            return list;
        } else
        if (node == tail) { // equivalent to if (node->next == list)
            *deleted = NULL;
            return list;
        }
    }
}

【讨论】:

  • 非常感谢!!我尝试将其拆分为 2 个不同的功能,一个仅用于头部移除,第二个用于其他情况,它有效但你的更好!
  • @ValeriF21:我更新了答案:如果这个节点是被删除的节点,递归版本不应该返回列表。我还简化了非递归版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
相关资源
最近更新 更多