leetcode有道題就是問的這個問題: leetcode:Remove Nth Node From End of List[E]
鏈表的問題
這是鏈表中非常常見的問題,衆所周知,鏈表慢就慢在遍歷查找,而對於單鏈表來說,每次必須從頭開始搜索,這樣使得鏈表在處理“倒數”這個概念的時候,特別無力。常規的做法必須要2遍遍歷:1遍計算鏈表長度len,1遍搜索倒數的元素len-n。(當然,你可以通過加入鏈表長度變量或者使用雙向鏈表解決這個問題。)
但是題目要求是一遍完成,有沒有一遍的思路呢?其實是有的,那就是雙指針或遞歸。
解決思路一 ——雙指針
這裏有個常用的做法去解決“倒數問題”:雙指針。
雙指針常用做法是:
- 一個指針用來作爲參考,控制長度(作爲循環停止條件)
- 一個指針延遲啓動用來跑“倒數”。
第一個指針先運行n個數,然後打開第二個指針,這樣,當第一個指針跑完時,第二個指針剛好跑過Len-N數,這樣就找到了倒數第n個數。
注意:由於刪除操作比較特殊,必須找到前一個節點才能刪除下個節點,所以一般刪除操作我們會構造一個虛擬節點作爲開頭,以防開頭節點被刪除。
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode newhead = new ListNode(-1); //防止頭被刪除
newhead.next = head;
ListNode point1 = newhead;
ListNode point2 = newhead;
for(;point1 != null;point1 = point1.next,n--) //point1 控制長度
{
if(n < 0)
point2 = point2.next; //point2延遲啓動
}
point2.next = point2.next.next;
return newhead.next;
}
}
解決思路二 —— 遞歸
除了用雙指針外,還可以考慮用遞歸,凡是這種涉及單鏈表插入刪除操作的時候,都可以考慮用遞歸,因爲插入和刪除都需要涉及它的父親操作。我們考慮最後一個元素是第一層,然後逐級返回,當返回到第N+1層(也就是父親節點所在層數)就開始刪除操作。
public class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode newhead = new ListNode(-1);
newhead.next = head;
remove(newhead,n);
return newhead.next;
}
private int remove(ListNode node, int n) {
if(node.next == null) return 1;
int level = remove(node.next,n)+1; //層數+1
if(level == n+1) //找到了父親
node.next = node.next.next;
return level;
}
}