一、概述
24題兩兩調換元素他爹。這個我沒想好怎麼用奇偶鏈表做,用二階指針或者遞歸也感覺太麻煩。因此選擇用棧做。
做出來了,但是效果很辣雞。
二、分析
1、我的方法
循環,棧中壓入k個元素,再讀取k個元素即可。代碼如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
stack<ListNode*> s;
ListNode *result = new ListNode(-1);
ListNode *now, *tail;
tail = head;
now = result;
while (tail != NULL)
{
int i;
ListNode *tmp=tail;
for (i = 0; i<k; i++)
{
s.push(tmp);
tmp = tmp->next;
if (tmp == NULL&&i!=k-1)
break;
}
if(tmp==NULL&&i!=k)
break;
tail = tmp;
while (s.size() != 0)
{
now->next = s.top();
s.pop();
now = now->next;
}
}
now->next=tail;
return result->next;
}
};
有以下幾點要注意:
第一,調換k個元素,最後要是不夠k個元素則不調換。那問題來了,我又不知道什麼時候到最後,發現不夠k個的時候元素已經壓入棧了,怎麼辦?要用一個tmp指向棧底元素,當發現不夠直接退出壓入循環,指針重新指向tmp指向的元素即可。
第二,壓棧循環的退出條件,一個是到k個,另外一個是到末尾。到末尾又有兩種情況,一種是到已經滿足k個,一種是還沒滿足。由於要在循環體結束後進行判斷,因此存在到末尾且有k個元素壓入棧的時候,提前退出。這樣一來,我們即使到末尾,到底是出棧還是指向棧底元素就需要兩個判斷了:一個是沒到k個元素,一個是末尾爲NULL。缺一不可。
然後是出棧,在結尾加元素。
當所有的k個元素的序列都調換完,最後剩的元素也要加上,無論是剩下幾個還是剩下NULL。否者會出現循環鏈表導致TLE。
別的就沒有什麼注意的了。
2、指針迭代法
我就知道有這種方法,可惜那天沒耐心,沒仔細往下做。
先來看一張圖吧。用圖來講更直觀容易。
我們要維護五個指針:a、b、c、pre、tmp。
在24題中,我們已經知道,調換完所有元素之後,有一個指針的指向錯誤要在下一次循環中修正,在本題中體現爲“上一次循環的第一個”指向“本次循環的最後一個”。這裏的第一個和最後一個都指的是沒調換順序時候的鏈表順序。
因此我們用pre來保存前者,tmp來保存後者。a、b、c則負責調換工作。a指向本組k個元素的第一個,b是第二個,c是第三個。
進行調換前是上圖中的第一個鏈表,調換一次後則是第二個鏈表。
調換代碼如下:
b->next = a;
a = b;
b = c;
c = c->next;
在調換完k-1次之後進行下一步操作。如下圖。
我們已經完成了k個元素順序的調換,接下來要開始下一組的k個元素了。在開始之前,要先對上一個循環中的指向錯誤進行修正。即令pre->next指向a。然後令tmp成爲新的pre,b成爲新的tmp。開始下一個循環。
因爲元素總數很可能不能被k整除,因此在調換到最後的時候,若不夠k個就不動它們。但是我們是一組一組調換的,調換到一半發現不夠k個就會傻眼。因此要提前知道鏈表中元素的總數,然後再開始調換。這樣十分直觀易懂。雖然需要多遍歷鏈表一次。但這點時間的耗費是值得的。
循環退出條件就可以是num<k了。在退出後把最後的接上就可以。
代碼如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if(k==1)
return head;
ListNode *a, *b, *c, *tmp, *pre;
int num = 0;
tmp = head;
while (tmp != NULL)
{
tmp = tmp->next;
num++;
}
if(num<k)
return head;
ListNode* result = new ListNode(-1);
result->next = head;
tmp = head;
pre = result;
while (num>=k)
{
a = tmp;
b = a->next;
c = b->next;
int n;
for (n = 0; n<k - 1; n++,num--)
{
b->next = a;
a = b;
b = c;
if(c==NULL)
break;
c = c->next;
}
num--;
pre->next = a;
pre = tmp;
tmp = b;
}
pre->next = b;
return result->next;
}
};
三、總結
指針的運用啊,最好還是畫圖解決,要不指針一多就暈暈的。但是效率真的高。
另外,有時候多遍歷一次鏈表的代價是值得的。