前幾天有個朋友去面試字節跳動,面試官問了他一道鏈表相關的算法題,不過他一時之間沒作出來,就來問了我一下,感受這道題還不錯,拿來說一講。面試
這實際上是一道變形的鏈表反轉題,大體描述以下算法
給定一個單鏈表的頭節點 head,實現一個調整單鏈表的函數,使得每K個節點之間爲一組進行逆序,而且從鏈表的尾部開始組起,頭部剩餘節點數量不夠一組的不須要逆序。(不能使用隊列或者棧做爲輔助)函數
例如:spa
鏈表:1->2->3->4->5->6->7->8->null, K = 3。那麼 6->7->8,3->4->5,1->2各位一組。調整後:1->2->5->4->3->8->7->6->null。其中 1,2不調整,由於不夠一組。code
# 解答blog
這道題的難點在於,是從鏈表的尾部開始組起的,而不是從鏈表的頭部,若是是頭部的話,那咱們仍是比較容易作的,由於你能夠遍歷鏈表,每遍歷 k 個就拆分爲一組來逆序。可是從尾部的話就不同了,由於是單鏈表,不能日後遍歷組起。不過這道題確定是用遞歸比較好作,對遞歸不大懂的建議看我以前寫的一篇文章爲何你學不會遞歸?告別遞歸,談談個人一些經驗,這篇文章寫了關於遞歸的一些套路。遞歸
先作一道相似的反轉題隊列
在作這道題以前,咱們不仿先來看看若是從頭部開始組起的話,應該怎麼作呢?例如:鏈表:1->2->3->4->5->6->7->8->null, K = 3。調整後:3->2->1->6->5->4->7->8->null。其中 7,8不調整,由於不夠一組。leetcode
對於這道題,若是你不知道怎麼逆序一個單鏈表,那麼能夠看一下我以前寫的如何優雅着反轉單鏈表rem
這道題咱們能夠用遞歸來實現,假設方法reverseKNode()的功能是將單鏈表的每K個節點之間逆序(從頭部開始組起的哦);reverse()方法的功能是將一個單鏈表逆序。
那麼對於下面的這個單鏈表,其中 K = 3。
咱們把前K個節點與後面的節點分割出來:
temp指向的剩餘的鏈表,能夠說是原問題的一個子問題。咱們能夠調用reverseKNode()方法將temp指向的鏈表每K個節點之間進行逆序。再調用reverse()方法把head指向的那3個節點進行逆序,結果以下:
接着,咱們只須要把這兩部分給鏈接起來就能夠了。最後的結果以下:
代碼以下:
//k個爲一組逆序
public ListNode reverseKGroup(ListNode head, int k) {
ListNode temp = head;
for (int i = 1; i < k && temp != null; i++) {
temp = temp.next;
}
//判斷節點的數量是否可以湊成一組
if(temp == null)
return head;
ListNode t2 = temp.next;
temp.next = null;
//把當前的組進行逆序
ListNode newHead = reverseList(head);
//把以後的節點進行分組逆序
ListNode newTemp = reverseKGroup(t2, k);
// 把兩部分鏈接起來
head.next = newTemp;
return newHead;
}
//逆序單鏈表
private static ListNode reverseList(ListNode head) {
if(head == null || head.next == null)
return head;
ListNode result = reverseList(head.next);
head.next.next = head;
head.next = null;
return result;
}
回到本題
這兩道題能夠說是及其類似的了,只是一道從頭部開始組起,這道從頭部開始組起的,也是 leetcode 的第 25 題。而面試的時候,常常會進行變形,例如這道字節跳動的題,它變成從尾部開始組起,可能你一時之間就不知道該怎麼弄了。固然,可能有人一會兒就反應出來,把他秒殺了。
其實這道題很好作滴,你只須要先把單鏈表進行一次逆序,逆序以後就能轉化爲從頭部開始組起了,而後按照我上面的解法,處理完以後,把結果再次逆序即搞定。兩次逆序至關於沒逆序。
例如對於鏈表(其中 K = 3)
咱們把它從尾部開始組起,每 K 個節點爲一組進行逆序。步驟以下
一、先進行逆序
逆序以後就能夠把問題轉化爲從頭部開始組起,每 K 個節點爲一組進行逆序。
二、處理後的結果以下
三、接着在把結果逆序一次,結果以下
代碼以下
public ListNode solve(ListNode head, int k) {
// 調用逆序函數
head = reverse(head);
// 調用每 k 個爲一組的逆序函數(從頭部開始組起)
head = reverseKGroup(head, k);
// 在逆序一次
head = reverse(head);
return head;
}
相似於這種須要先進行逆序的還要兩個鏈表相加,這道題字節跳動的筆試題也有出過,以下圖的第二題
這道題就須要先把兩個鏈表逆序,再節點間相加,最後在合併了。
# 總結
關於鏈表的算法題,在面試的時候據說是挺常考的,你們能夠多注意注意,遇到不錯的鏈表算法題,也歡迎扔給我勒。