題目描述(題目難度,困難)
給出一個鏈表,每 k 個節點一組進行翻轉,并返回翻轉后的鏈表。
k 是一個正整數,它的值小于或等于鏈表的長度。如果節點總數不是 k 的整數倍,那么將最后剩余節點保持原有順序。
示例 :
給定這個鏈表:1->2->3->4->5
當 k = 2 時,應當返回: 2->1->4->3->5
當 k = 3 時,應當返回: 3->2->1->4->5
說明 :
- 你的算法只能使用常數的額外空間。
- 你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
示例代碼
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
// 一組結點的鏈表轉置函數
private void reverse(ListNode header, ListNode tail) {
ListNode p = null, q = null, r = null;
p = header.next;
q = p.next; // 主要是要改變 q 指針的指向
r = q.next;
header.next = tail;
p.next = tail.next;
while (true) {
q.next = p;
p = q;
if (p == tail) break;
q = r;
r = r.next;
}
}
public ListNode reverseKGroup(ListNode head, int k) {
if (k <= 1) {
return head;
}
ListNode header = new ListNode(0);
header.next = head;
ListNode p = header;
ListNode q = header;
ListNode r = null;
while (true) {
for (int i = 0; i < k; ++i) {
q = q.next;
if (q == null) return header.next; // 后面的結點不夠一組,不需要翻轉,可以直接返回結果了
}
r = p.next;
reverse(p, q);
p = r;
q = r;
}
}
}
思路解析
翻轉鏈表算得上是一道常見的鏈表類面試題了,題目難度本身算不上困難。這種鏈表類問題其實只要做到細心和注意指針判空,問題就應該不大。正是因為常見,所以翻轉鏈表的代碼求解一定要熟練掌握。
鏈表類問題做到現在,總結出了如下兩點技巧:
- 處理鏈表問題時,一般都給輸入的鏈表加一個頭結點,這樣就可以在一個循環里統一處理所有的結點,而不需要去特殊處理原本的頭節點。
- 處理鏈表類問題,我覺得使用 while(true){} 這種死循環最順手。因為在你寫出循環體的代碼之前,你很難立馬準確的判斷循環截止條件是什么,與其在寫循環體的時候老是操心 while() 里的循環截止條件,邊寫循環體邊把循環截止條件改來改去。還不如順著循環體的思路一路順暢的寫下去,然后在需要跳出循環時,直接使用 if 語句判斷是否應該跳出循環即可,而且提前跳出循環也能避免一些不必要的麻煩。