LEETCODE(力扣) 25. K 個一組翻轉鏈表
給你鏈表的頭節點 head ,每 k 個節點一組進行翻轉,請你返回修改后的鏈表。
k 是一個正整數,它的值小于或等于鏈表的長度。如果節點總數不是 k 的整數倍,那么請將最后剩余的節點保持原有順序。
你不能只是單純的改變節點內部的值,而是需要實際進行節點交換。
示例 1:
輸入:head = [1,2,3,4,5], k = 2
輸出:[2,1,4,3,5]
示例 2:
輸入:head = [1,2,3,4,5], k = 3
輸出:[3,2,1,4,5]
提示:
鏈表中的節點數目為 n
1 <= k <= n <= 5000
0 <= Node.val <= 1000
進階:你可以設計一個只用 O(1) 額外內存空間的算法解決此問題嗎?
自解

因為每次只對部分節點進行處理,因此需要記錄處理段前、后的節點,而頭節點因無更前面的節點,需要單獨寫處理邏輯,因此可以用一個dummy節點插入頭節點前面,減少代碼量。
因為要與dummy節點的處理邏輯保持一致,我們每次reverse傳入的參數要傳入處理段的前一個節點。
如下,每次處理傳入節點后的k個節點,處理完畢后將處理段首節點連接到已處理段尾節點上,本次處理段的尾節點連在未處理段的首結點上,并返回本次處理段的尾節點
主函數while循環到全部處理完畢或剩余節點不足k時(reverse都返回NULL)退出。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *reverse(struct ListNode *head,int k)
{
/*last、next0用于反轉鏈表時的中間變量,next1記錄處理段的后一個節點,head1記錄處理段的前一個節點*/
struct ListNode *last=head,*next0=NULL,*next1=NULL,*head1=head;
int i=k;
head=head->next;//從處理段開始反轉
next1=head;
while(next1&&i)//遍歷到處理段的后一個節點,同時檢查節點數量是否足夠
{
i--;
next1=next1->next;
}
if(i)return NULL;//節點數量不夠時返回
while(head&&k)//開始對節點進行反轉,反轉k個
{
next0=head->next;
head->next=last;
last=head;
head=next0;
k--;
}
head1->next=last;//last是該次反轉后的處理段首結點,連接到已處理段的尾節點上
while(last->next!=head1)last=last->next;//將last遍歷到尾節點上
last->next=next0;//該次反轉后的處理段尾節點連接到未處理段上
return last;//該次處理段的尾節點
}
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
struct ListNode dummy,*temp=NULL,*last=NULL,*head1=NULL;
dummy.next=head;
head=&dummy;
while(head->next&&(head=reverse(head,k)));
return dummy.next;
}
力扣解
自解的反轉處理與該解法相同,但該解法代碼更加簡潔,for循環的判斷也值得深究
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
// 統計節點個數
int n = 0;
for (struct ListNode* cur = head; cur; cur = cur->next) {
n++;
}
struct ListNode dummy = {0, head};
struct ListNode* p0 = &dummy;
struct ListNode* pre = NULL;
struct ListNode* cur = head;
// k 個一組處理
for (; n >= k; n -= k) {
for (int i = 0; i < k; i++) { // 同 92 題
struct ListNode* nxt = cur->next;
cur->next = pre; // 每次循環只修改一個 next,方便大家理解
pre = cur;
cur = nxt;
}
// 見視頻
struct ListNode* nxt = p0->next;//--p0->next此時指向該次處理段的尾節點
p0->next->next = cur;//--p0->next->next是未處理段的首結點
p0->next = pre;//--pre是該次處理段的尾節點
p0 = nxt;//--p0指向該次處理段的尾節點
//--這段處理就是為了使cur指向已處理段的最后一個節點,原因就是我在自解說的,減少代碼處理量,不必單獨對首結點進行邏輯處理(ps:--注釋僅為個人理解,與靈佬無關)
}
return dummy.next;
}
作者:靈茶山艾府
鏈接:https://leetcode.cn/problems/reverse-nodes-in-k-group/solutions/1992228/you-xie-cuo-liao-yi-ge-shi-pin-jiang-tou-plfs/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
仿解
個人認為靈佬的代碼更簡潔也更優美,進行了仿寫練習
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
int i=0;
struct ListNode dummy={0,head};
struct ListNode *p=&dummy;
struct ListNode *cur=head,*nxt=head,*last=NULL;
while(nxt)
{
nxt=nxt->next;
i++;
}
for(;i>=k;i-=k)
{
for(int j=0;j<k;j++)
{
nxt=cur->next;
cur->next=last;
last=cur;
cur=nxt;
}
nxt=p->next;//nxt存放處理段尾節點
p->next=last;//連接已處理段尾節點與處理段首結點
nxt->next=cur;//處理段尾節點連接未處理段首結點
p=nxt;
}
return dummy.next;
}

浙公網安備 33010602011771號