Leetcode:148_Sort List | O(nlogn)链表排序 | Medium

简介: 题目:Sort List Sort a linked list in O(n log n) time using constant space complexity 看题目有两个要求:1)时间复杂度为O(nlogn);2)空间复杂度为常数,即不能增设额外的空间。

题目:Sort List

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

看题目有两个要求:1)时间复杂度为O(nlogn);2)空间复杂度为常数,即不能增设额外的空间。
满足这样要求的排序算法,我们首先想到快排,合并排序和堆排序。我们来分析下几种排序算法对时间和空间复杂度的要求,堆排序实现上过于繁琐,我们不做考虑。快排的最坏的时间复杂度是O(n^2),平均复杂度为O(nlgn),如果按照题目的严格要求显然快排是不满足的,而且快排的实现引入了递归操作,递归调用的栈空间严格意义上说也是额外空间。另外值得注意的一点是:链表不像数组一样,可以随机访问元素,链表必须顺序访问,所以一般的递归操作很难实现,虽然也可以实现哈,见该文:递归实现链表排序。对于归并排序,我们知道需要O(n)的空间复杂度,即需要一个临时数组来存放排好序的元素,显然也合理,但那是针对的是数组,对于链表,归并排序的空间复杂度为in-place sort,即不需要额外空间就可以完成。另外,归并排序还有一个比较好的优势是其稳定性。所以,对于本题的解法,我们首选归并排序。

归并排序有多种方式,总的来说有三种,1)递归;2)非递归;3)自然合并;详见本文:归并排序的三种实现方法。对于链表,采用非递归的方式更为高效,用以下的一幅图来说明非递归的方式:

将两两子列表进行合并组合,达到排序的目的。本题的代码如下,参考上文实现的。

 1 ListNode *sortList(ListNode *head) 
 2 {
 3     assert(NULL != head);
 4     if (NULL == head)
 5         return NULL;
 6 
 7     ListNode *p = head;
 8     int len = 0;        //get the length of link
 9     while (NULL != p) {
10         p = p->next;
11         len ++;
12     }
13 
14     ListNode *temp = new ListNode(0);
15     temp->next = head;
16     
17     int interval = 1;   //合并子列表的长度
18     for (; interval <= len; interval *= 2) {
19         ListNode *pre = temp;
20         ListNode *first = temp->next;  //两段子列表的起始位置
21         ListNode *second = temp->next;
22 
23         while (NULL != first || NULL != second) {
24             int i = 0;
25             while (i < interval && NULL != second) {
26                 second = second->next; //将second移到第二段子列表的起始处
27                 i ++;
28             }
29 
30             int fvisit = 0, svisit = 0; //访问子列表的的次数<interval,保证列表中的元素全部能被访问
31             while (fvisit < interval && svisit < interval && NULL != first && NULL != second) {
32                 if (first->val < second->val) {
33                     pre->next = first;
34                     pre = first;
35                     first = first->next;
36                     fvisit ++;
37                 }
38                 else {
39                     pre->next = second;
40                     pre = second;
41                     second = second->next;
42                     svisit ++;
43                 }
44             }
45             while (fvisit < interval && NULL != first) {
46                 pre->next = first;
47                 pre = first;
48                 first = first->next;
49                 fvisit ++;
50             }
51             while (svisit < interval && NULL != second) {
52                 pre->next = second;
53                 pre = second;
54                 second = second->next;
55                 svisit ++;
56             }
57             pre->next = second;
58             first = second;
59         }
60     }
61     ListNode *retResult = temp->next;
62     delete temp;
63     temp = NULL;
64     return retResult;
65 }

 

目录
相关文章
|
2月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
37 1
|
2月前
Leetcode第21题(合并两个有序链表)
这篇文章介绍了如何使用非递归和递归方法解决LeetCode第21题,即合并两个有序链表的问题。
52 0
Leetcode第21题(合并两个有序链表)
|
2月前
LeetCode第二十四题(两两交换链表中的节点)
这篇文章介绍了LeetCode第24题的解法,即如何通过使用三个指针(preNode, curNode, curNextNode)来两两交换链表中的节点,并提供了详细的代码实现。
22 0
LeetCode第二十四题(两两交换链表中的节点)
|
2月前
Leetcode第十九题(删除链表的倒数第N个节点)
LeetCode第19题要求删除链表的倒数第N个节点,可以通过快慢指针法在一次遍历中实现。
44 0
Leetcode第十九题(删除链表的倒数第N个节点)
|
2月前
|
索引
力扣(LeetCode)数据结构练习题(3)------链表
力扣(LeetCode)数据结构练习题(3)------链表
95 0
|
2月前
【LeetCode 10】142. 环形链表 II
【LeetCode 10】142. 环形链表 II
23 0
|
2月前
【LeetCode 09】19 删除链表的倒数第 N 个结点
【LeetCode 09】19 删除链表的倒数第 N 个结点
18 0
|
2月前
【LeetCode 08】206 反转链表
【LeetCode 08】206 反转链表
13 0
|
2月前
【LeetCode 06】203.移除链表元素
【LeetCode 06】203.移除链表元素
33 0
|
4月前
|
C++ 索引
leetcode 707.设计链表
本文提供了解决LeetCode 707题"设计链表"的C++实现,包括单链表的节点定义和类方法实现,如添加节点、获取节点值、删除节点等。