1. 程式人生 > >LeetCode刷題Medium篇 Remove Nth Node From End of List

LeetCode刷題Medium篇 Remove Nth Node From End of List

題目

Given a linked list, remove the n-th node from the end of list and return its head.

Example:

Given linked list: 1->2->3->4->5, and n = 2.

After removing the second node from the end, the linked list becomes 1->2->3->5.

Note:

Given n will always be valid.

Follow up:

Could you do this in one pass?

十分鐘嘗試

1. 第一個反應,不從尾部,從頭部開始,因為尾部沒有指標。size減去n就可以,但是size計算需要遍歷一遍才知道。不是one pass 演算法

2.如何實現one pass沒有想到

先按照第一種思路寫程式碼試試吧,寫程式碼過程中出現一個遺漏點:如果刪除的是第一個元素的情況沒有處理,這個時候head=head.next。我處理的是非第一個節點的情況。程式碼如下,通過測試,感覺不夠簡潔:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        int length=0;
        ListNode curr=head;
        while(curr!=null){
            length++;
            curr=curr.next;
        }
        int postion=length-n+1;
        curr=head;
        int count=1;
        int pre=postion-1;
        if(pre==0){
            head=head.next;
            return head;
        }
        while(curr!=null){
            if(count==pre){
                curr.next=curr.next.next;
            }
            curr=curr.next;
            count++;
        }
        return head;
    }
}

看了答案後,發現了思路簡潔的方法,為了去掉一些corner case,比如我忽略的情況,去掉的是第一個元素等等。可以增加一個dummy node,這樣可以簡化程式碼邏輯。因為如果刪除第一個元素,head指標需要移動,增加dummy節點後,統一返回的dummy.next。學習了思路之後修改一版如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
       ListNode dummy=new ListNode(0);
        //dummy.next=head,不是dummy=head
       dummy.next=head;
       ListNode curr=head;
       int length=0;
       while(curr!=null){
           length++;
           curr=curr.next;
       }
       length-=n;
       curr=dummy;
       while(length-->0){
           curr=curr.next;
       }
       curr.next=curr.next.next;
        //head可能移動,比如只有兩個元素,刪除第一個,head=head.next,所以返回是dummy.next
       return dummy.next;
    }
}

One pass解法

1. 建立dummy node,設定兩個指標first和second,初始都指向dummy

2. 移動n個節點,first比second間隔n個節點。

3 迴圈移動first和second,直到first為空,此時second的位置就是第k個元素的前一個

4 修改指標刪除節點

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
      ListNode dummy=new ListNode(0);
      dummy.next=head;
      ListNode first=dummy;
      ListNode second=dummy;
      for(int i=1;i<=n+1;i++){
          first=first.next;
      }
      while(first!=null){
          first=first.next;
          second=second.next;
      }
      second.next=second.next.next;
      return dummy.next;
    }
}

進一步思考

以前做過刪除單鏈表中的某個元素,當時也是碰上corner case,比如刪除head節點的情況,當時沒有利用dummy node處理。是否也可以這樣處理呢?

https://leetcode.com/problems/remove-linked-list-elements/

這是當時的題目。解法裡面的確單獨處理了,當pre==null的時候 head=head.next;

Remove all elements from a linked list of integers that have value val.

Example:

Input:  1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5

來,為了整合思路,試試今天的dummy方法。完美通過,記住關鍵點:在尋找元素的時候,不想等才移動curr,如果相同,不需要移動curr。有人沒有用dummy node,當然可以,無非需要處理head=head.next的情況,我這裡以後統一用dummy node。程式碼如下:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head==null) return null;
        ListNode dummy=new ListNode(0);
        dummy.next=head;
        ListNode curr=dummy;
        while(curr!=null&&curr.next!=null){
            if(curr.next.val==val){
                curr.next=curr.next.next;
            }
            else{
                //不相等才移動curr,相等不用
                curr=curr.next;
            }
            
        }
        return dummy.next;

    }
}