演算法學習記錄十二(C++)--->連結串列題目集合

Deft_MKJing宓珂璟發表於2017-08-25

題目一

輸入一個連結串列,輸出該連結串列中倒數第k個結點。

分析

最普通的方法就是,先統計單連結串列中節點的個數,然後再找到n-k個節點,時間複雜度為O(n)
另一個思路就是雙指標,,先讓前面的指標走向正想第k個節點,這樣前後兩個節點的差距就是k-1,
之後前面兩個指標一起走,前面的指標走到最後一個節點的時候,後走的那個指標就是倒數第k個節點

程式碼

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(k==0 || pListHead == NULL){ // 這裡k的計數是從1開始的,若k為0或連結串列為空返回NULL  
            return NULL;
        }
        ListNode *pHead = pListHead;
        ListNode *pBehind = pListHead;
        while(k>1 && pHead != NULL){ // 前面的指標先走到正向第k個結點  
            pHead = pHead->next;
            k--;
        }

        if(k>1||pHead==NULL){ // 結點個數小於k,返回NULL  
            return NULL;
        }

        while(pHead->next!=NULL){// 前後兩個指標一起向前走,直到前面的指標指向最後一個結點 
            pHead=pHead->next;
            pBehind=pBehind->next;
        }
        return pBehind; // 後面那個指標所指節點就是倒數第k個
    }
};

題目二

輸入一個連結串列,反轉連結串列後,輸出連結串列的所有元素

分析

1.非遞迴 三指標 
pPrev -> pNode -> pNext; 
我們先儲存原來的下一個指標位置,要不然翻轉後其next會指向pPrev,從而丟失指向pNext的指標 
接著反轉指標, 
然後下面指標同步後移,以便處理後面的指標

2.遞迴
核心程式碼,當前節點的兩次next取值指向自己,就是翻轉
pHead->next->next=pHead;
pHead->next=NULL;

程式碼 非遞迴

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/

/*
next = head.next;//首先記錄當前節點的下一個節點,(儲存起來)
head.next = pre;//讓當前節點指向前一個節點,因為要反序嘛
pre = head;//讓前一個節點值,取代當前的節點值。因為要繼續向下走
head = next;//讓下一個節點,取代當前節點。同樣是向下走,為下一次迴圈做準備
*/
/*
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode *pNode = pHead;
        ListNode *pPre = NULL;
        ListNode *pNext = NULL;

        while(pNode != NULL){
            pNext = pNode->next;
            pNode->next = pPre;
            pPre = pNode;
            pNode = pNext;
        }
        return pPre;
    }
};
*/

程式碼 遞迴

class Solution {
public:
    // 這個遞迴非常贊 舉個例子
    // 例如 1->2->3-NULL  這個連結串列
    // 當pHead為1的時候傳進去  內部會呼叫ReverseList(2)繼續傳遞,而且每次呼叫都會觸發一下兩行程式碼
    // pHead->next->next=pHead;pHead->next=NULL;
    // 當你呼叫ReverseList(3)的時候就直接返回3這個節點,不呼叫那兩行程式碼,然後進行迴歸
    // pHead->next->next=pHead; 這句話的意思是,例如節點2呼叫的時候,pHead是2,遞迴最後一層ReverseList(3)返回3
    // 然後->next就是對應的3節點,再next就是反序替換節點  兩個next指向當前節點,就是反轉一次了
    // pHead->next=NULL;這句話的意思就是最後一個節點的next為Null結束
    ListNode* ReverseList(ListNode* pHead) {
        //如果連結串列為空或者連結串列中只有一個元素
        if(pHead==NULL||pHead->next==NULL) return pHead;

        //先反轉後面的連結串列,走到連結串列的末端結點
        ListNode* pReverseNode=ReverseList(pHead->next);

        //再將當前節點設定為後面節點的後續節點
        pHead->next->next=pHead;
        pHead->next=NULL;

        return pReverseNode;

    }
};

題目三

已知兩個單連結串列pHead1 和pHead2 各自有序,把它們合併成一個連結串列依然有序

分析

這個類似歸併排序。尤其注意兩個連結串列都為空,和其中一個為空時的情況。只需要O(1)的空間。時間複雜度為O(max(len1, len2))。分別用遞迴和常規指標進行操作

常規操作

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/

/*
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL){
            return pHead2;
        }
        if(pHead2 == NULL){
            return pHead1;
        }

        ListNode *orginalHeader = NULL;
        ListNode *currentNode = NULL;
        while(pHead1 && pHead2){
            if(pHead1->val < pHead2->val){
                if(orginalHeader == NULL){ // 該判斷為了找出對應的頭結點儲存到orginalHeader
                    orginalHeader = currentNode = pHead1;
                }
                else{
                    currentNode->next = pHead1; // 取下連結串列的當前節點放到臨時連結串列的next節點
                    currentNode = currentNode->next; // 臨時節點往後移動一位
                }
                pHead1 = pHead1->next; // 每次比較之後都要把比較小的連結串列往後移動一個
            }
            else{
                if(orginalHeader == NULL){
                    orginalHeader = currentNode = pHead2;
                }
                else{
                    currentNode->next = pHead2;
                    currentNode = currentNode->next;
                }
                pHead2 = pHead2->next;
            }
        }
        // 一個為空的時候結束遍歷而且直接拼接到後面
        if(pHead1 == NULL){
            currentNode->next = pHead2;
        }
        else{
            currentNode->next = pHead1;
        }
        return orginalHeader;
    }
};

*/

遞迴拼接

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL){
            return pHead2;
        }
        if(pHead2 == NULL){
            return pHead1;
        }
        ListNode *tempNode = NULL;
        if(pHead1->val < pHead2->val){
            pHead1->next = Merge(pHead1->next,pHead2);
            return pHead1; // 一開始的頭結點大小決定返回head1還是head2
        }else{
            pHead2->next = Merge(pHead1,pHead2->next);
            return pHead2;
        }
    }
};

相關文章