链表与递归
从一个问题开始
我们从LeetCode上的一个问题开始对链表和递归进行研究分析学习
在LeetCode上的第203号问题, 是一个链表删除元素的问题, 具体题目如下
Remove all elements from a linked list of integers that have value val.
Example:
Input: 1->2->6->3->4->5->6, val = 6 Output: 1->2->3->4->5
显然, 让我们删除头节点为head中所有值为val的元素
不使用虚拟头节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 检查头节点是否为需要删除的节点, 如果是, 删除掉, 检查删除头节点之后的节点是否为需要删除的节点
while (head != null && head.val == val) {
head = head.next;
}
if (head == null) {
return null;
}
// 经过上述循环, 此时第一个节点一定不会是需要删除的节点了
ListNode pre = head;
// 需要删除的节点不为null, 删除到链表的最后
while (pre.next != null) {
if (pre.next.val == val) {
pre.next = pre.next.next;
} else {
pre = pre.next;
}
}
return head;
}
}
在此段代码中, 我并没有使用虚拟头节点. 可以看到, 需要分别判断头节点和头节点之后的节点.
使用虚拟头节点
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return null;
}
//使用虚拟头节点
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode pre = dummyHead;
// 需要删除的节点不为null, 删除到链表的最后
while (pre.next != null) {
if (pre.next.val == val) {
pre.next = pre.next.next;
} else {
pre = pre.next;
}
}
return dummyHead.next;
}
}
显然嘛, 使用虚拟头节点的时候, 就不用判断头节点了. 添加虚拟头节点的话, 直接可以把头节点当作一个普通的节点使用.
递归
递归的本质, 就是将原来的问题, 转换为更小的问题
举例如:
Sum(arr[0...n-1]) = arr[0] + Sum(arr[1...n-1]) <----Sum(arr[0...n-1])变成更小的问题了
Sum(arr[1...n-1]) = arr[1] + Sum(arr[2...n-1]) <----Sum(arr[1...n-1])变成更小的问题了
Sum(arr[n-1...n-1]) = arr[n-1] + Sum([]) <----最基本的问题
使用递归数组求和
public class Sum {
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5, 6, 7};
System.out.println("sum(nums) = " + sum(nums));
}
public static int sum(int[] arr) {
return sum(arr, 0);
}
/**
* 递归求和
* @param arr 需要求的数组
* @param l 从数组的那个位置求和
* @return 返回从l到length的和
*/
private static int sum(int[] arr, int l) {
if (l == arr.length) {
return 0;
}
return arr[l] + sum(arr, l + 1);
}
}
链表具有天然的递归性
链表是一个链式结构, 对于每一个结点来说, 操作都是一致的, 所以说, 链表具有天然递归性
我们使用递归解决LeetCode中203号问题
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return null;
}
if (head.val == val) {
return removeElements(head.next, val);
} else {
head.next = removeElements(head.next, val);
}
return head;
}
}
还可以继续优化一下
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return null;
}
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
}
没错, 递归实现的代码, 就是这么优雅, 只有仅仅五行代码.