5-1 Leetcode中和链表相关的问题
leetcode 203题目
删除链表中给定值val的【所有】元素
先定义ListNode结构
//Definition for singly-linked list.
public class ListNode {
public int val;
public ListNode next;
ListNode(int x){
val = x;
}
}
解决题目:
- 没有使用虚拟头结点的情况
import java.util.List;
public class Solution {
public ListNode removeElements(ListNode head,int val){
//如果头结点的元素与给定的元素相等,删除掉头结点
//开始部分先删除
while(head!=null && head.val == val){
ListNode delNode = head;
head=head.next;
delNode.next=null;
}
//如果所有节点都要删除
if (head==null)
return null;
//删除链表中间需要删除的元素
ListNode prev = head;//注意,经过前面的处理,此时的head一定不是一个要删除的节点了,他之后的元素才是可能要删除的结点
while(prev.next!=null){
if (prev.next.val == val){
ListNode delNode = prev.next;
prev.next = delNode.next;
delNode.next=null;
}
else
prev = prev.next;
}
return head;
}
}
import java.util.List;
//不考虑回收机制,不用使用delNode = null这样的操作的情况下
public class Solution2 {
public ListNode removeElements(ListNode head,int val){
//如果头结点的元素与给定的元素相等,删除掉头结点
while(head!=null && head.val == val){
head = head.next;
}
//如果所有节点都要删除
if (head==null)
return head;
//删除链表中间需要删除的元素
ListNode prev = head;
while(prev.next!=null){
if (prev.next.val == val){
prev.next=prev.next.next;
}
else
prev = prev.next;
}
return head;
}
}
- 使用虚拟头结点的情况
import java.util.List;
//有虚拟头结点的情况
public class Solution3 {
public ListNode removeElements(ListNode head,int val){
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode prev = dummyHead;
while(prev.next!=null){
if(prev.next.val==val)
prev.next = prev.next.next;
else
prev =prev.next;
}
return dummyHead.next;
}
}
可以看到 代码更加简洁了
5-2 测试自己的Leetcode链表代码
我们编写一个使用数组的内容来构建链表的函数
//Definition for singly-linked list.
public class ListNode {
public int val;
public ListNode next;
ListNode(int x){
val = x;
}
//链表节点的构造函数
// 使用arr为参数,创建一个链表,当前的ListNode为链表头结点
public ListNode(int[] arr){
if(arr == null || arr.length == 0)
throw new IllegalArgumentException("arr can not be empty");
this.val = arr[0];
ListNode cur = this;
for (int i=1;i<arr.length;i++){
cur.next = new ListNode(arr[i]);
cur = cur.next;
}
}
//以当前节点为头结点的链表信息字符串
@Override
public String toString(){
StringBuilder s = new StringBuilder();
ListNode cur = this;
while(cur!=null){
s.append(cur.val+"->");
cur= cur.next;
}
s.append("NULL");
return s.toString();
}
}
测试solution
import java.util.List;
public class Solution {
public ListNode removeElements(ListNode head,int val){
while(head!=null && head.val == val){
ListNode delNode = head;
head=head.next;
delNode.next=null;
}
if (head==null)
return null;
ListNode prev = head;
while(prev.next!=null){
if (prev.next.val == val){
ListNode delNode = prev.next;
prev.next = delNode.next;
delNode.next=null;
}
else
prev = prev.next;
}
return head;
}
public static void main(String[] args) {
int[] nums = {1,2,6,3,4,5,6};
ListNode head = new ListNode(nums);
System.out.println(head);
ListNode res =(new Solution()).removeElements(head,6);
System.out.println(res);
}
}
5-3 递归基础与递归的宏观语意
public class Sum {
public static int sum(int[] arr){
return sum(arr,0);
}
//计算arr[1.....n)这个区间内所有数字的和
private static int sum(int[] arr,int l){
if(l==arr.length)
return 0;
return arr[l]+sum(arr,l+1);
}
public static void main(String[] args) {
int[] nums = {1,2,3,4,5,6,7,8};
System.out.println(sum(nums));
}
}
5-4 链表的天然递归结构性质
对于写一个递归算法只有两部分
- 问题规模最小的情况的解
- 分成多个小问题,构建递归过程
好好看代码,我们是怎样复用removeELements这个函数来解决了一个更小规模的问题,并且用这个更小规模问题的解res组建成原问题的解。
public class Solution4 {
public ListNode removeElements(ListNode head,int val){
if(head==null)
return head;
ListNode res = removeElements(head.next,val);
if (head.val==val)
return res;
else{
head.next = res;
return head;
}
}
public static void main(String[] args) {
int[] nums = {1, 2, 6, 3, 4, 5, 6};
ListNode head = new ListNode(nums);
System.out.println(head);
ListNode res = (new Solution4()).removeElements(head, 6);
System.out.println(res);
}
}
简化后的版本(使用三目运算符)
public class Solution5 {
public ListNode removeElements(ListNode head,int val){
if (head==null)
return head;
head.next = removeElements(head.next,val);
return head.val==val?head.next:head;
}
}
5-5 递归运行的机制:递归的微观解读
5-6 递归算法的调试
如何使用打印输出的方式展现一个递归函数调用的整个过程
几个概念
- 递归深度:depth
public class RecurDebug {
public ListNode removeElements(ListNode head,int val,int depth){
String depthString = generateDepthString(depth);
System.out.print(depthString);
System.out.println("Call:remove "+ val +"in" + head);
if (head==null){
System.out.print(depthString);
System.out.println("Return: " + head);
return head;
}
ListNode res = removeElements(head.next,val,depth+1);
System.out.print(depthString);
System.out.println("After move "+ val + ":" +res);
ListNode ret;
if (head.val == val)
ret =res;
else{
head.next = res;
ret = head;
}
System.out.print(depthString);
System.out.println("Return: "+ret);
return ret;
}
private String generateDepthString(int depth){
StringBuilder res = new StringBuilder();
for (int i=0;i<depth;i++)
res.append("--");
return res.toString();
}
public static void main(String[] args) {
int[] nums = {1, 2, 6, 3, 4, 5, 6};
ListNode head = new ListNode(nums);
System.out.println(head);
ListNode res = (new RecurDebug()).removeElements(head,6,0);
System.out.println(res);
}
}
5-7 更多和链表相关的问题
- 关于递归
- 近乎和链表相关的所有操作,都可以使用递归的形式完成。
- 建议同学们对链表的增删改查,进行递归实现。
其他练习
- leetcode练习题 “链表”相关
- 斯坦福的文档 LinkedListProblem.pdf里面有18个问题,可以看。