力扣147.148排序链表--复习插入、归并和快速排序
插入排序
插入排序就是对当前元素从头搜索其在已排好序列表中的合适位置。
//升序
void InsertSort(vector<int>& nums)
{
int len = nums.size();
for(int i=0;i<len;i++)
{
int cur = nums[i];
//已经排好序的最后一个位置
int last = i-1;
while(last >= 0 && nums[last] > cur)
{
//向后挪动数组
nums[last+1] = nums[last];
last--;
}
//当nums[last] <= cur --> cur 的位置是last+1
nums[last+1] = cur;
}
}
链表的插入排序
由于链表是由指针决定下一个元素,所以不会涉及到覆盖操作,但需要维持一个插入位置前的指针。
ListNode* InsertSort(ListNode* head)
{
if(head == nullptr || head->next == nullptr) return head;
ListNode* dummy = new ListNode(-1,head);
//已排序好数组的最后一个元素
ListNode* sorted = head;
//待排序元素
ListNode* cur = head->next;
while(cur)
{
//如果sorted的值<=cur 说明cur在合适的位置
if(sorted->val <= cur->val)
{
sorted = sorted->next;
}
//从头搜索cur的位置
else
{
ListNode* pre = dummy;
sorted->next = cur->next;
//在已排序的链表中寻找最后一个小于cur的节点
while(pre->next && pre->next->val <= cur->val)
{
pre = pre->next;
}
cur->next = pre->next;
pre->next = cur;
}
cur = sorted->next;
}
return dummy->next;
}
递归的归并排序
归并排序主要流程是拆分 -- 排序 -- 合并 -- 排序 -- 合并
//拆分
void mergeSort(vector<int>& nums,int start, int end)
{
if(start>=end) return ;
int mid = start+(end-start)/2;
mergeSort(nums,start,mid);
mergeSort(nums,mid+1,end);
//最后一层排序
merge(nums,start,mid,end);
}
//排序
void merge(vector<int>& nums,int start, int mid, int end)
{
vector<int> temp(end-start+1);
int p1 = start;
int p2 = mid+!;
int p = 0;
while(p1<=mid && p2<=end)
{
if(nums[p1] < nums[p2])
{
temp[p++] = nums[p1++];
}
else
{
temp[p++] = nums[p2++];
}
}
while(p1<=mid)
{
temp[p++] = nums[p1++];
}
while(p2<=end)
{
temp[p++] = nums[p2++];
}
for(int i = 0;i<temp.size();i++)
{
nums[i] = temp[i];
}
}
//实际调用
void sort(vector<int>& nums)
{
mergeSort(nums,0,nums.size()-1);
}
对于链表的递归归并排序
最主要的就是如何找到中间节点,可以利用快慢指针来寻找中间节点,然后在mergeSort函数中记录中间节点的下一个节点,然后断开mid->next = nullptr;
class Solution {
public:
//寻找中点
ListNode* FindMid(ListNode* head)
{
if(head == nullptr) return nullptr;
//快慢指针
ListNode* fast = head->next;
ListNode* slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
//合并
ListNode* merge(ListNode* left,ListNode* right)
{
ListNode* dummy = new ListNode(-1,left);
ListNode* cur = dummy;
while(left && right)
{
if(left->val < right->val)
{
cur->next = left;
left = left->next;
}
else
{
cur->next = right;
right = right->next;
}
cur = cur->next;
}
while(left)
{
cur->next = left;
left = left->next;
cur = cur->next;
}
while(right)
{
cur->next = right;
right = right->next;
cur = cur->next;
}
return dummy->next;
}
//归并排序
ListNode* mergeSort(ListNode* head)
{
//当为空或者只有一个节点时直接返回
if(head == nullptr || head->next == nullptr)
{
return head;
}
//寻找中间节点
ListNode* mid = FindMid(head);
ListNode* next = mid->next;
//断开分段
mid->next = nullptr;
//划分
ListNode* left = mergeSort(head);
ListNode* right = mergeSort(next);
//合并
return merge(left,right);
}
public:
ListNode* sortList(ListNode* head) {
return mergeSort(head);
}
};
快速排序
冒泡排序基础上使用了递归分治法,快速排序在每一轮挑选一个基准元素,并让其他比它大的元素移到数列一边,比他小的移动到另一边,进行拆解。
void quicksort(vector<int>& nums,int left,int right)
{
if(left >= right) return;
int l = left-1;
int r = right+1;
//基准
int mid = nums[left+right >> 1];
while(l < r)
{
while(nums[++l]<mid);
while(nums[--r]>mid);
if(l<r) swap(nums[l],nums[r]);
}
quicksort(nums,left,r);
quicksort(nums,r+1,right);
}
快速排序链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* FindMid(ListNode* head)
{
//if(head == nullptr || head->next == nullptr) return head;
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast->next)
{
fast= fast->next->next;
slow = slow->next;
}
return slow;
}
ListNode* quickSort(ListNode* head)
{
if(head == nullptr || head->next == nullptr) return head;
//3组指针:小于 等于 大于 基准值的边界节点
ListNode* leftmin = new ListNode(-1);
ListNode* rightmin = leftmin;
ListNode* lefteq = new ListNode(-1);
ListNode* righteq = lefteq;
ListNode* leftmax = new ListNode(-1);
ListNode* rightmax = leftmax;
ListNode* cur = head;
//选择中间节点为基准
int pivot = FindMid(head)->val;
while(cur)
{
//记录当前分段头节点的下一个节点
ListNode* next = cur->next;
//当当前分段的第一个值小于当前分段的基准,截断
//更新小于的区间
if(cur->val < pivot)
{
cur->next = nullptr;
rightmin->next = cur;
rightmin = rightmin->next;
}
else if(cur->val > pivot)
{
cur->next = nullptr;
rightmax->next = cur;
rightmax = rightmax->next;
}
else
{
cur->next = nullptr;
righteq->next = cur;
righteq = righteq->next;
}
//对下一个节点开始判断
cur = next;
}
//对小于和大于的链表快排
leftmin = quickSort(leftmin->next);
leftmax = quickSort(leftmax->next);
//leftmin 可能为空
lefteq = lefteq->next;
righteq->next = leftmax;
if(leftmin == nullptr) return lefteq;
else
{
rightmin = leftmin;
while(rightmin->next)
{
rightmin = rightmin->next;
}
rightmin->next = lefteq;
return leftmin;
}
}
public:
ListNode* sortList(ListNode* head) {
return quickSort(head);
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了