这个想法是生成的结构将是一个“Y”:一个双头列表(实际上是一个简单的图)。
Y 的一个分支是原始列表。另一个是您删除节点的新列表。 Y 的垂直柄是您删除的元素之后的内容。这两个列表都很常见。这是一些将 Y 翻转过来的 ascii 艺术作品,显示了 1 到 5 的列表,其中 3 被删除了。
new -> 1 -> 2 ------\
v
original -> 1 -> 2 -> 3 -> 4 -> 5 -> null
递归思考就是根据自身的较小版本加上固定的工作量来定义问题。而且您需要一个基本案例(或者可能几个)。
链表本身就是一个递归结构:
列表要么是空的,要么是通过其“下一个”引用链接到列表的元素。
请注意,这使用较小的列表定义了一个列表。基本情况是空列表。固定位是元素。
阅读这个定义几次,然后看看它是如何翻译代码的:
class MyList {
int value; // the element at the head of this list
MyList next; // the rest of the list
MyList(int value, MyList next) {
this.value = value;
this.next = next;
}
}
基本情况“空列表”只是一个null 引用。使用相同模式递归表示的元素去除问题变为:
删除一个元素的列表的副本是 a) 如果要删除的元素是头部,则列表后面的其余部分是头部或 b) 当前节点的副本,然后是副本删除了所需元素的列表的其余部分。
在这里,我使用相同事物的较小版本来定义“删除了一个元素的列表副本”。情况 a) 是基本情况。当它不是removee时,固定位正在复制头部。
当然还有另一种基本情况:如果列表为空,则无法找到 removee。这是一个错误。
将其放入代码中:
MyList removeNumber(MyList m, int removee) {
if (m == null) throw new RuntimeException("removee not found");
if (m.value == removee) return m.next;
return new MyList(m.value, removeNumber(m.next, removee));
}
使用函数看起来像这样:
MyList originalList = ... // list of 1 to 5.
MyList newListWith3removed = removeNumber(originalList, 3);
System.out.println("Original list:");
for (MyList p : originalList) System.out.println(p.value);
System.out.println("With 3 removed:");
for (MyList p : newListWith3removed) System.out.println(p.value);
输出将与预期一样:第一个列表中为 1 到 5,第二个列表中为 1,2,4,5。 IE。第一个列表没有改变。