我写了一个递归来删除具有特定数据值的节点,但是它不能正常工作。
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;
}
提供给函数的链接列表包含值h e l l o
我希望输出的列表包含值h e o
,但它具有值h e l o
答案 0 :(得分:2)
我将以教程的形式回答此问题,因为几乎每个人在学习如何进行递归思考时都会遇到一些困难。
请注意,由于@Edward使用while
循环,因此它不是完全递归的形式。
学习时,首先要用人类语言写出答案的递归描述。从代码开始,人们将注意力从思考算法转移到了语法和指针语义等不重要的细节上。用英语
格式为
[HEAD, rest_of_list]
且字符C已删除的列表等于rest_of_list
且字符C已删除,且HEAD
可选地附加在其前。是否加上HEAD
取决于它是否等于C
。
HEAD
是一个字符,rest_of_list
本身是一个列表。
递归部分正在从C
中删除rest_of_list
。请注意,递归发生在比输入短一个字符的字符串上。那很好!这意味着该算法正在从一个递归调用进展到下一个递归调用。
我们还需要描述递归停止的“基本情况”。在这里,由于列表从一个呼叫到另一个呼叫越来越短,因此尝试空列表是合乎逻辑的。用英语
当输入列表为空时,它不能包含
C
,因此请返回空列表。
因此,我们准备编写代码。首先是基本情况。您的实现很好。 NULL
指针是通常的C列表实现中的空列表。
Node *removeAll(Node *list, char c) {
// Base case.
if (list == NULL) return NULL;
// Recursive case.
// TODO: Complete me.
}
对于递归情况,我们用英语写的HEAD
在C中是list->data
。而rest_of_list
是list->next
。所以继续写:
// Recursive case.
char head = list->data;
Node *rest = list->next;
递归案例本身有2个案例。如果head
是c
,那么我们只返回rest
并删除c
。
if (c == head) return removeAll(rest, c);
其余情况是head
不等于c
。这里有一个选择。您需要一个节点来容纳c
。您可以重用当前持有的列表,这意味着您正在更改原始列表。或者,您可以分配一个新节点,这意味着原始列表保持不变。在实际应用中,此决定可能非常重要。假设您要保持原始列表不变。前置是通过
return allocateNewNode(head, removeAll(rest, c));
这里allocateNewNode
为未被其他列表使用的节点获取新的内存。例如,它可以调用malloc
。
另一方面,如果要更改输入列表(术语mutate
很常见),请修改list
中的第一个节点。
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;
}
我希望这对您和其他试图摆脱递归思维的人有帮助。
答案 1 :(得分:1)
更改此:
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;
这样,包含目标值的连续元素将全部删除,然后再移至下一个元素。
顺便说一句,如果该函数是迭代而不是递归编写的,则该函数可能会使用较少的内存,并且速度更快。