各种排序算法(C与C++)版
一、冒泡排序
经典排序算法之一,类似于泡泡在水中的运动规律,大的泡泡在上面,小的在下面。在排序过程中,我们把大的数字找出来并让它“冒上去”,小的下去,适合数据量较小,要求排序稳定(不改变相同元素索引的先后顺序),或教学演示。代码奉上:
/*======= 冒泡排序 =======*/
void bubbleSort(int *arr, int n)
{
for (int i = 0; i < n-1; i++) // i表示冒上去的大泡泡的数目
for (int j = 0; j < n-i-1; j++) // n-i-1是因为大的泡泡已经在之前的j循环中冒上去了,不需要再遍历
if (arr[j] > arr[j+1]) // arr[j]更大,把它往后移(大的泡泡冒上去)
{
int tmp = arr[j]; // 交换两数
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
二、选择排序
相较于冒泡排序“找大泡泡”,选择排序是找小的值并把它放在数组前面,数组分为两部分:已排序和未排序,适合数据量小,内存有限制,且不需要稳定性的场合。代码:
/*======= 选择排序 =======*/
void selectSort(int *arr, int n)
{
for (int i = 0; i < n-1; i++) // i表示已经排好序的数字个数
{
int min = arr[i], minIndex = i; // 找最小值及其索引,已经排好了[0,i-1]部分了,从i开始再排
for (int j = i+1; j < n; j++) // 往i后面找[i+1,n)的最小值
if (arr[j] < min) min = arr[j], minIndex = j; // 发现更小值,更新min和min的索引
if (minIndex != i) // 若i之后还有更小的,则把min往前移
{
int tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
}
}
}
三、插入排序
一样也是找小的元素,数组也分为两部分:已排序和未排序,与选择排序的区别是:它是逐个取出未排序部分的元素,插入到已排序部分的正确位置,而选择排序则是每次从未排序部分选出最小的元素,且插入排序稳定。
适用情况:
- 小规模数据集(通常n < 32)
- 数据基本有序的情况
- 作为其他高级排序算法的子程序(如快速排序的优化)
代码如下:
/*======= 插入排序 =======*/
void insertSort(int *arr, int n)
{
for (int i = 1; i < n; i++)
{
int tmp = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > tmp) // 查看前面有没有更小的
{
arr[j+1] = arr[j];
j--;
}
arr[j+1] = tmp;
}
}
四、希尔排序
将序列分为若干个子序列,分别进行插入排序,待整个序列基本有序时,再对全体进行一次插入排序。通过设置不同的步幅gap逐步使数组有序。适用场景: 中等规模数据集,内存受限环境,对稳定性无要求。
代码如下:
/*======= 希尔排序 =======*/
void shellSort(int *arr, int n)
{
for (int gap = n/2; gap > 0; gap /= 2)
for (int i = gap; i < n; i++)
{
int tmp = arr[i], j;
for (j = i; j >= gap && arr[j-gap] > tmp; j -= gap) arr[j] = arr[j-gap];
arr[j] = tmp;
}
}
五、快速排序
平均性能最优秀的排序算法,平均时间复杂度O(nlogn),最坏情况下是O(n²),使用分治算法,原地排序,但是不稳定。
适用场景:
- 大规模数据集
- 平均性能要求较高的场合
- 内存充足且对最坏情况容忍度较高的应用
/*======= 快速排序 =======*/
void quickSort(int *arr, int start, int end)
{
if (start >= end) return; // 递归结束条件
int left = start, right = end, pivot = arr[left]; // 左右边界,取左边第一个作为中间值
while (left < right)
{
while (arr[right] > pivot && left < right) right--; // 中间值的右边,若比pivot大,则可以继续向左移动,直到遇见比pivot小的或者边界相遇
if (left < right) // 若是因为arr[right] > pivot才停止的,说明遇到更小的了
{
arr[left] = arr[right]; // 把小的移到前面
left++; // 左边界右移
}
while (arr[left] < pivot && left < right) left++;
if (left < right)
{
arr[right] = arr[left];
right--;
}
}
arr[left] = pivot; // 把中间值移位
quickSort(arr, start, left-1);
quickSort(arr, left+1, end);
}
归并排序
这种排序方式和快速排序有点相似,都是把大的序列分为小的序列,即使用分治算法,但是快速排序在每一次“分”之前就排好序了,而归并排序是先“分”,一直分到每一个序列都只有一个元素时,再开始排序。怎么排序呢?
1、用一个长度等于两个小序列长度之和的大序列来存放小序列的元素:
int *ans = new int[right-left+1]; // 开新序列,存储两个小序列归并后的有序序列,left,right分别表示两个小序列在arr中对应的起始、末尾元素的索引
2、再把小序列的元素放到大序列ans中:
int i = left, j = mid + 1, idx = 0; // 分别表示两个小序列、大序列的索引,mid = (left + right) / 2
while (i <= mid && j <= right)
{
if (arr[i] <= arr[j]) ans[idx] = arr[i++];
else ans[idx] = arr[j++];
idx++;
}
// 处理小序列剩余未加入到ans中的部分
while (i <= mid) ans[idx++] = arr[i++]; // 左边还有剩余
while (j <= right) ans[idx++] = arr[j++]; // 右边还有剩余
3、把ans中的元素复制到原数组arr中:
for (i = left, j = 0; i <= right; i++, j++) // i用于arr,对于arr的[left,right]部分,j用于ans,从0开始
arr[i] = ans[j];
提供图解参考:

最后奉上完整代码:
/*======= 归并排序 =======*/
/*------- 归并函数 -------*/
void merge(int *arr, int left, int mid, int right)
{
int *ans = new int[right-left+1]; // 开新序列,存储两个小序列归并后的有序序列
int i = left, j = mid + 1, idx = 0; // 分别表示两个小序列、大序列的索引
while (i <= mid && j <= right)
{
if (arr[i] <= arr[j]) ans[idx] = arr[i++];
else ans[idx] = arr[j++];
idx++;
}
// 处理小序列剩余未加入到ans中的部分
while (i <= mid) ans[idx++] = arr[i++]; // 左边还有剩余
while (j <= right) ans[idx++] = arr[j++]; // 右边还有剩余
// 再把有序的大序列ans拷贝到arr的[left,right]内
// i用于arr,对于arr的[left,right]部分,j用于ans,从0开始
for (i = left, j = 0; i <= right; i++, j++) arr[i] = ans[j];
delete[] ans;
}
/*------- 递归函数(其实可以放在主函数中) -------*/
void mergeSort(int *arr, int start, int end)
{
if (start >= end) return;
// “递”过程,把整个序列分割为很多个小序列
int mid = (start + end) / 2;
mergeSort(arr, start, mid);
mergeSort(arr, mid+1, end);
// “归并”过程,把两个小段的有序序列合并成一大段有序序列
merge(arr, start, mid, end);
}
/*------- 主函数 -------*/
void mergeSort(int *arr, int size)
{
mergeSort(arr, 0, size-1);
}
归并排序稳定适用于需要稳定排序、处理链表或外部排序(数据无法完全加载到内存)的场景。
题目依旧推荐洛谷题单:排序
Attention:代码可能有更简洁更高效的写法,此处只是分享本人的学习过程。如果你有好的建议或意见,欢迎在评论区分享,我都会看的并且会一一答复☆: .。. o(≧▽≦)o .。.:☆
还有一些排序算法在赶进度中,请耐心等待……
浙公网安备 33010602011771号