1. 程式人生 > 遊戲攻略 >《原神攻略》踏鞴砂雙藏鏡挑戰心得

《原神攻略》踏鞴砂雙藏鏡挑戰心得

題目

給定單鏈表的頭節點 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 

遞迴解法

這題有個很騷氣的遞迴解法,遞迴解法很不好理解,這裡最好配合程式碼和動畫一起理解。
遞迴的兩個條件:

  1. 終止條件是當前節點或者下一個節點==null
  2. 在函式內部,改變節點的指向,也就是 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