本文首發自公衆號:承香墨影(ID:cxmyDev),歡迎關注。java
鏈表做爲一種基本的數據結構,自己理解起來很簡單。它經過指針,將一組零散的內存空間(結點),串聯起來,組成一個數據結構。web
在面試的算法題中,常常會碰到鏈表相關的面試題。雖然鏈表的結構比較好理解,可是鏈表的題仍是比較考教代碼能力的。一些單鏈表的題,指針指來指去,很容易就把結點的 next 指針弄丟了,形成鏈表斷裂。面試
鏈表翻轉是一個面試中常常會碰到的題,在以前的文章中,也聊過單鏈表翻轉、鏈表雙雙翻轉,今天再來聊聊它們的「升級版」,以 K 個爲一組,翻轉鏈表,同時這也是 LeetCode 的第 25 題。算法
以 K 個結點爲一組,將給定的單鏈表進行翻轉。有點相似以前的鏈表兩兩翻轉,只是那時的 K = 2
。而在這道題中,K 變成一個外部傳入的正整數,它是一個可變的值,而且小於或者等於鏈表的長度。數據結構
這道算法題,會用到以前的知識,既然 K 是可變的,咱們沒法估計 K 的大小,可是咱們能夠將原始鏈表,以 K 個結點爲一組,執行單鏈表翻轉的邏輯。app
這就要用到以前《單鏈表翻轉》的技巧了,不瞭解的建議先讀讀以前的文章。學習
既然須要將原始鏈表先以 K 個結點分組,再依次執行單鏈表翻轉,在每組字鏈表翻轉以後,還須要將它們再串起來,否者鏈表不就斷裂了麼。spa
這就須要使用兩個變量 prev 和 end,分別記錄子鏈表的前驅結點,以及子鏈表的尾結點。有了 end 這個子鏈表的尾結點,就能夠很容易經過 end.next 拿到下一個子鏈表的頭結點。指針
依據這幾個結點,就能夠完成子鏈表的翻轉,以及保證在子鏈表翻轉後依然能夠串起來。code
另外有一個特殊處理的地方,當原始鏈表以 K 個結點爲一組分組時,末尾不滿一組的子鏈表,保持原樣不進行翻轉。
參考鏈表兩兩翻轉的思路,爲了保證咱們的代碼邏輯統一,咱們增長一個虛擬的頭結點 dummy,來方便咱們編寫代碼。
直接上代碼,細節都在註釋裏。
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
// 增長虛擬頭結點
ListNode dummy = new ListNode(0);
dummy.next = head;
// 定義 prev 和 end 結點
ListNode prev = dummy;
ListNode end = dummy;
while(end.next != null) {
// 以 k 個結點爲條件,分組子鏈表
for (int i = 0; i < k && end != null; i++)
end = end.next;
// 不足 K 個時不處理
if (end == null)
break;
// 處理子鏈表
ListNode start = prev.next;
ListNode next = end.next;
end.next = null;
// 翻轉子鏈表
prev.next = reverseList(start);
// 將子連表先後串起來
start.next = next;
prev = start;
end = prev;
}
return dummy.next;
}
// 遞歸完成單鏈表翻轉
private ListNode reverseList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode p = reverseList(head.next);
head.next.next = head;
head.next = null;
return p;
}
}
複製代碼
這裏單鏈表的翻轉,使用了遞歸,通常 K 值都不會太大,因此用遞歸沒問題,固然你也能夠換成循環實現。對細節不瞭解的,能夠參見《單鏈表翻轉》。
到這裏單鏈表,按 K 分組翻轉的具體思路和代碼,就介紹清楚了。我這種處理方式可能不是最高效的,可是應該是比較清晰的。
另外在實際面試中,其實不少場景下,都不會直接出 leetcode 上的算法題,都會稍微變種一下,可是你們要學會將複雜問題,轉化爲簡單問題來解決。
到這裏,鏈表翻轉的基礎題,基本上就說清楚了,包含三個篇文章:
今天就到這裏,有任何問題歡迎留言討論。
本文對你有幫助嗎?留言、轉發、收藏是最大的支持,謝謝!
公衆號後臺回覆成長『成長』,將會獲得我準備的學習資料。