Sort List
原题链接Sort List
要求在O(nlogn)的时间复杂度下排序链表,且时间复杂度在O(1)
涉及到O(logn)的算法有
- 二分法
- 快速排序
- 归并排序
二分法通常应用在已排序的序列中,且常用语查找算法,而不用作排序算法
快速排序需要从两边向中间逼近,对待链表而言同样不现实
归并排序类似于二分,不断切分已有序列再合并成一个有序序列,排序操作集中在合并过程中且通常是将两个有序序列合并成一个
综上,归并排序是适用于对链表的排序算法,算法流程如下
- 找到当前链表的中点,可以通过walker,runner指针的方法定位链表中部位置
- 将已有链表分成左右两部分分别作为独立的链表继续切分,直到链表只有一个节点或为nullptr时返回
- 在返回的过程中将左右两部分(有序)合并成一个有序链表,继续向上返回
- 最后得到排序后的链表
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head == nullptr || head->next == nullptr)
return head;
/* 找到链表中间的节点,prev代表中间节点的前一个节点 */
ListNode* walker = head;
ListNode* runner = head;
ListNode* prev = nullptr;
while(runner && runner->next)
{
prev = walker;
walker = walker->next;
runner = runner->next->next;
}
prev->next = nullptr;
/* 此时head被分成两部分,head->prev->nullptr是一个,walker->nullptr是一个 */
/* 对左右两部分分别进行切分和排序 */
ListNode* lhs = sortList(head);
ListNode* rhs = sortList(walker);
/* 返回的lhs和rhs是已经排好序的链表,接下来将这两个有序链表合并成一个 */
return mergeSort(lhs, rhs);
}
private:
/* 归并排序,将两个有序链表合并成一个 */
ListNode* mergeSort(ListNode* lhs, ListNode* rhs)
{
ListNode* header = new ListNode(-1);
ListNode* cur = header;
while(lhs || rhs)
{
ListNode* next = nullptr;
if(lhs == nullptr)
{
next = rhs;
rhs = rhs->next;
}
else if(rhs == nullptr)
{
next = lhs;
lhs = lhs->next;
}
else if(lhs->val > rhs->val)
{
next = rhs;
rhs = rhs->next;
}
else
{
next = lhs;
lhs = lhs->next;
}
next->next = nullptr;
cur->next = next;
cur = next;
}
cur = header->next;
delete header;
return cur;
}
};
Sort Colors
原题链接Sort Colors
给定一个数组,数组中的元素只有0,1,2三种,对数组进行排序,要求不能使用标准库的排序函数
数组中只有0,1,2三种数值,那么遍历一遍记录这三个数分别有多少,最后一次赋值即可(低配版的计数排序)
代码如下:
class Solution {
public:
void sortColors(vector<int>& nums) {
vector<int> counts(3, 0);
for(int n : nums)
++counts[n];
int n = 0;
for(int i = 0; i < 3; ++i)
{
for(int j = 0; j < counts[i]; ++j)
nums[n++] = i;
}
}
};
上述两道题都是排序算法的类型,归并排序的时间复杂度是O(nlogn),并且适用于链表。而计数排序不是基于比较的排序算法,复杂度是O(k),k是待排序序列中元素的个数。