一.题目描述
给你一个链表,删除链表的倒数第 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;
}
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;
}
|