在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
完整代码
看到时间复杂度为O(nlogn)的要求,就考虑用快排来实现,但是传统的快排(以第一个元素为基准)无法实现链表的移动,这里是单向链表。采用了如下的移动方法(以第一个元素为基准)
快排
#include<bits/stdc++.h>
using namespace std;
void swap(int &a, int &b){//交换两个元素
int temp = a;
a = b;
b = temp;
}
void quickSort(vector<int> &nums, int s, int e){
if(s >= e)//递归终止条件
return;
int flag = nums[e];
int i = s - 1;
for(int j = s; j < e; ++j){
if(nums[j] < flag){
swap(nums[j], nums[++i]);
}
}
swap(nums[++i], nums[e]);
quickSort(nums, s, i - 1);
quickSort(nums, i + 1, e);
}
int main(){
vector<int> nums = {4, 2, 3, 1};
quickSort(nums, 0, nums.size() - 1);
for(auto a : nums)
cout << a << " ";
cout <<"Dsa";
return 0;
}
基本思想
快排
- 交换两个节点时, 这里只是交换的链表的值,并没有改变链表的指向
/**
* 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 == NULL || head->next == NULL)
return head;
ListNode* n_head = new ListNode(0), *end_Node;
n_head->next = head;//指向真正的头节点的节点
ListNode* p = head;
while(p){//尾节点
end_Node = p;
p = p->next;
}
fastSort(n_head, end_Node);
return n_head->next;
}
private:
void fastSort(ListNode* n_head, ListNode* end_Node){
if(n_head == end_Node)//递归结束的条件
return;
ListNode* flag = end_Node;
ListNode* res = n_head, *left = n_head->next;
while(left != end_Node){
if(left->val < end_Node->val){
res = res->next;
swap(res, left);//所需交换的两个节点的前驱
}
left = left->next;
}
//res是左半部分末尾的节点
swap(end_Node, res->next);//已经排好序的值放在了res->next中
fastSort(n_head, res);
fastSort(res->next, end_Node);
}
void swap(ListNode* a, ListNode* b){//交换两个节点的值
int temp = a->val;
a->val = b->val;
b->val = temp;
}
};
在排序算法中除了快排,还有归并排序的时间复杂度也是O(nlogn)
归并排序
参考:添加链接描述
/**
* 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 == NULL || head->next == NULL)
return head;
ListNode *slow = head, *fast = head->next;
while(fast != NULL && fast->next != NULL){//寻找中间结点
slow = slow->next;
fast = fast->next->next;
}
ListNode* right_head = slow->next;
slow->next = NULL;
ListNode* left = sortList(head);
ListNode* right = sortList(right_head);
//归并链表
ListNode* dummy = new ListNode(0), *p;
p = dummy;
while(left && right){
if(left->val < right->val){
p->next = left;
left = left->next;
}
else{
p->next = right;
right = right->next;
}
p = p->next;
}
if(left)
p->next = left;
if(right)
p->next = right;
return dummy->next;
}
};
非递归形式的归并排序
基本思想:
对于整个链表而言,先两两合并,再四四合并,知道待合并的长度大于等于链表长度时不再合并
/**
* 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 == NULL || head->next == NULL)
return head;
//1.统计链表长度
int len = 0;
ListNode *p = head;
while(p){
p = p->next;
++len;
}
int intv = 1;//间隔以2的整数次幂的方式递增
ListNode *res = new ListNode(0);
res->next = head;
while(intv < len){
ListNode *pre = res;
p = res->next;
while(p){//将间隔为intv的链表合并
//第一段链表
ListNode* h1 = p;
int i = intv;
while(i > 0 && p){
p = p->next;
i--;
}
if(p == NULL)//没有第二段链表,无需合并
break;
//第二段链表,(长度不一定是intv)
ListNode* h2 = p;
i = intv;
while(i > 0 && p){
p = p->next;
i--;
}
//合并两段链表
int l1 = intv, l2 = intv - i;
while(l1 > 0 && l2 > 0){
if(h1->val < h2->val){
pre->next = h1;
h1 = h1->next;
--l1;
}
else{
pre->next = h2;
h2 = h2->next;
--l2;
}
pre = pre->next;
}
while(l1){
pre->next = h1;
pre = pre->next;
h1 = h1->next;
--l1;
}
while(l2){
pre->next = h2;
pre = pre->next;
h2 = h2->next;
--l2;
}
// pre->next = (l1 == 0) ? h2 : h1;
// while (l1 > 0 || l2 > 0) {
// pre = pre->next;
// l1--;
// l2--;
// }
pre->next = p;//将已排好序的那一步分和后面的连接上
}
intv *= 2;
}
return res->next;
}
};