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
- Sort List(四种算法)
- STL list::sort算法
- SGI STL: list::sort()算法
- LeetCode算法题目:Sort List
- sort-list(Leetcode)
- STL中的list::sort算法解析
- stl中list的sort算法实现
- stl::list自带的sort算法
- list不支持通用的sort算法
- STL源码分析--list中的sort算法
- STL源码系列--List::sort算法解析
- list不能使用STL算法sort()
- stl中list的sort算法实现
- leetcode 147 Insertion Sort List java 算法
- 算法系列——Sort List
- list排序(Collections.sort)
- 【leetcode】sort list(python)
- Insertion Sort List(medium)
- decltype类型
- Maven 使用指南
- 轻松搞定Linux常用命令
- 蓝桥杯:入门训练 圆的面积【JAVA算法实现】
- 我对基金的浅见
- Sort List(四种算法)
- [C/C++]十-->二进制转换
- 图的遍历递归和非递归实现
- 栈
- effective C++ 第一章:让自己习惯C++
- centos4.X 安装python2.7.X SSL模块
- 字符编码详解
- 【算法】简单的桶排序算法
- thinking In Java ---07 复用类