目錄鏈接:http://www.lxweimin.com/p/9c0ada9e0ede
k個一組翻轉鏈表
給出一個鏈表,每 k 個節點一組進行翻轉,并返回翻轉后的鏈表。
k 是一個正整數,它的值小于或等于鏈表的長度。如果節點總數不是 k 的整數倍,那么將最后剩余節點保持原有順序。
示例 :
給定這個鏈表:1->2->3->4->5
當 k = 2 時,應當返回: 2->1->4->3->5
當 k = 3 時,應當返回: 3->2->1->4->5
說明 :
你的算法只能使用常數的額外空間。
你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
切題
一、Clarification
1、每組k個元素翻轉后,每組之間的銜接。[1,k] 與 [k+1,2k]之間銜接
2、注意 剩余元素的處理
二、Possible Solution
1、迭代
遍歷鏈表 找出每組的 首尾以及下一組的首,借助單鏈表翻轉獲得翻轉后的首尾,對首尾節點銜接處理,對哨兵節點移位處理
時間復雜度 O(N)
2、遞歸
遞歸終止條件:
剩余節點 < k
遞歸從里向外出來,每層遞歸返回當前層級鏈表翻轉后的頭節點,那么每層遞歸中我們知道當前層級翻轉后的頭尾節點以及下一個k元素組的頭節點(遞歸的上一層級),可以很輕松地將翻轉后的鏈表銜接起來
3、借助棧
將k個元素壓入棧,通過出棧翻轉,注意剩余元素處理
Python3實現
迭代 借助單鏈表翻轉
# @author:leacoder
# @des: 迭代 借助單鏈表翻轉 k個一組翻轉鏈表
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
cur = head #由于遍歷
dim = ListNode(0) #新建一節點
dim.next = head #next指向head
pre = dim #pre 賦值為 dim 1、記錄和獲取 k個一組的頭 2、方便統一處理
# pre 與 dim 均為哨兵
count = 1
while cur: #遍歷鏈表
if count % k == 0: #k個一組 對這里面的數據翻轉
nextstart = cur.next #記錄 后一個 k個一組的 頭節點
cur.next = None #賦值為None 前面 k個一組數據 為新的鏈表 以 None結束 這個新鏈表可以采用206的處理方式
end, start = self.reverse(pre.next) #翻轉新鏈表 返回翻轉的 尾 和 頭 注pre.next 為新鏈表翻轉前的頭
# 銜接處理,哨兵的next指向翻轉后的頭, 翻轉后的尾的next指向nextstart
# pre 和 cur 依次下移k個元素 變為 end 和 nextstart下一輪處理就變成本輪一樣了
pre.next,end.next,pre,cur = start,nextstart,end,nextstart
else:
cur = cur.next #我們關心k個一組的首尾
count += 1
return dim.next
def reverse(self, head): #翻轉鏈表
dim1,cur,pre = head,head,None
while cur: #遍歷
cur.next,pre,cur = pre,cur,cur.next
return dim1,pre #翻轉后end 和 start
遞歸
# @author:leacoder
# @des: 遞歸 k個一組翻轉鏈表
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
if self.isEnd(head,k):
return head
pre = ListNode(None) # 哨兵
pre.next,cur,count = head,head,1
while count <= k: # 翻轉 k個元素鏈表 處理類似 206
cur.next,pre,cur,count = pre,cur,cur.next,count + 1
# 循環結束后 cur 指向 下一組 k個元素(未翻轉)的頭 ;pre指向當前組在翻轉后的頭節點
nexthead = self.reverseKGroup(cur,k) # nexthead 下一組翻轉后的頭節點
head.next = nexthead #當前組的head在翻轉后成為尾節點,其next指向nexthead 下一組翻轉后的頭節點
return pre # 返回 翻轉后的頭節點
# 遞歸終止判斷:
def isEnd(self,head,k):
count,cur = 0,head
while cur:
count = count + 1
cur = cur.next
if count >= k:
return False #
return True
借助棧
# @author:leacoder
# @des: 借助棧 k個一組翻轉鏈表
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
result= ListNode(None)
cur,result.next = head,head
tmpresult = result
stack = [] # 列表實現棧的 先入后出功能
count = 0
while cur:
count = count + 1
stack.append(cur) # 入棧
cur = cur.next
if count % k == 0: # 已存儲k個元素
count = 0
while stack: # 出棧倒換
tmpresult.next = stack.pop()
tmpresult = tmpresult.next
# 處理 stack 中剩余元素
while stack:
tmpresult.next = stack.pop(0) #
tmpresult = tmpresult.next
return result.next
C++實現
存儲法
用數組存儲k個元素,雖然能通過但是執行用時有點長,不過邏輯簡單
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
* @author:leacoder
* @des: 存儲法 k個一組翻轉鏈表
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode *nodearray[k];//存儲 需翻轉的 k個節點
ListNode* result = new ListNode(0); //翻轉后鏈表 用于結果返回
ListNode* ret = result;//由于存儲翻轉后鏈表
int count = 0;
while( NULL != head){
ListNode* next = head->next; //記錄下移節點
nodearray[count] = head; //記錄到數組
count++;//數組 下標自加
if(k == count){ //已存 k個值 需要翻轉了
for(int i = k;i>0;i--){ //循環讀取 數組中數據
ret->next = nodearray[i-1]; //從后往前去數組中數據 添加到ret鏈表中
ret = ret->next;//移位
nodearray[i-1] = NULL;//置空
count--;//數組中數據個數--
}
}
head = next;
}
for(int i = 0;i<count;i++){//處理不需要翻轉的數據
ret->next = nodearray[i];
ret = ret->next;
nodearray[i] = NULL;
}
ret->next = NULL;
return result->next;
}
};
k作為函數參數傳入后就確定了可以當做常數,但是k作為參數可以為合理范圍內任意值不確定,這點和題目描述說明貌似有點出入
說明 : 你的算法只能使用常數的額外空間。
你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
我這種方法處理也只是提供一種思路,待后續其他實現方法
GitHub鏈接:
https://github.com/lichangke/LeetCode
個人Blog:
https://lichangke.github.io/
歡迎大家來一起交流學習