一.题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
二.题目解析
1.两趟扫描
public ListNode removeNthFromEnd(ListNode head, int n) {
/*两次遍历
* */
//定义虚拟头结点方便处理
ListNode dummyHead = new ListNode(-1,head);
ListNode p1 = dummyHead.next;
int count = 0;
//统计链表节点的数量
while (p1 != null){
count++;
p1 = p1.next;
}
//p1重新指向dummyHead
p1 = dummyHead;
//倒数第N个是正数第count - n + 1个(下标从1开始)
int forward = count - n + 1;
//判断越界
if(forward <= 0){
return null;
}
//p1定位到要删除节点的前面一个节点
for (int i = 1; i < forward; i++) {
//p1总共移动count-n次,最终指向第count - n个节点
p1 = p1.next;
}
//delNode指向要删除的节点
ListNode delNode = p1.next;
//记录要删除节点后面那个节点
ListNode after = delNode.next;
//实现“删除”
p1.next = after;
delNode.next = null;
return dummyHead.next;
}
两趟扫描优化版:
原因:prev初始为虚拟头结点,先行count - n步到达待删除节点前驱节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head == null){
return null;
}
ListNode dummy = new ListNode();
dummy.next = head;
ListNode prev = head,successor;
int count = 1;
while(prev.next != null){
prev = prev.next;
count++;
}
prev = dummy;
for(int i = 0;i < count - n;i++){
prev = prev.next;
}
successor = prev.next.next;
prev.next.next = null;
prev.next = successor;
return dummy.next;
}
}
2.快慢指针,一趟扫描
public ListNode removeNthFromEnd2(ListNode head, int n) {
/*快慢指针
* */
//考虑到删除的节点可能是头结点,故引入虚拟头结点
ListNode dummyHead = new ListNode(-1,head);
ListNode fast = dummyHead,slow = dummyHead;
//fast指针先走n+1步
for (int i = 0; i <= n; i++) {
fast = fast.next;
}
//最后fast指针指向null的时候,slow指针指向倒数第N个节点的前一个节点
while (fast != null){
fast = fast.next;
slow = slow.next;
}
//被删除的节点
ListNode delNode = slow.next;
//被删除的节点后面的节点
ListNode afterNode = delNode.next;
//被删除的节点前节点next指针 指向 被删除节点后面那个节点--实现删除
slow.next = afterNode;
delNode.next = null;
return dummyHead.next;
}
3.递归,一趟扫描
//全局变量,当前head是倒数第几个节点,这个标记会在每次递归结束时候+1
int count = 0;
public ListNode removeNthFromEnd1(ListNode head, int n) {
/*递归,不需要特殊判断头结点,无需引入虚拟头结点
* */
if(head == null){
return null;
}
//递归实现删除,将下面一层递归调用的结果赋值给head.next
head.next = removeNthFromEnd1(head.next,n);
//回溯到被上层函数调用的地方count+1(count是从倒数第一个节点开始加的,从后往前加)
count++;
//满足if条件即说明当前head节点就是链表中要删除的倒数第N个节点,直接返回head.next实现删除
if (count == n){
return head.next;
}
return head;
}