反轉鏈表這題真的是面試很是喜歡考的了,這題看起來簡單,可是能用兩種方法一遍 bug free 也是不容易的,面試的時候能夠篩下來一大批人,不管是對 junior 仍是 senior 面試都很愛考。java
今天齊姐就帶你梳理清楚思路,思路清楚了才能寫碼若有神。node
這是從力扣中文站上截下來的,可是這個輸出不太形象。git
對鏈表的反轉,並非要把它實際翻個個,只是動一動 next 指針就行了。github
咱們先看對數組進行反轉。面試
數組是一個物理上連續存儲的數據結構,反轉以後原來放 1 的位置就變成了放 5.數組
可是鏈表並非,由於鏈表在物理上是不連續的,它的每一個單元 ListNode
是經過 next
指針鏈接在一塊兒的,而每一個 ListNode 之間在內存裏並不必定是挨着的。數據結構
因此反轉鏈表,就不是非要把 1 的位置放 5,由於它們想在哪在哪。學習
那麼怎麼保證這個順序呢?spa
沿着 next 指針的方向走下去,就是鏈表的順序。這也就保證了,只要咱們拿到了頭節點,就掌控了整個 LinkedList.3d
那麼題目中的例子,形象點是這個樣子滴:
也就是元素本身不用動,只須要動動小指針,就是反轉了。
遞歸的三步驟你們還記得嗎?
Base case + 拆解 + 組合
不記得的趕忙在公衆號內回覆「遞歸」二字,獲取遞歸的入門篇詳解。
那麼咱們來看這個題的:
當只有一個 node,或者沒有 node 了唄,也就是
if(node == null || node.next == null) { return node; }
其實呢,只剩一個 node 的時候嚴格來說並非 base case,而是 corner case,
由於它本能夠再 break down 到 node == null 的,但由於後面有對 node.next 的 dereference 操做,因此不能省略。
遞歸的拆解就是把大問題,分解成小一點點的問題,直到 base case 能夠返回,進行第三步的組合。
那麼這裏就是
組合的意思是,假設咱們可以拿到小問題的解,那麼用小問題的解去構造大問題的解。
那麼這個問題裏如何構造呢?
這裏很明顯,在 2 後面接上 1 就好了,可是怎麼拿到 2 呢?
別忘了,原問題裏,此時還有 1 指向 2 呢~
也就是 node1.next = node2
,
而後把 2 指向 1:node2.next = node1
合起來就是:node1.next.next = node1
思路清楚就不繞,看着以爲繞的就是沒想清楚哼~
遞歸的代碼寫起來都很簡潔:
class Solution { public ListNode reverseList(ListNode head) { if(head == null || head.next == null) { return head; } ListNode newHead = reverseList(head.next); head.next.next = head; head.next = null; return newHead; } }
咱們在「遞歸」這篇文章裏說過,遞歸的時間複雜度分析方法就是把遞歸樹畫出來,每一個節點的時間加起來就好了。
這個遞歸樹是一個很簡單的單項鍊表,每一個節點上作的就是那三行代碼,也就是「組合」作的事,即 O(1) 的時間,總共有 n 個節點,那麼總的時間就是 O(n).
那看遞歸樹也很明顯了,每一層也就用了點小變量,是 O(1),因此總的空間共是 O(n).
(誰能告訴我這個中文的專業說法。。
Iterative 的思路就是:
過一遍這個 Linked List,邊過邊把這個 node 的 next 指針指向前面那個 node,直到過徹底部。
這樣說太抽象,面試時也是,直接過例子。
那也就是把 1 的 next 指針翻過來指向 NULL;
把 2 的 next 指針翻過來指向 1;
把 3 的 next 指針翻過來指向 2;
...
因此咱們還須要一個變量來記錄當前 node 的前一個 node,不妨設爲 prev.
同時呢,一旦咱們把指針翻轉過去,後面的那個 node 就丟了有木有!因此還須要有個額外的變量事先記錄下後面的 node,設爲 nxt,這樣纔不至於走丟~
翻轉箭頭:把 1 的 next 指針指向 NULL;
這樣子,同時咱們也明確了,prev 的初始化應該是 NULL.
而後把這仨變量都移動到下一個位置:
翻轉箭頭:把 2 的 next 指針指向 1,
而後三人行:
翻轉箭頭:把 3 的 next 指針指向 2,
再齊步走:
再把 4 的反過來:
再日後走:
再把 5 的 next 反過來:
可是由於咱們的 while 循環包含了
「翻轉箭頭」+「三人行」
兩個步驟,因此還須要走完最後一個三人行,變成:
不少同窗搞不清楚這個 while 循環的結束條件,其實很簡單,你就走個例子畫畫圖嘛!
那結束條件就是 curr = null
的時候,
最後返回的是 prev
.
好了,看代碼吧:
class Solution { public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while(curr != null) { ListNode nxt = curr.next; curr.next = prev; // 翻轉箭頭 prev = curr; //三人行 curr = nxt; //三人行 } return prev; } }
這裏的時間複雜度很明顯了,就是過了一遍這個鏈表,因此是 O(n).
空間是 O(1).
若是你喜歡這篇文章,記得給我點贊留言哦~大家的支持和承認,就是我創做的最大動力,咱們下篇文章見!
我是小齊,紐約程序媛,終生學習者,天天晚上 9 點,雲自習室裏不見不散!
更多幹貨文章見個人 Github: https://github.com/xiaoqi6666...