19. 删除链表的倒数第 N 个结点(快慢指针、栈、递归)
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
**进阶:**你能尝试使用一趟扫描实现吗?
解法一、计算链表长度
先对链表遍历一遍计算出长度,再遍历到第L-n个结点,这个节点的下一个结点删除就ok了。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
int length = getLength(head);
ListNode cur = dummy;
for (int i = 0; i < length - n ; ++i) {
cur = cur.next;
}
cur.next = cur.next.next;
ListNode ans = dummy.next;
return ans;
}
public int getLength(ListNode head) {
int length = 0;
while (head != null) {
++length;
head = head.next;
}
return length;
}
}
- 时间复杂度:O(L),其中 L是链表的长度。
- 空间复杂度:O(1)。
解法二、快慢指针。
快指针先走n个,慢指针再开始走。这样快指针走到头,慢指针的下一个就是要删除的结点。
ps:当L==n时,快指针会走到最后为空。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
int end = n;
ListNode slow = null, fast = head;
while(end != 0 && fast.next!=null){
fast = fast.next;
end--;
}
//第一个结点是特例,end=1,fast.next == null时退出
//其余情况都是end=0 退出
//end = 0,开始循环
if(end==0) slow = head;
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
if(slow != null) {
slow.next = slow.next.next;
return head;
}
return head.next;
}
}
优化代码:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode slow = head, fast = head;
for(int i = 0; i < n; ++i) fast = fast.next;
if(fast == null) return head.next ; //倒数第L个
while(fast.next!=null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return head;
}
}
- 时间复杂度:O(L),其中 L是链表的长度。
- 空间复杂度:O(1)。
解法三、栈
全部入栈,退栈n个后,栈顶为删除结点的前驱结点.
- 时间复杂度:O(L),其中 L是链表的长度。
- 空间复杂度:O(1)。
解法四、递归
class Solution {
private int index = 0;
public ListNode removeNthFromEnd(ListNode r, int n) {
if(r == null) return null;
r.next = removeNthFromEnd(r.next,n);
index++;
if(index == n) return r.next; //当前r是目标删除结点
return r;
}
}
- 时间复杂度:O(L),其中 L是链表的长度。
- 空间复杂度:O(1)。