十大排序算法速记总结
1.快速排序 O(nlog(n)) 不稳定排序
-
选取第一个数为哨兵
-
将比哨兵小的数都交换到前面,比哨兵大的数都交换到后面
-
返回哨兵位置,根据哨兵位置划分左右区间重复第二步,直到各区间只有一个数。
int partition(vector<int> &nums, int left, int right){
int key = nums[left]; // 第一个为哨兵
int i = left, j = right;
while(i < j){
// 要先从右边开始,只要右边的数比哨兵大,就继续
while(i < j && nums[j] >= key){
j--;
}
// 再找左边,只要左边的数<=哨兵就继续
while(i < j && nums[i] <= key){
i++;
}
swap(nums[i], nums[j]); // // 走到这步时,一定有nums[i] > nums[left] 而nums[j] < nums[left],为了nums[i] < nums[j] 所以要进行交换
}
swap(nums[i], key); // 最后将哨兵放到正确的位置上,完成一次partition
return i; // 最后返回哨兵所在位置
}
void QuickSort(vector<int> &nums, int left, int right){
if(left >= right) return;
int pivot = patition(nums, left, right);
QuickSort(nums, 0, pivot - 1);
QuickSort(nums, pivot + 1, right);
}
2.归并排序 O(nlog(n)) 稳定排序
- 将数组划分为左右两个区间,区间内再划分左右区间,直到左右区间中只有一个元素(划分),通过递归实现
- 对左右区间元素进行合并, 也就是merge的过程、
- merge的思想是设置一个长度为左右两区间长度和的辅助数组,设置两个指针分别从左子区间和右子区间的起始处开始,比较这两个指针指向的元素,谁更小就把谁放到辅助数组中,同时该指针+1。
- 当某个区间的指针已经走到头时,跳出循环。将剩余的没有走到头的区间的元素全部搬到辅助数组中。
- 将辅助数组中的元素更新到原数组中。完成一次合并排序。
void merge(vector<int> &nums, int left, int mid, int right){
vector<int> tmp(right - left + 1); // 设置辅助数组
int i = left, j = mid + 1, idx = 0;
while(i <= mid && j <= right){
if(nums[i] <= nums[j]){
tmp[idx++] = nums[i++];
}
else{
tmp[idx++] = nums[j++];
}
}
while(i <= mid){
tmp[idx++] = nums[i++];
}
while(j <= right){
tmp[idx++] = nums[j++];
}
for(int i = 0; i < tmp.size(); i++){
nums[left + i] = tmp[i];
}
}
void MergeSort(vector<int> &nums, int left, int right){
if(left >= right) return;
int mid = left + (right - left) / 2;
MergeSort(nums, left, mid);
MergeSort(nums, mid + 1, right);
merge(nums, left, mid, right);
}
3.堆排序O(nlog(n)) 不稳定排序
思想: 堆是用数组构建的,一个节点i它的左右子节点在数组中的位置分别为2 * i + 1, 2 * i + 2。
首先建立一个堆,然后每次从堆中拿出堆顶元素与数组的最后一个元素交换,再对堆进行调整,循环n次,可以得到排序后的数组。
// 小根堆
void sift(vector<int> &nums, int index, int size){
int i = index, j = 2 * i + 1;
while(j < size){
if(j + 1 < size && nums[j] > nums[j + 1]){ // 比较i的左右孩子,j指向较小者
j++;
}
if(nums[i] < nums[j]) break;
else{
swap(nums[i], nums[j]);
i = j; j = 2 * i + 1; // i指向交换后的新位置,继续向下比较,一直下沉到叶子节点
}
}
}
void HeapSort(vector<int> &nums){
int n = nums,size();
// 初始建堆, 从最后一个非叶子结点开始
for(int i = n / 2 - 1; i < n; i++){
sift(nums, i, n);
}
// 排序,重复移走堆顶元素,再调整堆
for(int i = 0; i < n; i++){
swap(nums[0], nums[n - i - 1]);
sift(nums, i, n - i - 1);
}
}
4.冒泡排序(O(n^2) 稳定排序
冒泡排序就是把小(或大)的元素往前调,比较的是相邻两个元素,交换也发生在这两个元素之间。
- 比较相邻的元素,如果第二个比第一个大,就交换他们两个。
- 对每一对相邻的元素执行步骤一,从开始到结尾,最后一个元素会是最大的数。
- 每次对越来越少的元素重复上面的步骤
void BubbleSort(vector<int> &nums){
for(int i = 0; i < nums.size(); i++){
for(int j = 0; j < nums.size() - i - 1; j++){
if(nums[j] < nums[j + 1]){
swap(nums[j], nums[j]);
}
}
}
}
5.简单插入排序(O(n^2)) 稳定
每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。
初始有序序列为数组第一个元素
void InsertSort(vector<int> &nums){
int n = nums.size();
for(int i = 1; i < n; i++){
int key = nums[i]; // 待插入的数据key
for(int j = i - 1; j >= 0; j--){
if(key >= nums[j]){
break;
}
nums[j + 1] = nums[j];
}
nums[j + 1] = key;
}
}
6.希尔排序(O(n^2 - nlog(2n)) 不稳定)
希尔排序是对直接插入排序的改进,采用插入排序的方法,先让数组中任意间隔为h的元素有序,刚开始h=n/2,接着让h=n/4,让h一直缩小,当h=1时,也就是此时数组中任意间隔为1的元素有序。
排序完成。
void ShellSort(vector<int>& nums){
int n = nums.size();
for(int dis = n / 2; dis >= 1; dis /= 2){
for(int i = dis; i < n; i++){
int key = nums[i];
for(int j = i - dis; j >=0; j--){
if(key >= nums[j])
break;
nums[j + dis] = nums[j];
}
nums[j + dis] = key;
}
}
}
7.选择排序(O(n^2) 不稳定)
思想:给每个位置选择当前元素最小的
- 在未排序的数组中找到最大(小)元素,存放到排序序列的起始位置
- 从剩余未排序的元素中继续找最大(小)元素放到已排序序列的末尾
- 重复直到所有元素均排序
void SelectSort(vector<int> &nums){
int n = nums.size();
int maxIndex = 0;
for(int i = 0; i < n; i++){
maxIndex = i;
for(int j = i + 1; j <n; j++){
if(nums[j] > nums[maxIndex]){
maxIndex = j;
}
}
swap(nums[i], nums[maxIndex]);
}
}
8.计数排序(O(n + k) 稳定)
思想:利用额外的数组空间,但是元素需要在0~k之间,不能小于0
- 首先找到待排序数组中最大和最小的元素,以最大元素值为长度构建辅助数组
- 统计待排序数组中每个值出现的次数,以值为下标,次数为值放入辅助数组中
- 遍历辅助数组,从中按顺序拿出数组值(出现次数)不为0的下标,每拿一次,次数减一,直到为0
- 拿出所有后排序完成。
本质是将值转化为数组索引进行排序。
9.桶排序
将数组分到有限数量的桶里,每个非空的桶再分别排序,从不是空的桶子里把元素再拿出来放回原来序列中。
10.基数排序,O(k * n) 稳定
也是一种桶排序,将整数按位数切分为不同的数字(个位, 十位,百位...),从最低为开始,依次进行桶排序。
所有位数字排序完成后,排序完成
总结:
计数排序,桶排序和基数排序都用到了同的概念,
- 计数排序:每个桶只存储单一键值。
- 桶排序:每个桶存储一定范围的数值
- 基数排序:根据键值的每位数字来分配桶
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!