【日拱一卒】鏈表——鏈表反轉
需求
實現鏈表的反轉
輸入:1->2->3->4->5
輸出:5->4->3->2->1
難點
如果換成數據反轉,你會嗎(傻子才不會)。
按照常規思維,鏈表反轉需要知道最後一個元素,然後從最後一個元素依次往前找,直到遍歷到第一個元素即完成反轉。
但是這裡並不是雙向鏈表,即使找到最後一個元素也找不到前繼節點。再者,即使是雙向鏈表,通過找到尾結點再往回遍歷聽著也不像是很高端的做法。
思路
除了找到尾結點進行反轉的思路,我們是否可以考慮,在遍歷的同時就去做反轉的操作,類似
1->2->3->4->5
2->1->3->4->5
3->2->1->4->5
4->3->2->1->5
5->4->3->2->1
直覺告訴我們,肯定沒問題,但是具體怎麼做呢,看圖
第一步:畫出輸入鏈表結構
第二步:初始化
注意
-
為了不干擾head指針,重新定義一個新的cur指針,用於後面的遍歷。pre指針表示最終反轉後的鏈表指針
-
指針存儲了指向變數的地址,比如這裡的cur存儲的是head.next的地址
第三步:將1節點的next指向pre(即null)
注意
- 此時的pre指向的是一個null的地址,需要注意的是pre是一個指針,pre還可以指向其他節點(參見下一步操作)
第四步:將pre指向1節點
注意
- 將pre指向指向節點1,pre之前指向的是null,現在指向節點1,所以圖中虛線部分的連接就不存在了
- 這時候,我們成功的將第一個節點納入最終反轉鏈表pre中
第五步:解決指針丟失問題
按照前面幾步的思路,我們依次遍歷後面的節點,並對遍歷的節點做如上步驟的處理,這樣,最終我們就會得到反轉後的鏈表pre。
但是,這裡有個指針丟失的問題。
原來cur指向節點1,cur.next指向節點2,但是在第三步的時候將cur.next指向了null。所以使用cur.next就無法滿足我們遍歷節點2的需求了。
這時候,我們需要一個新指針,用來保證鏈表遍歷的時候不會斷掉,如下圖
注意
- 這裡next指針需要在第二步初始化的時候就做好,不然這時候做next=cur.next就無法指向節點2了
第六步:開啟下一次迭代
注意
- cur作為一個哨兵節點,指向節點2,開始像處理節點1一樣處理節點2,直到遍歷完所有節點
程式碼實現
版本1
func reversrList(head *ListNode) *ListNode { cur := head var pre *ListNode = nil for cur != nil { next := cur.Next cur.Next = pre pre = cur cur = next } return pre }
對照該程式碼實現和上面幾個步驟,基本就可以摸透鏈表反轉的思路了。
這裡還有一個更加簡潔的程式碼實現
版本2
func reversrList(head *ListNode) *ListNode { cur := head var pre *ListNode = nil for cur != nil { pre, cur, cur.Next = cur, cur.Next, pre } return pre }
個人感覺不是很好理解,所以將其拆解成上面的程式碼實現。
不忘初心
老王:你不好好種地,你以後長大能幹什麼
小王:學演算法
老王:學演算法?!你數組、鏈表、棧、隊列、堆、排序、查找都整不明白,你學什麼演算法
小王:我只學鏈表反轉
老王:。。。
如果您覺得閱讀本文對您有幫助,請點一下「推薦」按鈕,您的「推薦」將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,並和您一起分享我日常閱讀過的優質文章。