1. 程式人生 > >LeetCode 連結串列翻轉相關(24 25)

LeetCode 連結串列翻轉相關(24 25)

思路

連結串列反轉這種題,上個星期我還是不會做的,但是自從學會了前插法,寫起來就遊刃有餘的。 下面介紹下前插法

  1. 新建一個空節點作為反轉連結串列的表頭
  2. 將待反轉連結串列的頭結點取出,用一個指標指向頭結點的下一個結點,然後將頭結點指向新連結串列的表頭
  3. 新連結串列的指標指向插入的頭結點,待反轉連結串列的表頭為之前存放的舊結點的next結點
  4. 重複上述操作,直到反轉連結串列全部插入到新連結串列中
  5. 返回新連結串列的頭結點即可 這樣看著不好理解,直接看程式碼吧

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個一組反轉連結串列

這道題是我第一次獨立做的困難題(雖然是通過率很高的困難題) 心裡還是挺有成就感的,下面說一下我的思路

思路

首先我想到這題跟反轉連結串列其實很像,就是用前插法翻轉連結串列,只不過用的指標有點多

  1. need指向需要劃分的區間的起始結點,我用一個ptr來指向need,方便劃分
  2. 然後用need往下走,走到下一個區間的起始結點
  3. 現在我們有了翻轉區間的起始結點和結束條件,那麼我們就可以開始用前插法來翻轉區間了
  4. ok,翻轉完區間,我們把翻轉完區間的尾結點(其實就是更改前的need哦,不用多次遍歷!),指向下一個需要翻轉的尾結點。這樣做能使得你的連結串列每一次翻轉都能連貫起來,並且下一次翻轉不會斷鏈,不信可以用筆驗算一下。(如果碰到剩下的不用翻轉怎麼辦?直接指向當前的need結點返回即可!)
  5. 然後我們返回連結串列需要注意一下,返回的是第一個區間的尾結點為頭結點的連結串列,所以第一步要往後找到這個頭結點即可。

簡單的說就三步 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;
    }
};