數據結構和算法之單向鏈表二:獲取倒數第K個節點


  我們在做算法的時候或多或少都會遇到這樣的問題,那就是我們需要獲取某一個數據集的倒數或者正數第幾個數據。那么今天我們來看一下這個問題,怎么去獲取倒數第K個節點。我們拿到這個問題的時候自然而然會想到我們讓鏈表從末尾開始next   K-1 次不就是第K-1個節點了么,但是必須要注意一點,這是單向鏈表。那么這時候的解決思路或許就會出現分歧,大多數人都會想到我們遍歷一下鏈表,獲取鏈表的長度,然后再減去 K 長度的節點,那么我們這個鏈表的最后一個節點就是原鏈表的倒數第K個節點:我們看一下實現代碼:

/**
     * 獲取倒數第K個節點的數據
     * @param index
     * @return
     */
    public int getDtae(int index){
        //對整個鏈表進行遍歷
        int size = 0;
        Node current = head;//head是頭結點
        while(current!=null){
            size++;
            current = current.next;
        }
        current = head;
        //向后遍歷size-K獲取倒數第K個節點
        for(int i = 0;i < size - index;i++){
            current = current.next;
        }
        return current.date;
    }

  我們可以發現,這段代碼可不可以實現我們需要的功能,當然可以。那么問題來了,如果我們要是輸入的index大於鏈表的長度或者說鏈表自身就是一個空鏈表,那么,我們這段代碼會不會出現問題。或者當我們的index等於0的時候,又會不會出現問題,正常來說我們將倒數都是從倒數第一開始,倒數零是不是就沒有意義,那么這一段代碼還夠不夠強壯。這個問題或許我們稍微有一點良好的編程思想都會想到,我們留到最后解決。下面我們需要思考的是怎么在遍歷一邊鏈表的情況下就獲取到上面的數據。我們可不可以定義兩個節點first和second,他們同時指向head頭結點。我們先把第二個節點向后移動index-1步,這時first和second是不是就相距k,我們再把兩個節點同時向后移動,當second到達鏈表尾端的時候,是不是就可以說first的位置就是我們需要的倒數第K個節點。代碼如下:

/**
     * 獲取倒數第K個節點的數據
     * @param index
     * @return
     */
    public int getDtae(int index){
        //定義兩個節點指向head
        Node first = head;
        Node second = head;
        //把第二個節點向后移動k-1步
        for(int i = 0;i < index - 1;i++){
            second = second.next;
        }
        //再把兩個節點同時向后移動,直到second到達尾端位置
        while(second!=null){
            first = first.next;
            second = second.next;
        }
        return first.date;
    }

  我們可以看到這一段代碼是不是就已經實現了。但是還是那個問題,一段代碼的強壯型在於它在處理特殊事件時候的能力,別讓整個程序崩潰。接下來我們進行以下操作,避免我們所說的三個問題,index等於0,index超過了鏈表的長度,鏈表是空鏈表/**     * 獲取倒數第K個節點的數據     * @param index

 * @return */ public int getDtae(int index){ //判斷index是否為零或者是小於零的不合法數據 if(index <= 0 || head == null){ //拋出空指針異常 throw new NullPointerException(); } //定義兩個節點指向head Node first = head; Node second = head; //第二個節點向后移動K-1步 for(int i = 0;i < index -1;i++){ //判斷second是否為空 second = second.next;
 if(second==null){
 
         
throw new NullPointerException();
      }
} //兩個節點向后移動直到鏈表的尾端 while(second!=null){ first = first.next; second = second.next; } return first.date; }

  我們可以看到在開始直接判斷k等於0的情況,我們在第二個節點向后移動的時候直接判斷他是否為空,如果鏈表為空,那么剛開始second自然為空,如果index大於鏈表長度,在之后next的過程中,自然second也會產生空的情況。這就完美解決了上面提到的三個情況,index等於零,index大於鏈表長度,鏈表為空的情況。我們在進行測試的時候可以通過我上一篇博客對一個鏈表插入數據,然后再進行功能測試,獲取鏈表的首尾中三個節點數據,在進行特殊測試的時候可以輸入上面的三種情況就是測試。

  通過以上問題我們還可以思考一個事情,如果我們需要得到中間節點,但是只允許遍歷一次的情況下我們應該怎么去實現:

public int getMiddle(){
      //判斷鏈表是否為空
      if(head == null){
           throw new NullpointerExpection();   
      }
      //定義兩個節點同時指向首節點
      Node first = head;
      Node second = head;
      //將第二個節點向后移動兩步,第一個節點向后移動一步,直到second到達尾端
      while( second.next != null){
            first = first.next;
            second = second.next.next;
      }  
      return first.date;       
}

  我們需要注意的是這段代碼有一點缺陷,那就是我們的鏈表長度如果是偶數的話,那么我們獲取到的中間值就是 N/2 + 1的節點,與我們的計算會相差一位,不過這不是重點,重要的是我們需要體會里面的思想。

 

 if(second==null){
                throw new NullPointerException();
            }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM