Leetcode刷題筆記8.12-8.16
19.刪除倒數第n個連結串列結點(8.12)
- 一個巧妙刪除倒數第n個結點的trick
該方法避免了對連結串列的一次全面掃描來獲得總長度
// 返回連結串列的倒數第 k 個節點
ListNode findFromEnd(ListNode head, int k) {
ListNode p1 = head;
// p1 先走 k 步
for (int i = 0; i < k; i++) {
p1 = p1.next;
}// 注意這裡是向後處理k次
ListNode p2 = head;
// p1 和 p2 同時走 n - k 步
while (p1 != null) {
p2 = p2.next;
p1 = p1.next;
}
// p2 現在指向第 n - k + 1 個節點,即倒數第 k 個節點
return p2;
}
- 在這個真題的處理上,建立了虛擬結點來處理邊界問題
為了防止出現空指標的情況,比如說連結串列總共有 5 個節點,題目就讓你刪除倒數第 5 個節點,也就是第一個節點,那按照演算法邏輯,應該首先找到倒數第 6 個節點。但第一個節點前面已經沒有節點了,這就會出錯。但有了我們虛擬節點 dummy 的存在,就避免了這個問題,能夠對這種情況進行正確的刪除。
26. 刪除有序陣列中的重複項(8.14)
- 雙指標技巧:用於在連結串列或陣列中原地索引或修改,這類過程一般需要陣列中元素自身作比較,使用雙指標(可以是不同步移動、同步移動)
左右指標是兩個指標相向而行或者相背而行;快慢指標是兩個指標同向而行,一快一慢。
對於單連結串列來說,大部分技巧都屬於快慢指標,因為無法直接定位到連結串列的末尾。單連結串列的六大解題套路 都涵蓋了,比如連結串列環判斷,倒數第 K 個連結串列節點等問題,它們都是透過一個 fast 快指標和一個 slow 慢指標配合完成任務。
- 【原地修改】:
如果不是原地修改的話,直接new int[],把去重之後的元素放進這個新陣列中,然後返回這個新陣列即可。但是原地刪除,不允許new新陣列,只能在原陣列上操作,然後返回一個長度,這樣就可以透過返回的長度和原始陣列得到我們去重後的元素有哪些了。
所以該題無所謂去重後剩餘空間中的數字,無需考慮剩餘空間的處理。
3.採用快慢指標策略:讓慢指標 slow 走在後面,快指標 fast 走在前面探路,找到一個不重複的元素就賦值給 slow 並讓 slow 前進一步。這樣,就保證了 nums[0..slow] 都是無重複的元素,當 fast 指標遍歷完整個陣列 nums 後,nums[0..slow] 就是整個陣列去重之後的結果。
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length == 0) {
return 0;
}
int slow = 0, fast = 0;
while (fast < nums.length) {
if (nums[fast] != nums[slow]) {
slow++;
// 維護 nums[0..slow] 無重複
nums[slow] = nums[fast];
}
fast++;
}
// 陣列長度為索引 + 1
return slow + 1;
}
}
27.移除元素(8.16)
- 這道題和上一道思路很像,透過fast指標遍歷後移來掃描元素,在滿足條件時替換(賦值)slow指標,本質上是刪除重複/不需要的元素,同時縮短整個陣列
把nums中所有值為val的元素原地刪除,依然需要使用快慢指標技巧:
如果 fast 遇到值為 val 的元素,則直接跳過,否則就賦值給 slow 指標,並讓 slow 前進一步