LeetCode--鏈表

一、使用常量空間複雜度在O(n log n)時間內對鏈表進行排序。java

思路:算法

由於題目要求複雜度爲O(nlogn),故能夠考慮歸併排序的思想。
歸併排序的通常步驟爲:
1)將待排序數組(鏈表)取中點並一分爲二;
2)遞歸地對左半部分進行歸併排序;
3)遞歸地對右半部分進行歸併排序;
4)將兩個半部分進行合併(merge),獲得結果。
 
因此對應此題目,能夠劃分爲三個小問題:
1)找到鏈表中點 (快慢指針思路,快指針一次走兩步,慢指針一次走一步,快指針在鏈表末尾時,慢指針剛好在鏈表中點);
2)寫出merge函數,即如何合併鏈表。 (見merge-two-sorted-lists 一題解析)
3)寫出mergesort函數,實現上述步驟。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    //找到鏈表中的中點
    ListNode *findMiddle(ListNode *head){
        ListNode *chaser = head;
        ListNode *runner = head->next;
        while(runner !=NULL &&runner->next != NULL){
            chaser = chaser->next;
            runner = runner->next->next;
        }
        return chaser;
    }
    //將兩組鏈表進行排序
    ListNode *mergeTwoLists(ListNode* l1, ListNode* l2){
        if(l1==NULL) return l2;
        if(l2==NULL) return l1;
        ListNode *dummy = new ListNode(0);
        ListNode *head = dummy;
        while(l1!=NULL && l2!=NULL){
            if(l1->val > l2->val){
                head->next = l2;
                l2 = l2->next;
            }else{
                head->next = l1;
                l1 = l1->next;
            }
            head = head->next;
        }
        if(l1==NULL) head->next = l2;
        if(l2==NULL) head->next = l1;
        return dummy->next;
    }
    
    ListNode *sortList(ListNode *head) {
        if(head==NULL || head->next == NULL) return head;
        ListNode* middle = findMiddle(head);
        ListNode* right = sortList(middle->next);
        middle -> next = NULL;
        ListNode* left = sortList(head);
        return mergeTwoLists(left, right);
    }
};

 二、給出單鏈表L:L 0→L 1→...→L n-1→L n,數組

將其從新排序爲:L 0→L n→L 1→L n-1→L 2→L n-2→......
您必須在不改變節點值的狀況下就地執行此操做。
例如,
給定{1,2,3,4},將其從新排序爲{1,4,2,3}。dom

思路:快慢指針找到中間節點,將後面的鏈表反轉(前插法),合併鏈表函數

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        //快慢指針找到中間節點
        while(fast->next!=NULL && fast->next->next!=NULL){
            slow = slow->next;
            fast = fast->next->next;
        }
        //拆分鏈表,並反轉中間節點以後的鏈表
        ListNode *after = slow->next;
        slow->next = NULL;
        ListNode *pre = NULL;
        while(after!=NULL){
            ListNode *temp = after->next;
            after->next = pre;
            pre = after;
            after = temp;
        }
        // 合併兩個表
        ListNode *left = head;
        after = pre;
        while(left!=NULL && after!=NULL){
            ListNode *ltemp = left->next;
            ListNode *rtemp = after->next;
            left->next = after;
            left = ltemp;
            after->next = left;
            after = rtemp;
        }
    }
};

三、給定鏈表,返回循環開始的節點。 若是沒有循環,則返回null。
跟進:
你能不用額外的空間解決它嗎?spa

思路:指針

1)首先判斷是否有環,有環時,返回相遇的節點,無環,返回null
2)有環的狀況下, 求鏈表的入環節點
從頭結點開始走,一邊走一邊將走過的路清空,當遇到空時,此時就是環的入口點
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        //找到相遇點
        while(fast!=NULL && fast->next!=NULL){
            slow=slow->next;
            fast=fast->next->next;
            //若是快慢指針相遇
            if(slow==fast){
                ListNode *temp = NULL;
                //一邊走一邊將走過的路清空,當遇到空時,此時就是相遇點
                while(head->next){
                    temp = head->next;
                    head->next = NULL;
                    head = temp;
                }
                return head;
            } 
        }
        return NULL;
    }
};

四、給定一個鏈表,肯定它是否有一個循環。
跟進:
你能不用額外的空間解決它嗎?code

思路:blog

利用快慢節點相遇即有環,排序

慢節點一次走一格

快捷點一次走兩格

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *slow = head;
        ListNode *fast = head;
        //找到相遇點
        while(fast!=NULL && fast->next!=NULL){
            slow=slow->next;
            fast=fast->next->next;
            //若是快慢指針相遇
            if(slow==fast) return true;
        }
        return false;
    }
};

 五、給出鏈表,使得每一個節點包含一個附加的隨機指針,該指針能夠指向列表中的任何節點或爲空。
返回列表的深層副本。

思路:

先拷貝新節點,插入到原節點的後邊;而後再 拷貝隨機指針;最後將新節點從原鏈表中分離出,注意要保證原鏈表正常。

/**
 * Definition for singly-linked list with a random pointer.
 * struct RandomListNode {
 *     int label;
 *     RandomListNode *next, *random;
 *     RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        RandomListNode *p,*copy;
        if (!head) return NULL;
        //先將拷貝原節點產生的新節點,放在原節點的後面
        for(p=head;p;p=p->next){
            copy = new RandomListNode(p->label);
            copy->next = p->next;
            p = p->next = copy;
        }
        //拷貝random指針
        for(p=head;p;p=copy->next){
            copy = p->next;          
            copy->random = (p->random?p->random->next:NULL);
        }
        //刪除新節點
        for(p=head,head=copy=p->next;p;){
            p = p->next = copy->next; 
            copy = copy->next = (p?p->next:NULL);
        }
        return head;
    }
};

六、將位置m的連接列表反轉到n。 在原地和一次經過。
例如:
給定1-> 2-> 3-> 4-> 5-> NULL,m = 2且n = 4,
return1->4->3-> 2->5-> NULL。
注意:
給定m,n知足如下條件:
1≤m≤n≤列表長度。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        ListNode *dummy = new ListNode(-1);
        ListNode *preStart,*Start;
        dummy->next = head;
        preStart = dummy;
        Start = head;
        for(int i=1;i<m;i++){
            preStart = Start;
            Start = Start->next;
        }
        for(int i=0;i<n-m;i++){
            ListNode *temp = Start->next;
            Start->next = temp->next;
            temp->next = preStart->next;
            preStart->next = temp;
        }
        return dummy->next;
    }
};

七、給定鏈表和值x,對其進行分區,使得小於x的全部節點都在大於或等於x的節點以前。
您應該保留兩個分區中每一個分區中節點的原始相對順序。
例如,
給定1-> 4-> 3-> 2-> 5-> 2和x = 3,
return1-> 2->2->4->3->5。

思路:

建立兩張鏈表,分別我list01和list01

把節點值小於x的節點連接到鏈表1上,節點值大等於x的節點連接到鏈表2上。

最後把兩個鏈表相連便可
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *partition(ListNode *head, int x) {
        //建立兩個鏈表
        ListNode *list01 = new ListNode(-1);
        ListNode *list02 = new ListNode(-2);
        ListNode *cur1 = list01;
        ListNode *cur2 = list02;
        while(head!=NULL){
            //把節點值小於x的節點連接到鏈表1上
            if(head->val < x){
                cur1->next = head;
                cur1=cur1->next;
            }
            //節點值大等於x的節點連接到鏈表2上
            else{ 
                cur2->next = head;
                cur2=cur2->next;
            }
            head = head->next;
        }
        //合併兩個鏈表
        cur1->next = list02->next;
        cur2->next = NULL;
        return list01->next;
    }

};

八、給定一個列表,將列表向右旋轉k個位置,其中k爲非負數。

例如:
給定1-> 2-> 3-> 4-> 5-> NULL和k = 2,
返回4-> 5-> 1-> 2-> 3-> NULL。

思路:

先遍歷一遍,得出鏈表長度len,注意k可能會大於len,所以k%=len。
將尾結點next指針指向首節點,造成一個環,接着日後跑len-k步,從這裏斷開,就是結果
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *rotateRight(ListNode *head, int k) {
        if(head==NULL) return NULL;
        ListNode *p=head;
        int len=1;
        //計算連鏈表的長度
        while(p->next){
            len++;
            p=p->next;
        }
        k = len - k%len;
        //首尾相連
        p->next = head;
        for(int i=0;i<k;i++){
            p=p->next;
        }
        head = p->next;
        p->next = NULL;
        return head;
    }
};

九、給定已排序的連接列表,刪除全部具備重複數字的節點,只留下原始列表中的不一樣數字。

例如,
給定1-> 2-> 3-> 3-> 4-> 4-> 5,返回1-> 2-> 5。
給定1-> 1-> 1-> 2-> 3,return2-> 3。

思路:

首先要找到第一個非重複的節點做爲頭結點,
直接找比較麻煩,能夠添加一個新結點list做爲僞頭結點,
最後返回list->next便可。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *deleteDuplicates(ListNode *head) {
        if(head==NULL) return NULL;
        ListNode *list = new ListNode(head->val-1);
        list->next = head;
        ListNode *preStart = list;
        while(preStart->next && preStart->next->next){
            ListNode *Start = preStart->next;
            ListNode *aftStart = Start->next;
            if(Start->val!=aftStart->val){
                preStart->next = Start;
                preStart = Start;
            }else{
                while(aftStart && Start->val==aftStart->val){
                    aftStart=aftStart->next;
                }
                preStart->next = aftStart;
            }
        }
        return list->next;
    }
};

十、給定已排序的連接列表,刪除全部重複項,使每一個元素只出現一次。

例如,
給定1-> 1-> 2,return1-> 2。
給定1-> 1-> 2-> 3-> 3,返回1-> 2-> 3。

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *deleteDuplicates(ListNode *head) {
        if(head == NULL || head->next == NULL) return head;
        ListNode* p1 = head;
        while(p1 != NULL && p1->next != NULL) {
            ListNode* p2 = p1->next;
            if(p1->val != p2->val) {
                p1->next = p2;
                p1 = p1->next;
                continue;
            }
            while(p2->next != NULL && p1->val == p2->next->val) 
                p2 = p2->next;
            p1->next = p2->next;
            delete p2;
            p1 = p1->next;
        }
        return head;
    }
};

十一、合併兩個已排序的連接列表並將其做爲新列表返回。 新列表應該經過拼接前兩個列表的節點來完成。

思路:和歸併排序的思想同樣

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
        ListNode *head = new ListNode(0);
        ListNode *t = head;
        while (l1 != NULL || l2 != NULL) {
            if (l1 == NULL) {
                t->next = l2;
                l2 = l2->next;
            }
            else if (l2 == NULL) {
                t->next = l1;
                l1 = l1->next;
            }
            else if (l1->val < l2 -> val){
                t->next = l1;
                l1 = l1->next;
            }
            else {
                t->next = l2;
                l2 = l2->next;
            }
            t = t->next;
        }
        return head->next;
    }
};

十二、給定鏈表,一次反轉鏈表k的節點並返回其修改後的列表。
若是節點數不是k的倍數,那麼最後的剩餘節點應該保持不變。
您可能沒法更改節點中的值,只能更改節點自己。
只容許常量內存。
例如,
鑑於此鏈表:1-> 2-> 3-> 4-> 5
對於k = 2,您應該返回:2-> 1-> 4-> 3-> 5
對於k = 3,您應該返回:3-> 2-> 1-> 4-> 5

/**
 * 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 || k<2) return head;
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        ListNode *Start = head;
        ListNode *preStart = dummy;
        ListNode *temp;
        int len = 1;
        while(head!=NULL){
            len++;
            head=head->next;
        }
        for(int i=0;i<len/2;i++){
            for(int j=i+1;j<len;j++){
                temp = Start->next;
                Start->next = temp->next;
                temp->next = preStart->next;
                preStart->next = temp;
            }
            preStart = Start;
            Start = Start->next;
        }
        return dummy->next;
        */
        if(head == NULL || head->next == NULL || k < 2) return head;
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        ListNode *pre = dummy, *cur = head, *temp;
        int len = 0;
        //計算鏈表的長度
        while (head != NULL) {
            len ++ ;
            head = head->next;
        }
        for (int i = 0; i < len / k; i ++ ) {
            for (int j = 1; j < k; j ++ ) {
                temp = cur->next;
                cur->next = temp->next;
                temp->next = pre->next;
                pre->next = temp;
            }
            pre = cur;
            cur = cur->next;
        }
        return dummy->next;
    }
};

1三、給定鏈表,交換每兩個相鄰節點並返回其頭部。
例如,
給定1-> 2-> 3-> 4,您應該返回列表as2-> 1-> 4-> 3。
您的算法應該只使用恆定空間。 您可能沒法修改列表中的值,只能更改節點自己。

/**
 * 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==NULL || head->next==NULL ) return head;
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        ListNode *Start = head;
        ListNode *aftStart = head->next;
        ListNode *preStart = dummy;
        ListNode *temp;
        while(Start!=NULL && aftStart!=NULL){
            temp = aftStart->next;
            Start->next = temp;
            aftStart->next = Start;
            preStart->next = aftStart;
            preStart = Start;
            Start = temp;
            aftStart = temp->next;
        }
        return dummy->next;
    }
};

1四、給定鏈表,從列表末尾刪除第n個節點並返回其頭部。
例如,
給定鏈表:1-> 2-> 3-> 4-> 5,n = 2。
從末尾刪除第二個節點後,鏈表變爲1-> 2-> 3-> 5。
注意:
給定n將始終有效。
嘗試一次性完成此操做。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *removeNthFromEnd(ListNode *head, int n) {
        if(head==NULL ) return head;
        ListNode *dummy = new ListNode(0);
        dummy->next = head;
        head = dummy;
        ListNode *slow = head;
        ListNode *fast = head;
        for(int i=0;i<n;i++){
            fast = fast->next;
        }
        while(fast->next!=NULL){
            fast = fast->next;
            slow = slow->next;
        }
        ListNode *temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        return dummy->next;
    }
};
相關文章
相關標籤/搜索