連結串列反轉的兩種實現方法,後一種擊敗了100%的使用者!
阿新 • • 發佈:2020-10-13
連結串列反轉是一道很基礎但又非常熱門的演算法面試題,它也在《劍指Offer》的第 24 道題出現過,至於它有多熱(門)看下面的榜單就知道了。
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601347881236-317636b8-785e-4993-b1f1-b7edc228d076.png#align=left&display=inline&height=182&margin=%5Bobject%20Object%5D&name=image.png&originHeight=363&originWidth=1702&size=62857&status=done&style=none&width=851)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601347896337-0001da74-2582-45f9-acdb-780f8763b3c1.png#align=left&display=inline&height=168&margin=%5Bobject%20Object%5D&name=image.png&originHeight=335&originWidth=1704&size=59434&status=done&style=none&width=852)
從牛客網的資料來看,**連結串列反轉的面試題分別霸佔了【上週考過】和【研發最愛考】的雙重榜單**,像網易、位元組等知名網際網路公司都考過,但通過率卻低的只有 30%,所以本文我們就來學習一下反轉連結串列的兩種實現方法。
> 排行榜資料:[https://www.nowcoder.com/activity/oj](https://www.nowcoder.com/activity/oj)
### 題目
標題:劍指 Offer 24. 反轉連結串列
描述:定義一個函式,輸入一個連結串列的頭節點,反轉該連結串列並輸出反轉後連結串列的頭節點。
示例:
> 輸入: 1->2->3->4->5->NULL
>
> 輸出: 5->4->3->2->1->NULL
LeetCode 連結:[https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/](https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/)
### 實現方式一:Stack
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601354613664-d87fc4a5-c572-4e4c-b4f4-0603fbf5c0da.png#align=left&display=inline&height=96&margin=%5Bobject%20Object%5D&name=image.png&originHeight=191&originWidth=518&size=11362&status=done&style=none&width=259)
全部入棧:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601354755540-2c0c7665-5643-4b2c-bab6-2ed8d6a0785f.png#align=left&display=inline&height=207&margin=%5Bobject%20Object%5D&name=image.png&originHeight=414&originWidth=338&size=9133&status=done&style=none&width=169)
因為棧是先進後出的資料結構,因此它的執行過程如下圖所示:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601355289132-1234c65a-696f-4c55-ab7f-2a49ab429f84.png#align=left&display=inline&height=271&margin=%5Bobject%20Object%5D&name=image.png&originHeight=541&originWidth=444&size=18715&status=done&style=none&width=222)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601355349565-04edac1e-8252-4973-996e-c3f96875382f.png#align=left&display=inline&height=269&margin=%5Bobject%20Object%5D&name=image.png&originHeight=538&originWidth=598&size=20647&status=done&style=none&width=299)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601355468506-9c7d3486-0cf4-4cd9-9669-cdc4f908073b.png#align=left&display=inline&height=271&margin=%5Bobject%20Object%5D&name=image.png&originHeight=541&originWidth=877&size=27331&status=done&style=none&width=438.5)
最終的執行結果如下圖所示:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601355760993-988b8b0b-2858-46aa-80ba-6c378fc69a41.png#align=left&display=inline&height=322&margin=%5Bobject%20Object%5D&name=image.png&originHeight=643&originWidth=1207&size=38509&status=done&style=none&width=603.5)
實現程式碼如下所示:
```java
public ListNode reverseList(ListNode head) {
if (head == null) return null;
Stack stack = new Stack<>();
stack.push(head); // 存入第一個節點
while (head.next != null) {
stack.push(head.next); // 存入其他節點
head = head.next; // 指標移動的下一位
}
// 反轉連結串列
ListNode listNode = stack.pop(); // 反轉第一個元素
ListNode lastNode = listNode; // 臨時節點,在下面的 while 中記錄上一個節點
while (!stack.isEmpty()) {
ListNode item = stack.pop(); // 當前節點
lastNode.next = item;
lastNode = item;
}
lastNode.next = null; // 最後一個節點賦為null(不然會造成死迴圈)
return listNode;
}
```
LeetCode 驗證結果如下圖所示:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601284923351-06558bae-bca5-4b32-8dc4-f56bf25adc01.png#align=left&display=inline&height=178&margin=%5Bobject%20Object%5D&name=image.png&originHeight=356&originWidth=832&size=37403&status=done&style=none&width=416)
### 實現方式二:遞迴
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601430645953-18dc097e-6cba-4f39-9e6b-cd6115358d53.png#align=left&display=inline&height=93&margin=%5Bobject%20Object%5D&name=image.png&originHeight=186&originWidth=666&size=9937&status=done&style=none&width=333)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601430981845-8752059b-7d36-416c-8880-963763aff3ee.png#align=left&display=inline&height=80&margin=%5Bobject%20Object%5D&name=image.png&originHeight=160&originWidth=486&size=8051&status=done&style=none&width=243)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601430995922-9418438a-4cfb-4d71-b5a1-c7969e6f7f78.png#align=left&display=inline&height=84&margin=%5Bobject%20Object%5D&name=image.png&originHeight=168&originWidth=629&size=9951&status=done&style=none&width=314.5)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601431050890-c4fc76e4-33f4-4f0c-ab37-41a4a9292c6f.png#align=left&display=inline&height=81&margin=%5Bobject%20Object%5D&name=image.png&originHeight=161&originWidth=759&size=11022&status=done&style=none&width=379.5)
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601431134483-8495a112-0524-4085-abc8-5d7dc995563c.png#align=left&display=inline&height=85&margin=%5Bobject%20Object%5D&name=image.png&originHeight=169&originWidth=581&size=9129&status=done&style=none&width=290.5)
實現程式碼如下所示:
```java
public static ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
// 從下一個節點開始遞迴
ListNode reverse = reverseList(head.next);
head.next.next = head; // 設定下一個節點的 next 為當前節點
head.next = null; // 把當前節點的 next 賦值為 null,避免迴圈引用
return reverse;
}
```
LeetCode 驗證結果如下圖所示:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1601342696476-901c9450-45d1-4201-9be9-1257e3f6c9d9.png#align=left&display=inline&height=132&margin=%5Bobject%20Object%5D&name=image.png&originHeight=264&originWidth=745&size=25697&status=done&style=none&width=372.5)
## 總結
本文我們分別使用了 `Stack` 和遞迴的方法實現了連結串列反轉的功能,其中 `Stack` 的實現方式是利用了棧後進先出的特性可以直接對連結串列進行反轉,實現思路和實現程式碼都比較簡單,但在效能和記憶體消耗方面都不是很理想,可以作為筆試的保底實現方案;而遞迴的方式在效能和記憶體消耗方面都有良好的表現,同時它的實現程式碼也很簡潔,讀者只需理解程式碼實現的思路即可。