LeetCode 連結串列翻轉相關(24 25)
阿新 • • 發佈:2018-12-17
思路
連結串列反轉這種題,上個星期我還是不會做的,但是自從學會了前插法,寫起來就遊刃有餘的。 下面介紹下前插法
- 新建一個空節點作為反轉連結串列的表頭
- 將待反轉連結串列的頭結點取出,用一個指標指向頭結點的下一個結點,然後將頭結點指向新連結串列的表頭
- 新連結串列的指標指向插入的頭結點,待反轉連結串列的表頭為之前存放的舊結點的next結點
- 重複上述操作,直到反轉連結串列全部插入到新連結串列中
- 返回新連結串列的頭結點即可 這樣看著不好理解,直接看程式碼吧
24. 兩兩交換連結串列中的節點
思路
三個指標來交換
口 1 2 3 4 s p q 口 2 1 3 4 s q p 口 2 1 3 4 s q p
如上 p,q指向需要交換的指標,s為p指標之前的指標(沒有s會導致漏掉數) 1.p先指向q next 2.q的next為p 3.s的next為q 4.判斷如果p餘下的空間不夠交換 就可以返回ans了 5.如果夠 那麼繼續往下迭代 注意一開始要用ans來指向頭指標 不然head和start是會改變位置的,直接返回會漏掉數 程式碼
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(!head||!head->next) return head;
ListNode* ptr=head;
ListNode* qtr=head->next;
ListNode* start=new ListNode(0);
start->next=head;
ListNode* ans=start;
while(start-> next)
{
// cout<<"start "<<start->val<<"ptr "<<ptr->val<<"qtr "<<qtr->val<<endl;
ptr->next=qtr->next;
qtr->next=start->next;
start->next=qtr;
if(ptr->next==NULL||ptr->next->next==NULL)return ans->next;
start=ptr;
ptr=start->next;
qtr=ptr->next;
}
return ans->next;
}
};
25.k個一組反轉連結串列
這道題是我第一次獨立做的困難題(雖然是通過率很高的困難題) 心裡還是挺有成就感的,下面說一下我的思路
思路
首先我想到這題跟反轉連結串列其實很像,就是用前插法翻轉連結串列,只不過用的指標有點多
- need指向需要劃分的區間的起始結點,我用一個ptr來指向need,方便劃分
- 然後用need往下走,走到下一個區間的起始結點
- 現在我們有了翻轉區間的起始結點和結束條件,那麼我們就可以開始用前插法來翻轉區間了
- ok,翻轉完區間,我們把翻轉完區間的尾結點(其實就是更改前的need哦,不用多次遍歷!),指向下一個需要翻轉的尾結點。這樣做能使得你的連結串列每一次翻轉都能連貫起來,並且下一次翻轉不會斷鏈,不信可以用筆驗算一下。(如果碰到剩下的不用翻轉怎麼辦?直接指向當前的need結點返回即可!)
- 然後我們返回連結串列需要注意一下,返回的是第一個區間的尾結點為頭結點的連結串列,所以第一步要往後找到這個頭結點即可。
簡單的說就三步 1.劃分反轉區間,反轉該區間 2.該區間指向下一個反轉區間的尾結點(這樣可以連貫的反轉) 3.重複上述步驟,直到判斷出剩餘的結點不足夠反轉,就可以退出了
下面是程式碼,有一點長,用時16ms
/**
* 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(head==NULL||head->next==NULL)return head;
//題目的k有可能大於連結串列中的個數
ListNode* thead=head;
int num=0;
while(thead)
{
num++;
thead=thead->next;
}
if(k>num) return head;
//ans為需要返回的新連結串列的頭結點
ListNode* ans=head;
//need為劃分區間
ListNode* need=head;
for(int i=0;i<k-1;i++)
{
if(ans) ans=ans->next;
else return head;
}
bool enough=true;
ListNode* ptr=need;
ListNode* tneed=need;
//insTo為翻轉區域的尾節點
ListNode* insTo;
//ins為需要指向的下一個區間的結點
ListNode* ins=need;
while(need&&enough)
{
//指向尾結點
insTo=need;
//用於遍歷的臨時結點
tneed=need;
//翻轉區間的起點
ptr=need;
//不應該在這裡判斷
//用一個臨時結點來判斷!這一輪!是否足夠翻轉
for(int i=0;i<k;i++)
{
if(need) need=need->next;
}
//前插法反轉該連結串列 無論如何都需要翻轉前一區間的連結串列
ListNode* temp=new ListNode(0);
while(ptr!=need)
{
ListNode* qtr=ptr;
ptr=ptr->next;
qtr->next=temp;
temp=qtr;
}
//這裡還需另外判斷下一輪夠不夠!!
tneed=need;
for(int i=0;i<k;i++)
{
if(tneed) tneed=tneed->next;
else enough=false;
}
//1. 下一輪沒有足夠的結點用於反轉 直接指向need
if(enough==false)
{
insTo->next=need;
}
else
{
//非空結點需要指向need的下k-1個結點
ins=need;
for(int i=1;i<k;i++)
{
if(ins)ins=ins->next;
}
insTo->next=ins;
}
}
return ans;
}
};