(Java) LeetCode 206. Reverse Linked List —— 反轉鏈表


Reverse a singly linked list.

Example:

Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL

Follow up:

A linked list can be reversed either iteratively or recursively. Could you implement both?


 

反轉鏈表,鏈表算法里必備技能點,基礎的重中之重。很多有關鏈表反轉、翻轉的問題都是在這個基礎上進行思維的延伸。既然要求用兩個方法做,那就先說迭代再說遞歸。

 

解法一,迭代:

首先,肯定知道最后返回的結果是輸入鏈表的鏈表尾節點。但先找到尾節點是很難繼續實現的,因為鏈表沒有辦法高效獲取前驅。往往這類問題很多時候都要想到建立一個新的節點,之后在遍歷輸入的時候重新組織節點順序,將節點掛在新節點上。所以高效的做法是在遍歷鏈表的過程中,一個一個的把輸入鏈表的節點放到一個新的鏈表頭部。所以思路就是建立一個新的鏈表頭,每次遍歷輸入鏈表的節點都把他放到新鏈表的頭部,這樣遍歷完成后就獲得了反轉的鏈表。詳細代碼注釋見下。

 

解法二,遞歸:

每次想着用遞歸解法我習慣於用數學歸納法的思維去思考。先想輸入規模最小的情況,再想比較general的情況。就本題來說,如果輸入的是null或者單節點鏈表,必然是返回其本身。如果至少有兩個節點,那么才開始遞歸。想一下,遞歸后的結果一定是一個規模更小問題的結果。即如果輸入有k個節點,那么遞歸調用程序,輸入原鏈表第二個節點所返回的結果,是一個反轉后的,擁有k-1個節點的鏈表的首節點 —— 規模更小的問題的結果。那么如果把這個遞歸調用后返回的頭節點所指向鏈表的尾節點的next域,指向被調用的節點的前驅,就相當反轉了k個節點的鏈表。即利用k-1的結果去完成了k的問題。所以想到這里,在遞歸函數里要做的就是三件事:第一,記錄即將被遞歸調用節點的前驅(或者換句話說,建立個新的節點指向輸入的下一個節點,之后遞歸調用那個新節點);第二,遞歸調用輸入的下一個節點;第三,將返回結果的末尾指向記錄好的前驅節點,完成反轉。

這里需要注意的只有第三步,如何找到返回的結果鏈表的末尾。還是要回歸到遞歸的本質,即返回的結果是一個已經反轉完成的鏈表的首節點。反轉完成的意思就是我們輸入一個以節點S為頭結點,節點E為尾結點的鏈表,那么調用后返回的節點是E,而S經過調用后變成了尾節點。即,遞歸調用時的輸入本身,即是調用完成后我們需要的尾節點!所以我們並不需要每一次都去尋找遞歸調用后結果的尾節點,只需要直接利用遞歸調用的輸入即可,因為這個輸入就是調用完成的尾節點。詳細代碼注釋見下。

 


解法一(Java)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode first = head;
        ListNode reverseHead = null; //建立一個新的節點用來存放結果
        while (first != null) { //遍歷輸入鏈表,開始處理每一個節點
            ListNode second = first.next; //先處理第一個節點first,所以需要一個指針來存儲first的后繼
            first.next = reverseHead; //將first放到新鏈表頭節點的頭部
            reverseHead = first; //移動新鏈表的頭指針,讓它始終指向新鏈表頭部
            first = second; //繼續處理原鏈表的節點,即之前指針存放的后繼,循環往復
        }
        return reverseHead;
    }
}

 

解法二(Java)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head; //處理最小輸入的情況,即空鏈表和單節點鏈表
        ListNode second = head.next; //將即將被調用的下一個節點分離,即將下一個調用的輸入存在second里
        ListNode reverseHead = reverseList(second); //將調用后的結果存儲,這個結果就是最終結果。之后利用遞歸,調用剛才存好的輸入
        second.next = head; //上面一步的調用已經完成以second為首的鏈表的反轉,所以現在second變成了反轉完成后的尾節點
                 //把這個尾節點的next指向一開始輸入的前驅,即head,完成整個鏈表反轉
head.next = null; //最開始的頭節點要變成尾節點,即在后面補null使鏈表終結 return reverseHead; } }

 


免責聲明!

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



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