【力扣】19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
题解
方法一:两次遍历
- 先遍历一次链表,求出链表的总长度。
- 根据总长度 length 的值-n,就算出需要再遍历多少个节点,找到要删除的节点的前一个节点 x。
- 将 x 节点的 next 指针指向下下一个节点就可以删除节点了。
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val;this.next = next; }
}
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null || n <= 0) {
return head;
}
//增加一个特殊节点,方便边界处理
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode cur = dummyNode;
//第一次遍历,计算链表总长度
int length = 0;
while (cur.next != null) {
cur = cur.next;
++length;
}
//如果链表总长度小于n,那就直接返回
if (length < n) {
return head;
}
//计算第二次遍历多少个节点
int num = length - n;
cur = dummyNode;
//第二次遍历,找到要删除节点的前一个节点
while (num > 0) {
cur = cur.next;
--num;
}
//删除节点,并返回
cur.next = cur.next.next;
return dummyNode.next;
}
}
方法二:一次遍历(快慢指针)
需要两个指针 slow 和 fast。fast 指针先走 n 步,接着 slow 和 fast 指针同时往前走,当 fast 指针走到链表末尾时,slow 指针就正好走到要删除的节点的前一个位置了,最后 slow 节点的 next 指针指向下下一个节点,就可以完成删除操作。
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val;this.next = next; }
}
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//增加一个特殊节点方便边界判断
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode slow = dummyNode;
ListNode fast = dummyNode;
//第一个循环,fast 指针先往前走n步
while (n > 0 && fast != null) {
fast = fast.next;
n--;
}
// n > 链表length,fast走n步到尾了,于是后面的判断就不用做了,直接返回
if (fast == null) {
return head;
}
//第二次,fast、slow指针一起走
//当遍历结束时,slow指针就指向要删除的节点的前一个位置
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
//删除节点并返回
slow.next = slow.next.next;
return dummyNode.next;
}
}