實現反轉連結串列--遞迴、迭代、雙指標、棧

coder_soldier發表於2021-01-02

描述

2021年,新年第二天,吃過晚飯,沒想到一道簡簡單單的題把我卡了半天,於是乎記錄一下。建議自己先寫寫試試。

題目:

定義一個函式,輸入一個連結串列的頭節點,反轉該連結串列並輸出反轉後連結串列的頭節點。

示例:
輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL

連結串列結構:

class ListNode{
    int val;
    ListNode next;
    public ListNode(int val){
        this.val = val;
    }
}

public ListNode reverseList(ListNode head) {
	//實現程式碼
}

其實開始想法是遍歷連結串列,用棧儲存其中的int值,然後再從棧中取出int生成新的連結串列。確實可以實現,但是效率太低,肯定不是最優的。最後再貼出棧的實現方法。

遞迴

相信不少同學和我一樣,遇到遞迴的問題總是很頭疼,而一看解答,卻發現大佬們幾行遞迴程式碼就優雅的解決了問題。遇到遞迴問題很容易進入思維誤區,所謂遞迴,程式反覆呼叫自身即是遞迴。既然能夠呼叫自身說明它每一級的功能都是一樣的,因此我們只需要關注一級遞迴的解決過程即可。

遞迴把握好三個關鍵點就可以了:

  1. 找整個遞迴的終止條件:遞回應該在什麼時候結束?
  2. 找返回值:應該給上一級返回什麼資訊?
  3. 本級遞回應該做什麼:在這一級遞迴中,應該完成什麼任務?

這裡推薦一個部落格,寫的非常清晰,套路解決遞迴問題

對於該題分析:

終止條件:節點為空或者它的下一個節點為空
返回值:返回給上一級已經反轉完成的連結串列
本級應該做什麼:反轉後面的節點,並且將當前節點插入到它後面節點的後面

class Solution {
    public ListNode reverseList(ListNode head) {
    	//終止條件
        if(head == null || head.next == null){
            return head;
        }
        //將後面的節點反轉
        ListNode next = reverseList(head.next);
        //將當前節點插入它後面的節點
        head.next.next = head;
        //防止產生迴圈連結串列
        head.next = null;
        return next;
    }
}

記得上學時候資料結構老師告訴我們,關於連結串列的操作,一定要在紙上把過程先畫出來,再寫程式。不過這次畫翻車了,這裡記錄下我進入的誤區,因為head.next.next = head這行讓我想了好久,畫圖表示一下當時我的理解:

在這裡插入圖片描述
假設當前傳入的是2節點,假設它後面已經反轉完成,所以畫出了上面的圖,而我一直再想怎麼讓5去指向2,想著想著就迷路了。這裡就是一個思維誤區了,下意識的以為2就是已經指向了5,但是實際應該是下面這樣:

在這裡插入圖片描述
實際上2指向的是3節點,因為2的next並沒有變啊,這樣再看head.next.next = head是不是就瞬間明白了。而head.next = null;也懂了吧,就是防止3指向2,2又指向3。

在這裡插入圖片描述

迭代

一般來說遞迴能夠實現的迭代也可以,可以使用雙指標,也可以使用棧。先說雙指標:

考慮遍歷連結串列,並在訪問各節點時修改 next 引用指向:

class Solution {
    public ListNode reverseList(ListNode head) {
        //記錄前一個節點用的
        ListNode pre = null;
        //從頭節點開始
        ListNode cur = head;
        //臨時記錄下一個節點
        ListNode temp = null;
        while(cur != null){
        	//記錄下一個節點
            temp = cur.next;
            //將當前節點指向上前節點,實現反轉
            cur.next = pre;
            //將當前節點交給pre,為下一次的迴圈做準備
            pre = cur;
            //移動到下一個節點
            cur = temp;
        }
        return pre;
    }
}

具體思路已經加到註釋上了,不再贅述了,看下時間:

在這裡插入圖片描述
棧是一種先進後出的資料結構,下面是使用棧來實現反轉,思路就是遍歷儲存到棧中,再遍歷棧,生成新的連結串列:

class Solution {
    public ListNode reverseList(ListNode head) {
        Stack<Integer> stack = new Stack<>();
        while(head != null){
            stack.add(head.val);
            head = head.next;
        }
        ListNode temp = new ListNode(0),result = temp;
        while(stack.size() != 0){
            temp.next = new ListNode(stack.pop());
            temp = temp.next;
        }
        return result.next;
    }
}

棧沒啥好說的大家隨便看看,果然耗時最長。

在這裡插入圖片描述
記錄完畢。
歲月流逝忽已暮,皆因惆悵不知路。
2021年加油吧,打工人!

相關文章