Sort List(四种算法)

来源:互联网 发布:知悉和悉知是什么意思 编辑:程序博客网 时间:2024/05/17 00:15

Sort a linked list in O(n log n) time using constant space complexity.

算法一:快速排序。 因为链表是单向,所以有很多细节要注意,比如quick_sort(p,r)排序的区间其实是[p->next,r->next],因为单向链表不能回头。

/** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public:    //p,r分别为欲排序的子链表的首尾节点的前驱    void quick_sort(ListNode *p,ListNode *r){                if(p==r ||r == NULL || p==r->next) return;        ListNode *k = p,*pk = NULL; //pk为k的前驱        ListNode *q,*tail = r->next;                //注意r在迭代过程中可能会被改变,因此控制条件不能为q!=r,tail是迭代过程中是不会被改变的        for(q=p;q->next!=tail;){            if(q->next->val <= tail->val){                 bool flag = (q==k->next);                Swap(k,q);  //交换k->next与q->next节点                pk = k; k = k->next;                 if(!flag) q = q->next; //若k,q相邻则swap后q会后移一位,此时无需令q=q->next,注意k,q都是欲交换节点的前驱,非节点本身            }            else q = q->next;        }                bool flag = (k->next==q || k==q);        Swap(k,q);         quick_sort(p,pk);        if(!flag) quick_sort(k->next,q);    }        //当交换相邻的节点时要特别考虑    void Swap(ListNode *p, ListNode *r){        if(p==r) return;        else if(p==r->next) Swap(r,p);        else if(r==p->next){            ListNode *ssr = r->next->next;            p->next = r->next;            r->next->next = r;            r->next = ssr;        }        else{            ListNode *sp = p->next, *ssp = sp->next, *ssr = r->next->next;            p->next = r->next; r->next->next = ssp;            r->next = sp; sp->next = ssr;        }    }        ListNode *sortList(ListNode *head) {                if(head==NULL || head->next==NULL) return head;                ListNode* tmp_head = new ListNode(0);        tmp_head->next = head;   //构造头节点的前驱        ListNode* ptail = tmp_head;   //ptail为尾节点的前驱        while(ptail->next->next) ptail = ptail->next;         quick_sort(tmp_head,ptail);                ListNode* new_head = tmp_head->next;        delete tmp_head;        return new_head;    }};

算法二: 改进版快速排序。  快速排序在数据随机分布情况下速度较快,但在本题中会超时。查看超时的test case,发现有大量重复值。于是想到改进的算法,抛弃传统的两段划分,采用三段划分。{x:x < tail->val}, {y:y == tail->val}, {z:z > tail->val}. 每次分割后对中间段不再调用quick_sort。改进后可以AC

改进后的快排算法:

/** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public://p,r分别为欲排序的子链表的首尾节点的前驱//每轮递归后都保持格局{x:x->val < tail->val},{y:y->val == tail->val}, {z:z->val > tail->val}void quick_sort(ListNode *p,ListNode *r){if(p==r ||r == NULL || p==r->next) return;//k1指向等于tail->val的连续节点段首的前驱,若无此连续段,则k1与q同//k2指向大于tail->val的连续节点段首的前驱,若无此连续段,则k2与q同ListNode *k1 = p, *k2 = p, *pk1 = NULL, *pk2 = NULL; //pk为k的前驱ListNode *q,*tail = r->next,*pq = NULL;//注意r在迭代过程中可能会被改变,因此控制条件不能为q!=r,tail是迭代过程中是不会被改变的for(q=p;q->next!=tail;){if(q->next->val < tail->val){ bool flag1 = (q==k1->next);if(!flag1 && k1->next == k2) k2 = q->next;  //若k2是k1的后继,且大于tail->val的节点真实存在Swap(k1,q);  //交换k1->next与q->next节点,将小于tail->val的节点移到等于tail->val的节点的前面,或保持不变(交换相同节点)pk1 = k1; k1 = k1->next; if(pk1==k2 || k2==q){ //大于、等于tail->val的节点只现其一if(k2!=q){ //即k1==k2,只出现大于tail->val的节点,没出现过等于tail->val的k2 = k1; if(!flag1) q = q->next; //若flag1==true说明Swap时q已经向后移了一位,不用再右移}  else{ //k1<k2的情况,只出现过等于tail->val的节点,没出现过大于tail->val的if(!flag1){  q = q->next; k2 = q;} }} //若在前面同时出现了大于、等于tail->val的两种节点,则还要再将等于tail->val的节点与大于tail->val的节点交换else{ bool flag2 = (q==k2->next);  Swap(k2,q);  k2 = k2->next; if(!flag2) q = q->next;}}else if(q->next->val == tail->val){ //将等于tail->val的节点移到大于tail->val的节点的前面bool flag2 = (q==k2->next);Swap(k2,q);k2 = k2->next;if(!flag2) q = q->next;}else q = q->next;}bool flag = (k2->next==q || k2==q);Swap(k2,q); quick_sort(p,pk1);if(!flag) quick_sort(k2->next,q);}//当交换相邻的节点时要特别考虑void Swap(ListNode *p, ListNode *r){if(p==r) return;else if(p==r->next) Swap(r,p);else if(r==p->next){ListNode *ssr = r->next->next;p->next = r->next;r->next->next = r;r->next = ssr;}else{ListNode *sp = p->next, *ssp = sp->next, *ssr = r->next->next;p->next = r->next; r->next->next = ssp;r->next = sp; sp->next = ssr;}}ListNode *sortList(ListNode *head) {if(head==NULL || head->next==NULL) return head;ListNode* tmp_head = new ListNode(0);tmp_head->next = head;   //构造头节点的前驱ListNode* ptail = tmp_head;   //ptail为尾节点的前驱while(ptail->next->next) ptail = ptail->next; quick_sort(tmp_head,ptail);ListNode* new_head = tmp_head->next;delete tmp_head;return new_head;}};


算法三:递归版归并排序。 除了快速排序,当然也可以用归并排序。链表的归并排序与数组的归并排序唯一不同是数组可以在O(1)时间取到中点,但链表要花费O(n),有人因此就认为归并排序不适合链表了,其实不然。即使找中点要花费O(n)又怎样呢?事实是不管是数组还是链表,都要有个合并过程,而合并的时间复杂度就是O(n),因此取中点的复杂度是O(1)还是O(n)都不会影响到整个排序的时间复杂度,链表和数组的归并排序时间复杂度上仅在于系数的差别而已。都是O(n*log n)


/** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public:typedef pair<ListNode*,ListNode*> pair_node;pair_node merge_sort(ListNode* first,ListNode* last){    if(first==last || first->next==last) return make_pair(first,last);    ListNode *fast = first, *slow = first;    while(fast!=last && fast->next!=last){        fast = fast->next->next;        slow = slow->next;    }        pair_node list1 = merge_sort(first,slow);    pair_node list2 = merge_sort(slow,last);    return merge(list1,list2);}pair_node merge(pair_node list1,pair_node list2){    ListNode *first;    ListNode *cur,*p = list1.first, *q = list2.first;    if(p->val <= q->val){        cur = first = p;        p = p->next;    }    else{        cur = first = q;        q = q->next;    }    while(p!=list1.second && q!=list2.second){        if(p->val <= q->val){            cur->next = p; cur = p;            p = p->next;        }        else{            cur->next = q; cur = q;            q = q->next;        }    }    if(p!=list1.second){         cur->next = p;        while(p->next!=list1.second) p = p->next;        p->next = list2.second;    }    else cur->next = q;    return make_pair(first,list2.second);}ListNode *sortList(ListNode *head) {    if(head==NULL || head->next==NULL) return head;    return merge_sort(head,NULL).first;}};

因为用归并排序并不像快排在找分割点的过程中需要交换节点,所以在处理上要比快排容易,只需用一个首节点和尾节点的后继表示链表就可以了。


算法四:非递归归并排序。上面的归并排序是采用传统的递归实现。归并排序也可以用迭代实现,实质是递归的逆向实现。其思路是这样,假设有n个节点,从第一个节点1开始,发现此前没有已经排好的长度为1的链表(即单个节点),到2时发现此前有单节点1,则把(1,2)合并成长度为2的已排序链表,再看此前有没有长度为2的已排序的子链表,没有。再取3,发现此前无单节点(1,2已经合并),再取4,发现有单节点3,合并(3,4),合并后长度为2,再查找此前有没有长度为2的已排序子链表,刚好有!于是将子链表(1,2)与子链表(3,4)合并,合并后长度为4......


/** * Definition for singly-linked list. * struct ListNode { *     int val; *     ListNode *next; *     ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public:ListNode* merge(ListNode *list1,int len1,ListNode *list2,int len2){    if(len1==0) return list2;    if(len2==0) return list1;    int i=0,j=0;        ListNode *res,*cur,*p=list1,*q=list2;    if(p->val <= q->val){ res = cur = p; p = p->next; i++; }    else{ res = cur = q; q = q->next; j++; }        while(i<len1 && j<len2){        if(p->val <= q->val){            cur->next = p; cur = p;            i++;  p = p->next;         }        else{            cur->next = q; cur = q;            j++;  q = q->next;        }    }    if(i<len1){        cur->next = p;         while(++i<len1) p = p->next;        p->next = q;    }    else cur->next = q;    return res;}ListNode *sortList(ListNode *head) {    if(head==NULL || head->next==NULL) return head;        ListNode* tab[64];  //tab[i]表示从tab[i]开始的连续2^i个节点已经排好序,各tab[i]之间没有交集    for(int i=0;i<64;i++) tab[i] = NULL;        int k = 0;   //k表示tab[k-1]是最后一个非空的节点指针    ListNode *cur = head;    ListNode *carry;    while(cur!=NULL){        carry = cur;          cur = cur->next;        for(int i=0;i<=k;i++){            if(tab[i]==NULL){ //第一次出现的为NULL的tab[i],则将其指向为之前合并的链表的首节点                tab[i] = carry;                if(i==k) k++;                break;            }            else{                int len = 1<<i;                carry = merge(tab[i],len,carry,len); //合并长度相同的已排序链表                 tab[i] = NULL; //清空tab[i]            }        }    }        carry = NULL;    int curLen = 0;    //合并tab中的零散子链表    for(int i=0;i<k;i++){        if(tab[i]!=NULL){            carry = merge(tab[i],1<<i,carry,curLen);            tab[i] = NULL;            curLen += (1<<i);        }    }        return carry;}};




0 0
原创粉丝点击