# 19. Remove Nth Node From End of List
## 程式碼1(用陣列,可以一次迴圈結束,但好像犯規)
```cpp=
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummy = new ListNode;
dummy -> next = head;
vector <ListNode*> v;
ListNode *current = dummy;
while (current != nullptr) {
v.push_back(current);
current = current -> next;
}
v.push_back(nullptr);
int idx = v.size() - 2 - n + 1;
v[idx - 1] -> next = v[idx + 1];
delete v[idx];
head = dummy -> next;
delete dummy;
return head;
}
};
```
## 演算法(快慢指標)
```
1. 定義dummy指標,dummy->next指向head
2. 定義兩個指標ptr1與ptr2,先讓ptr1往前到索引為n的節點(設dummy節點索引為0),ptr2指向dummy,prev
3. 當ptr1 != nullptr:
3.1. prev = ptr2
3.2. ptr2 = ptr2->next
3.3. ptr1 = ptr1->next
4. prev->next指向ptr2->next
5. 刪除ptr2
6. head指向dummy->next
7. 刪除dummy
8. 回傳head
```
## 演算法正確性證明
以下說的索引都是概念上的,實際上鏈結串列不會有索引
循環不變式(第3行):在每次迴圈開始前:
1. ptr1在ptr2以後n個節點,也就是說,ptr1的索引-ptr2的索引=n。
2. prev都在ptr2的上一個節點,除了第一次以外。
```初始```:
1. 在第2行時,ptr1與ptr2的距離是n,所以1成立。
2. prev是空指標(第一次不算),所以2成立。
```維持```:
1. 根據循環不變式,ptr1的索引-ptr2的索引=n,此時執行3.2、3.3後,會讓ptr1與ptr2各往前一步,所以仍然1成立。
2. 第一次執行迴圈會讓2成立,而根據循環不變式,往後的prev在ptr2的上一個節點,所以執行3.1、3.2後,2仍然成立。
```終止```:
當迴圈終止時,ptr1會是nullptr(索引視為size+1),根據循環不變式,ptr1的索引-ptr2的索引=n,此時的ptr2索引會是size+1-n,也就是倒數第n個節點,也就是我們要刪除的節點,此時因為迴圈至少會執行一次(下面有證明),所以prev不為空,根據循環不變式的第2點,是倒數第n+1個節點,也就是ptr2的上一個節點。
再來只要執行4,5,6,7,8,就會刪除倒數第n個節點
---
```迴圈至少會執行一次,不會什麼都沒做就跳出```
proof:使用矛盾證法,假設迴圈什麼都沒做就跳出來,代表一開始ptr1=nullptr,而nullptr索引可以視為size+1,所以n=size+1,其中size是節點數量,要刪除倒數第n個節點,但根據題意,n最大到size而已(假如有5個節點,題目不可能說要刪除倒數第6個節點),所以假設錯誤,這代表迴圈至少會執行一次,完成證明。
## 程式碼
```cpp=
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummy = new ListNode;
dummy -> next = head;
ListNode *ptr1 = dummy, *ptr2 = dummy, *prev;
int idx = 0;
while (idx < n) {
ptr1 = ptr1 -> next;
idx++;
}
while (ptr1 != nullptr) {
prev = ptr2;
ptr2 = ptr2 -> next;
ptr1 = ptr1 -> next;
}
prev -> next = ptr2 -> next;
delete ptr2;
head = dummy -> next;
delete dummy;
return head;
}
};
```