跳到主要内容选择排序算法原理与实现 | 极客日志C算法
选择排序算法原理与实现
选择排序通过每轮遍历未排序元素找出最小值并交换至前端完成排序。涵盖基本工作原理、标准及双端优化的 C 语言代码实现,以及时间复杂度 O(n^2) 和空间复杂度 O(1) 的分析。该算法属于原地排序,稳定性较差但实现简单。
全栈工匠6 浏览 概述
选择排序(Selection Sort)是一种基础的排序算法,其核心思路是:在每一轮遍历中,从剩余未排序元素中选出最小(或最大)值,并将其放置在已排序序列的末端。
对于排序算法的实现,遵循由局部到整体的思路,先排序好一趟或一个元素,再排列多趟或全部元素。
一、选择排序的工作原理
以排序升序数组为例,工作原理如下:
- 初始化:假设当前数组中,前部分是已经排好序的,后部分是未排序的。
- 寻找最小(或最大)值:遍历未排序的部分,找出其中的最小值(或最大值)。
- 交换位置:将找到的最小值与当前未排序部分的第一个元素交换。
- 重复:缩小未排序部分的范围,重复以上步骤,直到整个数组排好序。

以上述数组为例,假设有一个待排列的数组为:[3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]。
- 第一轮排序:当前未排序部分 [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48],最小的值是 2。将 2 和未排序部分的第一个元素 3 交换,数组变为 [2, 44, 38, 5, 47, 15, 36, 26, 27, 3, 46, 4, 19, 50, 48]。
- 第二轮排序:当前未排序部分 [44, 38, 5, 47, 15, 36, 26, 27, 3, 46, 4, 19, 50, 48],最小值是 3。将 3 和未排序部分的第一个元素 44 交换,数组变为 [2, 3, 38, 5, 47, 15, 36, 26, 27, 44, 46, 4, 19, 50, 48]。
- 第三轮排序:当前未排序部分 [38, 5, 47, 15, 36, 26, 27, 44, 46, 4, 19, 50, 48],最小值是 4。将 4 和未排序部分的第一个元素 38 交换,数组变为 [2, 3, 4, 5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48]。
- 第四轮排序:当前未排序部分 [5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48],最小值是 5(它已经排好序,所以不需要交换)。数组仍然是 [2, 3, 4, 5, 47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48]。
- 第五轮排序:当前未排序部分 [47, 15, 36, 26, 27, 44, 46, 38, 19, 50, 48],最小值是 15。将 15 和未排序部分的第一个元素 47 交换,数组变为 [2, 3, 4, 5, 15, 47, 36, 26, 27, 44, 46, 38, 19, 50, 48]。
- 第六轮排序:当前未排序部分 [47, 36, 26, 27, 44, 46, 38, 19, 50, 48],最小值是 19。将 19 和第一个元素 47 交换,数组变为 [2, 3, 4, 5, 15, 19, 36, 26, 27, 44, 46, 38, 47, 50, 48]。
- 第七轮排序:当前未排序部分 [36, 26, 27, 44, 46, 38, 47, 50, 48],最小值是 26。将 26 和第一个元素 36 交换,数组变为 [2, 3, 4, 5, 15, 19, 26, 36, 27, 44, 46, 38, 47, 50, 48]。
- 第八轮排序:当前未排序部分 [36, 27, 44, 46, 38, 47, 50, 48],最小值是 27。将 27 和第一个元素 36 交换,数组变为 [2, 3, 4, 5, 15, 19, 26, 27, 36, 44, 46, 38, 47, 50, 48]。
- 第九轮排序:当前未排序部分 [36, 44, 46, 38, 47, 50, 48],最小值是 36(它已经排好序,所以不需要交换)。数组仍然是 [2, 3, 4, 5, 15, 19, 26, 27, 36, 44, 46, 38, 47, 50, 48]。
- 第十轮排序:当前未排序部分 [44, 46, 38, 47, 50, 48],最小值是 38。将 38 和第一个元素 44 交换,数组变为 [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 46, 44, 47, 50, 48]。
第十一轮排序:当前未排序部分 [46, 44, 47, 50, 48],最小值是 44。将 44 和第一个元素 46 交换,数组变为 [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 50, 48]。第十二轮排序:当前未排序部分 [46, 47, 50, 48],最小值是 46(它已经排好序,所以不需要交换)。数组仍然是 [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 50, 48]。第十三轮排序:当前未排序部分 [47, 50, 48],最小值是 47(它已经排好序,所以不需要交换)。数组仍然是 [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 50, 48]。第十四轮排序:当前未排序部分 [50, 48],最小值是 48。将 48 和第一个元素 50 交换,数组变为 [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]。第十五轮排序:当前未排序部分 [50],只有一个元素,已经排好序,排序完成。最终排序结果:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]。二、选择排序的代码实现
void SelectSort(int* a, int n) {
for (int i = 0; i < n; i++) {
int min_index = i;
for (int j = i + 1; j < n; j++) {
if (a[j] < a[min_index]) {
min_index = j;
}
}
Swap(&a[i], &a[min_index]);
}
}
三、选择排序的优化
当前版本的选择排序算法在每轮遍历中仅能确定一个最小值(或最大值)。我们考虑对其进行优化,使每轮排序能同时确定最大值和最小值,将最小值置于数组前端,最大值置于末尾,通过这种双端选择的方式,可显著提升排序效率。
void SelectSort(int* a, int n) {
int begin = 0;
int end = n - 1;
while (begin < end)
{
int mini = begin;
int maxi = begin;
for (int i = begin + 1; i <= end; i++) {
if (a[i] < a[mini]) mini = i;
if (a[i] > a[maxi]) maxi = i;
}
Swap(&a[begin], &a[mini]);
if (maxi == begin) {
maxi = mini;
}
Swap(&a[end], &a[maxi]);
begin++;
end--;
}
}
四、选择排序的时空复杂度
4.1 时间复杂度
无论最好、最坏还是平均情况,选择排序的时间复杂度都是:O(n^2)
- 第 1 趟:从 n 个元素里找最小值,要比较 n−1 次
- 第 2 趟:从剩下的 n-1 个元素里找最小值,要比较 n−2 次
- ...
- 第 n-1 趟:比较 1 次
- 比较总次数为:(n−1) + (n−2) + ⋯ + 2 + 1 = (n - 1) * n / 2
- 故而时间复杂度为 O(n^2)
4.2 空间复杂度
选择排序是原地排序,只需要常数个辅助变量(如保存当前最小值下标等),不需要额外开辟与 n 成比例的空间。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online