203.移除链表元素
这是LeetCode第203号问题,题目链接:https://leetcode-cn.com/problems/remove-linked-list-elements/
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
ListNode.java
LeetCode中提供的节点,为了本地测试方便,添加方法进行完善。
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
public ListNode(int[] arr) {
if (arr == null || arr.length == 0)
throw new IllegalArgumentException("arr can not be empty");
// val 等于第一个元素
this.val = arr[0];
// 把数组中元素创建成一个个新的ListNode接在前一个节点上
ListNode cur = this;
for (int i = 1; i < arr.length; i++) {
cur.next = new ListNode(arr[i]);
cur = cur.next; // 挪位置
}
// 之后this就是用循环创建的链表的头节点
}
// 返回以当前节点为头节点的字符串
@Override
public String toString() {
StringBuilder res = new StringBuilder();
ListNode cur = this;
while (cur != null) {
res.append(cur.val + "-->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
不使用虚拟头节点
按照思路一步一步实现
// 不创建虚拟头节点
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;
}
// 从中间删,此时head一定不是待删除元素,head后一个节点可能是带删除元素
ListNode prev = head; // prev为待删除元素前一个元素
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;
}
}
上述代码简写
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 如果头节点是待删除元素,需要删除
// 使用一个循环删除,存在头节点后一个元素也是需要删除的
while (head != null && head.val == val) { // 头节点不能为空
head = head.next;
}
if (head == null) {
return null;
}
// 从中间删,此时head一定不是待删除元素,head后一个节点可能是带删除元素
ListNode prev = head; // prev为待删除元素前一个元素
while (prev.next != null) {
if (prev.next.val == val) {
prev.next = prev.next.next;
} else {
// 否则的话不用删除,往回偏移
prev = prev.next;
}
}
return head;
}
使用虚拟头节点
// 使用虚拟头节点
class Solution {
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;
}
}
链表具有递归性质
把一个一个节点连接起来就是一个链表
对于链表0->1->2->3->4->5->NULL
,可以想像成0->和一个更短的链表(少了一个节点)
,链表可以理解为一个一个节点的挂接,也可以理解成一个头节点后面挂接了一个更小的链表。
- 在这个更短的链表中,“1”就变成了它的头节点。
- 更短的链表也可以类似的看作
头节点“1”挂接了一个比现在更小的链表
,在这个更小的链表中,“2”作为它的头节点。 - 依此类推,直到最后,可以理解
NULL本身也是一个链表
,它也是最基本的链表。
使用递归删除链表中的元素
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 对于空链表删除所有元素为val的节点,得到的还是空
if (head == null)
return null;
// 将头节点后的链表中所有的val值删除后剩下的链表
ListNode res = removeElements(head.next, val);
// 判断头节点的值是否为val
if (head.val == val)
return res;
// head的值不为val时候返回head和后面的链表
else {
// 让head挂接上后面的链表
head.next = res;
return head;
}
}
}
代码简写
// 使用递归来解决
class Solution5 {
public ListNode removeElements(ListNode head, int val) {
// 对于空链表删除所有元素为val的节点,得到的还是空
if (head == null)
return null;
// 将头节点后的链表中所有的val值删除后剩下的链表
head.next = removeElements(head.next, val);
// 三目运算符简写
return (head.val == val) ? head.next : head;
}
}
书写测试主函数
public static void main(String[] args) {
int[] arr = {1, 2, 6, 3, 4, 5, 6};
ListNode list = new ListNode(arr);
System.out.println(list);
ListNode res = (new Solution()).removeElements(list, 6);
System.out.println(res);
}
测试结果
1-->2-->6-->3-->4-->5-->6-->NULL
1-->2-->3-->4-->5-->NULL