《原神攻略》踏鞴砂雙藏鏡挑戰心得
題目
給定單鏈表的頭節點 head
,請反轉連結串列,並返回反轉後的連結串列的頭節點。
示例 1:
輸入:head = [1,2,3,4,5]
輸出:[5,4,3,2,1]
示例 2:
輸入:head = [1,2]
輸出:[2,1]
示例 3:
輸入:head = []
輸出:[]
提示:
- 連結串列中節點的數目範圍是
[0, 5000]
-5000 <= Node.val <= 5000
進階:連結串列可以選用迭代或遞迴方式完成反轉。你能否用兩種方法解決這道題?
注意:本題與主站 206 題相同: https://leetcode-cn.com/problems/reverse-linked-list/
Related Topics
遞迴
連結串列
程式碼實現
class Solution { public ListNode reverseList(ListNode head) { ListNode pre = null; ListNode cur =head; while (cur != null) { ListNode tmp = cur.next; cur.next = pre;//切斷原有指向 pre = cur; cur = tmp; } return cur; } }
動畫演示
利用外部空間
這種方式很簡單,先申請一個動態擴容的陣列或者容器,比如 ArrayList 這樣的。
然後不斷遍歷連結串列,將連結串列中的元素新增到這個容器中。
再利用容器自身的 API,反轉整個容器,這樣就達到反轉的效果了。
最後同時遍歷容器和連結串列,將連結串列中的值改為容器中的值。
因為此時容器的值是:
5 4 3 2 1
連結串列按這個順序重新被設定一邊,就達到要求啦。
當然你可以可以再新建立 N 個節點,然後再返回,這樣也可以達到目的。
這種方式很簡單,但你在面試中這麼做的話,面試官 100% 會追問是否有更優的方式,比如不用外部空間。所以我就不做程式碼和動畫演示了,下面來看看如何用 O(1)
雙指標迭代
我們可以申請兩個指標,第一個指標叫 pre,最初是指向 null 的。
第二個指標 cur 指向 head,然後不斷遍歷 cur。
每次迭代到 cur,都將 cur 的 next 指向 pre,然後 pre 和 cur 前進一位。
都迭代完了(cur 變成 null 了),pre 就是最後一個節點了。
動畫演示如下:
迭代.gif
動畫演示中其實省略了一個tmp
變數,這個tmp
變數會將cur
的下一個節點儲存起來,考慮到一張動畫放太多變數會很混亂,所以我就沒加了,具體詳細執行過程,請點選下面的幻燈片檢視。
程式碼實現:
class Solution {
public ListNode reverseList(ListNode head) {
//申請節點,pre和 cur,pre指向null
ListNode pre = null;
ListNode cur = head;
ListNode tmp = null;
while(cur!=null) {
//記錄當前節點的下一個節點
tmp = cur.next;
//然後將當前節點指向pre
cur.next = pre;
//pre和cur節點都前進一位
pre = cur;
cur = tmp;
}
return pre;
}
}
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 申請兩個節點,pre和 cur,pre指向None
pre = None
cur = head
# 遍歷連結串列,while迴圈裡面的內容其實可以寫成一行
# 這裡只做演示,就不搞那麼騷氣的寫法了
while cur:
# 記錄當前節點的下一個節點
tmp = cur.next
# 然後將當前節點指向pre
cur.next = pre
# pre和cur節點都前進一位
pre = cur
cur = tmp
return pre
遞迴解法
這題有個很騷氣的遞迴解法,遞迴解法很不好理解,這裡最好配合程式碼和動畫一起理解。
遞迴的兩個條件:
- 終止條件是當前節點或者下一個節點==null
- 在函式內部,改變節點的指向,也就是 head 的下一個節點指向 head 遞迴函式那句
head.next.next = head
很不好理解,其實就是 head 的下一個節點指向head。
遞迴函式中每次返回的 cur 其實只最後一個節點,在遞迴函式內部,改變的是當前節點的指向。
動畫演示如下:
遞迴.gif
幻燈片演示
感謝@zhuuuu-2的建議,遞迴的解法光看動畫比較容易理解,但真到了程式碼層面理解起來可能會有些困難,我補充了下遞迴呼叫的詳細執行過程。
以1->2->3->4->5
這個連結串列為例,整個遞迴呼叫的執行過程,對應到程式碼層面(用java做示範)是怎麼執行的,以及遞迴的呼叫棧都列出來了,請點選下面的幻燈片檢視吧。
程式碼實現:
class Solution {
public ListNode reverseList(ListNode head) {
//遞迴終止條件是當前為空,或者下一個節點為空
if(head==null || head.next==null) {
return head;
}
//這裡的cur就是最後一個節點
ListNode cur = reverseList(head.next);
//這裡請配合動畫演示理解
//如果連結串列是 1->2->3->4->5,那麼此時的cur就是5
//而head是4,head的下一個是5,下下一個是空
//所以head.next.next 就是5->4
head.next.next = head;
//防止連結串列迴圈,需要將head.next設定為空
head.next = null;
//每層遞迴函式都返回cur,也就是最後一個節點
return cur;
}
}
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 遞迴終止條件是當前為空,或者下一個節點為空
if(head==None or head.next==None):
return head
# 這裡的cur就是最後一個節點
cur = self.reverseList(head.next)
# 這裡請配合動畫演示理解
# 如果連結串列是 1->2->3->4->5,那麼此時的cur就是5
# 而head是4,head的下一個是5,下下一個是空
# 所以head.next.next 就是5->4
head.next.next = head
# 防止連結串列迴圈,需要將head.next設定為空
head.next = None
# 每層遞迴函式都返回cur,也就是最後一個節點
return cur
本文來自部落格園,作者:軟體工程師蝸小牛,轉載請註明原文連結:https://www.cnblogs.com/SoftwareEngineerWXN/p/15273102.html