在编程学习中,排序算法是基础且重要的知识点,而简单选择排序作为常用的排序算法之一,常常与冒泡排序被初学者混淆。今天,我们就来深入剖析简单选择排序,通过具体代码示例理解其原理,并清晰梳理它与冒泡排序的关键区别。
简单选择排序的核心思路是 “选最小(或最大),放对应位置”。它的执行过程是:在每一趟排序中,从待排序的元素里找出最小(或最大)元素的位置,然后将该元素与待排序部分的第一个元素进行交换,使得待排序部分的范围逐渐缩小,直到所有元素都完成排序。
简单来说,就像我们整理一堆杂乱的书籍,每次从剩下的书籍里挑出最薄的一本,放在已经整理好的书籍的最后面,重复这个过程,直到所有书籍都按厚度排序好。
我们先来看文档中给出的一段 C 语言代码,这段代码看似在实现排序,但实际上并非标准的简单选择排序,不过我们可以基于它进行修改和分析,更好地理解简单选择排序的逻辑。
文档中的代码如下:
#include<stdio.h>
int turn(int*b,int*c){ // 交换两个元素的函数
nt tem=*b;
*c;
em;
}
int main()
{
arr[10]={11,32,32,43,45,65,70,86,79,0};
i,j;
双重循环进行排序
=0;i<10;i++){
j=0;j<10;j++){
(arr[i]>arr[j]){ // 比较元素大小并交换
urn(&arr[i],&arr[j]);
排序后的数组
r(int a=0;a<10;a++){
intf("%d\n",arr[a]);
}
} pr fo // 输出 } }
} t if for( for(i // int int *c=t *b= i
这段代码的问题在于,内层循环j从 0 开始遍历整个数组,导致在每一轮i的循环中,会进行多次不必要的交换。而标准的简单选择排序会优化这一点,在内层循环中先找到最小元素的索引,仅在找到最小元素后进行一次交换。
下面是修改后的标准简单选择排序代码:
#include<stdio.h>
// 交换两个元素的函数
void turn(int* b, int* c) {
int tem = *b;
*b = *c;
*c = tem;
}
int main() {
int arr[10] = {11, 32, 32, 43, 45, 65, 70, 86, 79, 0};
int n = 10; // 数组长度
int i, j, min_index; // min_index用于存储最小元素的索引
// 简单选择排序核心循环
for (i = 0; i < n - 1; i++) { // 外层循环:确定每一轮的待排序起始位置
min_index = i; // 初始假设待排序部分的第一个元素是最小的
// 内层循环:找到待排序部分的最小元素索引
for (j = i + 1; j < n; j++) {
if (arr[j] < arr[min_index]) {
min_index = j; // 更新最小元素的索引
}
}
// 仅当最小元素不是待排序部分的第一个元素时,才进行交换
if (min_index != i) {
turn(&arr[i], &arr[min_index]);
}
}
// 输出排序后的数组
printf("排序后的数组:\n");
for (int a = 0; a < n; a++) {
printf("%d ", arr[a]);
}
return 0;
}
在这段标准代码中,外层循环i控制待排序部分的起始位置,从 0 到n-2(因为当i为n-1时,只剩下最后一个元素,无需排序)。内层循环j从i+1开始,遍历待排序部分的所有元素,找到最小元素的索引min_index。最后,只有当min_index不等于i(即最小元素不在待排序部分的第一个位置)时,才调用turn函数交换元素。这样一来,每一轮排序只需要进行最多一次交换,大大减少了交换操作的次数。
冒泡排序和简单选择排序都属于简单排序算法,时间复杂度在最坏和平均情况下均为O(n²),但二者在排序原理、交换次数、稳定性等方面存在明显差异,具体如下:
举个形象的例子:如果要将数组[5,3,8,1]按升序排序。
简单选择排序和冒泡排序虽然同属O(n²)时间复杂度的简单排序算法,但二者在核心逻辑和实际性能上有显著区别:
掌握这两种排序算法的区别,不仅能帮助我们在编程练习中选择更合适的排序方式,也能为后续学习更高效的排序算法(如快速排序、归并排序)打下坚实的基础。