C++ 递归函数,用于删除链表中字符的所有实例

C++ 递归函数,用于删除链表中字符的所有实例,c++,linked-list,nodes,C++,Linked List,Nodes,我编写了一个递归函数来删除具有特定数据值的节点,但是它不能正常工作 Node * removeAll(Node *top, char c){ if(top == NULL) return NULL; Node *newTop; if(top->data == c){ newTop = top->next; delete top; }else{ newTop = top; }

我编写了一个递归函数来删除具有特定数据值的节点,但是它不能正常工作

Node * removeAll(Node *top, char c){
    if(top == NULL)
        return NULL;

    Node *newTop;
    if(top->data == c){
        newTop = top->next;
        delete top;
    }else{
        newTop = top;
    }

    newTop->next = removeAll(newTop->next,c);

    return newTop;
}
提供给函数的链表包含值
HE l o
我希望输出的列表包含值
HE o
,但它却包含值
HE l o
更改此项:

if(top->data == c){
    newTop = top->next;
    delete top;
}else{
    newTop = top;
}
为此:

while(top && top->data == c){
    newTop = top->next;
    delete top;
    top = newTop;
}
newTop = top;
这样,在移动到下一个元素之前,包含目标值的连续元素都将被删除


另一方面,如果以迭代方式而不是递归方式编写此函数,它可能会占用更少的内存,速度更快。

我将以教程的形式回答此问题,因为几乎每个人在学习如何递归思考时都会遇到一些困难

请注意,因为它使用
while
循环,@Edward的答案不是完全递归的形式

当你学习时,首先用人类语言写出答案的递归描述总是有帮助的。从代码开始将注意力从思考算法转移到不重要的细节上,如语法和指针语义。用英语

删除字符C的形式
[HEAD,rest\u of_list]
的列表等于删除字符C且
HEAD
可选地挂起的形式
rest\u of_list
。是否预加
HEAD
取决于它是否等于
C

这里的
HEAD
是一个字符,而
rest\u of_list
本身就是一个列表

递归部分正在从列表的剩余部分删除
C
。请注意,递归发生在比输入短一个字符的字符串上。太好了!这意味着算法在从一个递归调用到下一个递归调用之间取得了进展

我们还需要描述一个递归停止的“基本情况”。在这里,由于列表从一个调用到下一个调用越来越短,因此尝试使用空列表是合乎逻辑的。用英语

当输入列表为空时,它不能包含
C
,因此返回空列表

我们已经准备好编写代码了。首先是基本情况。您的实现很好。在通常的C列表实现中,
NULL
指针是空列表

Node *removeAll(Node *list, char c) {
  // Base case.
  if (list == NULL) return NULL;
  // Recursive case.
  // TODO: Complete me.
}
对于递归的情况,
HEAD
正如我们用英语写的那样,是C中的
list->data
,而
rest\u是
list->next
。因此,请继续写下:

  // Recursive case.
  char head = list->data;
  Node *rest = list->next;
递归案例本身有两个案例。如果
head
c
,那么我们只需返回
rest
,并删除
c

  if (c == head) return removeAll(rest, c);
剩下的情况是,
head
不等于
c
。这里有一个选择。您需要一个节点来容纳
c
。您可以重新使用当前保存它的列表,这意味着您正在更改原始列表。或者您可以分配一个新节点,这意味着原始列表保持不变。在实际应用中,此决策可能非常重要。假设您希望保持原始列表的完整性。预编是用

return allocateNewNode(head, removeAll(rest, c));
此处
allocateNewNode
获取未用于其他列表的节点的新内存。例如,它可以调用
malloc

另一方面,如果要更改输入列表(术语
mutate
非常常见),请修改
列表中的第一个节点

list->next = removeAll(rest, c);
return list;
总的来说,突变的情况是:

Node *removeAll(Node *list, char c) {
  // Base case: on empty list, return empty list.
  if (list == NULL) return NULL;
  // Recursive cases. Extract head value and rest of list.
  char head = list->data;
  Node *rest = list->next;
  // If head is C, return rest with C removed.
  if (c == head) return removeAll(rest, c);
  // Otherwise prepend C to rest with C removed by mutating the first list node, 
  // which already contains head.
  list->next = removeAll(rest, c);
  return list;
}

我希望这对您和其他试图掌握递归思想的人有所帮助。

似乎是使用调试程序的好机会。当简单的迭代循环就足够时,为什么要使用递归?这也可以避免在大列表中循环时出现堆栈溢出的风险。`newTop=top->next“应该是“top=top->next”,但如果不是这样,我感谢您的帮助