news 2026/9/1 12:42:36

C语言选择排序详解:从零实现到复杂度分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言选择排序详解:从零实现到复杂度分析

不用着急,选择排序是排序算法里最像“人脑直觉”的一种。你不需要提前掌握任何高深的数据结构知识,只要理解“找最小、放前面、重复进行”这十二个字,25分钟内完全可以写出自己的排序代码。本文会从零开始,用具体数组演变过程、完整C语言代码、逐步讲解和常见误区排查,把选择排序彻底讲清楚。零基础读者建议按顺序阅读,有基础的可以直接跳到代码实现和复杂度分析部分。

1. 排序场景与选择排序核心思想

1.1 为什么先学选择排序

在实际开发中,排序是出现频率最高的基础操作之一。排行榜、价格区间过滤、按时间倒序展示、关键词匹配后的相关度排序,背后都离不开排序算法。C语言课程把选择排序放在指针、结构体之前讲,是因为它不依赖复杂语法,只需要数组、循环、条件判断和交换变量这四个最基础的能力。

选择排序的核心思想可以浓缩成一句话:每一轮从未排序区间里找到最小值,放到已排序区间的末尾。这句话初看有点绕,我们用打扑克牌的场景来理解。假设你手里有一把乱序的牌,要从左到右排好,最自然的做法是:先把整把牌里最小的一张找出来放到最左边,再从剩下牌里找最小的放到第二位,继续下去直到全部排完。选择排序就是把这个过程用程序实现。

它与冒泡排序的最大区别是:冒泡排序在每一轮里不断交换相邻元素,把大值“冒”到末尾,交换次数很多;选择排序每一轮只记录最小值的位置,本轮结束时才交换一次。所以在数据量较大时,选择排序的交换次数远少于冒泡排序,这是它的一大优势。

1.2 排序术语和区间划分

在学习选择排序前,先统一几个术语含义:

  • 已排序区间:数组左侧已经排好序的部分,初始为空。
  • 未排序区间:数组右侧尚未处理的部分,初始为整个数组。
  • 每轮选择:在未排序区间中找到最小元素的下标,与未排序区间的第一个元素交换。
  • 有序性:排序完成后,任意arr[i] <= arr[j](当i < j时)成立。

举例来说,数组{5, 3, 8, 1, 9, 2}初始时已排序区间为空,未排序区间是[0,5]。第一轮在未排序区间中找到最小值1,下标为3,将它与下标05交换,数组变为{1, 3, 8, 5, 9, 2}。此时已排序区间是[0,0],未排序区间是[1,5]。第二轮在[1,5]里找最小值2,与下标13交换,数组变为{1, 2, 8, 5, 9, 3}。这个流程一直持续,直到未排序区间只剩一个元素,排序自然结束。

这种“区间不断向右扩张”的思路在后续学习快速排序、归并排序时也会反复出现,理解选择排序的区间划分对后续算法学习非常有帮助。

2. 选择排序完整过程拆解

2.1 逐步演变示例

下面用一个更完整的例子演示全过程。我们使用数组:

int arr[6] = {64, 25, 12, 22, 11};

数组长度为 5,因此总共需要执行 4 轮(最后一个元素不需要再比较)。

第一轮,i = 0

  • 假设最小值下标minIndex = 0,即arr[0] = 64
  • j1遍历到4,逐个比较。
  • arr[1] = 25 < 64,更新minIndex = 1
  • arr[2] = 12 < 25,更新minIndex = 2
  • arr[3] = 22 > 12,不更新。
  • arr[4] = 11 < 12,更新minIndex = 4
  • 遍历结束后,最小值下标是4,交换arr[0]arr[4],数组变为{11, 25, 12, 22, 64}

第二轮,i = 1

  • 假设minIndex = 1,即arr[1] = 25
  • j2遍历到4
  • arr[2] = 12 < 25,更新minIndex = 2
  • arr[3] = 22 > 12,不更新。
  • arr[4] = 64 > 12,不更新。
  • 交换arr[1]arr[2],数组变为{11, 12, 25, 22, 64}

第三轮,i = 2

  • 假设minIndex = 2,即arr[2] = 25
  • j3遍历到4
  • arr[3] = 22 < 25,更新minIndex = 3
  • arr[4] = 64 > 22,不更新。
  • 交换arr[2]arr[3],数组变为{11, 12, 22, 25, 64}

第四轮,i = 3

  • 假设minIndex = 3,即arr[3] = 25
  • j4遍历到4
  • arr[4] = 64 > 25,不更新。
  • 交换arr[3]arr[3],相当于没有变化。

排序结果:{11, 12, 22, 25, 64}

2.2 i、j、minIndex 的作用

在代码实现中,三个变量构成了选择排序的骨架:

  • i:外循环变量,表示当前未排序区间的起点,也是“本轮最小值要放入的位置”。
  • j:内循环变量,从i + 1开始,一直扫描到数组末尾。
  • minIndex:记录当前轮次中最小值所在下标。它会在每一轮开始时被赋值为i,然后在内循环中不断被更新。

很多初学者会问:为什么每次都要用minIndex记录下标,而不是直接用minValue记录最小值?这是一个很重要的问题。如果只记录值,那么当你在扫描结束后需要把最小值放到i位置时,你根本不知道它原来在哪里,也就无法完成交换。所以记录下标是必须的。

2.3 边界条件和循环次数

对于长度为n的数组,外循环i的范围是0n-2,也就是说只需要执行n-1轮。为什么不是n轮?因为当i = n-1时,未排序区间只剩一个元素,它必然是最大值,不需要再比较。

内循环ji + 1n-1。当i = 0时,内循环比较n-1次;当i = n-2时,内循环比较1次。所以总的比较次数是固定的:

(n-1) + (n-2) + ... + 1 = n(n-1)/2

这个公式意味着选择排序的比较次数与数组初始顺序无关,即使数组已经有序,它依然要比较这么多次。

3. C语言选择排序完整代码实现

3.1 最简完整版

下面是一份可以直接复制运行的完整C语言代码。它包含了数组定义、选择排序函数、数组打印函数和主函数。

#include <stdio.h> // 选择排序函数,参数为数组首地址和数组长度 void selectionSort(int arr[], int n) { int i, j, minIndex; int temp; for (i = 0; i < n - 1; i++) { // 每一轮开始时,假设当前位置就是最小值位置 minIndex = i; // 在未排序区间 [i+1, n-1] 中寻找更小值 for (j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } // 如果最小值不是当前位置,才进行交换 if (minIndex != i) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } } // 打印数组函数 void printArray(int arr[], int n) { int i; for (i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr) / sizeof(arr[0]); printf("排序前数组: "); printArray(arr, n); selectionSort(arr, n); printf("排序后数组: "); printArray(arr, n); return 0; }

运行结果如下:

排序前数组: 64 25 12 22 11 排序后数组: 11 12 22 25 64

这份代码中有几个细节需要说明:

  • sizeof(arr) / sizeof(arr[0])是C语言中计算数组长度的常用方式,它用数组总字节数除以单个元素字节数,得到元素个数。
  • if (minIndex != i)这个判断避免了不必要的交换。如果本轮最小值已经在正确位置,交换反而会浪费一次操作。
  • 选择排序使用临时变量temp完成交换,不需要引入额外的库函数。

3.2 函数封装版

在实际项目中,排序逻辑通常封装为独立函数,并通过参数传递数组指针。下面的代码更适合作为工程代码的参考:

#include <stdio.h> // 使用指针方式实现选择排序 void selectionSortByPointer(int *arr, int n) { int i, j, minIndex; int temp; for (i = 0; i < n - 1; i++) { minIndex = i; for (j = i + 1; j < n; j++) { // 指针方式访问数组元素 if (*(arr + j) < *(arr + minIndex)) { minIndex = j; } } if (minIndex != i) { temp = *(arr + i); *(arr + i) = *(arr + minIndex); *(arr + minIndex) = temp; } } } int main() { int arr[] = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; int n = sizeof(arr) / sizeof(arr[0]); int i; printf("排序前: "); for (i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); selectionSortByPointer(arr, n); printf("排序后: "); for (i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

3.3 带过程输出的学习版

对于零基础读者,最好的学习方式是观察每一轮的变化。下面这个版本会在每一轮结束后打印当前数组状态,帮助你建立直观的“过程感”:

#include <stdio.h> void selectionSortWithProcess(int arr[], int n) { int i, j, minIndex; int temp; for (i = 0; i < n - 1; i++) { minIndex = i; for (j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } } if (minIndex != i) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } // 打印每一轮的结果 printf("第 %d 轮后: ", i + 1); for (j = 0; j < n; j++) { printf("%d ", arr[j]); } printf("\n"); } } int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr) / sizeof(arr[0]); printf("初始数组: "); int i; for (i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); selectionSortWithProcess(arr, n); return 0; }

运行结果:

初始数组: 64 25 12 22 11 第 1 轮后: 11 25 12 22 64 第 2 轮后: 11 12 25 22 64 第 3 轮后: 11 12 22 25 64 第 4 轮后: 11 12 22 25 64

从输出中可以很清楚地看到,每一轮都在把未排序区间的最小值放到左侧,这就是选择排序的直观特征。

4. 代码逐行精讲与复杂度分析

4.1 关键代码行讲解

以最简版代码为例,我们逐段分析核心逻辑。

for (i = 0; i < n - 1; i++)

这个外层循环控制轮数。每执行一轮,i位置的元素就固定下来,成为已排序区间的一部分。因为最后一个元素无需处理,所以循环条件是i < n-1

minIndex = i;

每轮开始时,先把当前位置i假设为最小值位置。这里不能把minIndex初始化成0,因为随着轮次推进,已排序区间左侧的元素已经固定,如果每次从0开始,会把已经排好的元素再次参与比较,导致逻辑错误。

for (j = i + 1; j < n; j++) { if (arr[j] < arr[minIndex]) { minIndex = j; } }

内层循环从i + 1开始,到数组末尾结束。每次比较arr[j]是否比当前最小值还小,如果是就更新minIndex。这个过程本质上是“打擂台”:minIndex是擂主,每个arr[j]都来挑战,谁更小谁成为新的擂主。

if (minIndex != i) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; }

找到真正的最小值下标后,如果它不在i位置,就交换两个元素。这里使用temp作为中间变量,是C语言交换两个变量的经典写法。也可以使用异或运算来交换:

arr[i] = arr[i] ^ arr[minIndex]; arr[minIndex] = arr[i] ^ arr[minIndex]; arr[i] = arr[i] ^ arr[minIndex];

但异或交换可读性较差,实际工程中不推荐,本文只做了解即可。

4.2 时间复杂度分析

选择排序的时间复杂度需要从两个维度来看。

比较次数:无论数组初始状态如何,比较次数都是固定的n(n-1)/2。这是因为每一轮都需要完整遍历未排序区间。即使数组已经有序,选择排序依然会执行全部比较。

交换次数:每轮最多交换一次,总共最多交换n-1次。最少交换 0 次(数组完全有序时,每轮最小值都在正确位置)。

所以:

  • 最好情况时间复杂度:O(n^2)(比较次数不变,但交换次数为0)
  • 最坏情况时间复杂度:O(n^2)
  • 平均情况时间复杂度:O(n^2)

这个特性让选择排序在数据规模较小时表现尚可,但数据量增大后性能下降明显。例如n = 10000时,比较次数约为 5000 万次,在普通计算机上需要几百毫秒甚至更久。

4.3 空间复杂度分析

选择排序只使用了一个临时变量temp和几个循环变量,额外空间不随数据规模增长而变化,因此空间复杂度为O(1),属于原地排序算法。

4.4 稳定性分析

选择排序是不稳定的排序算法。这句话的意思是:如果数组中有两个相等的元素,排序后它们原本的相对顺序可能发生改变。

举个例子,数组{5, 8, 5, 2, 9},有两个值为5的元素。为了区分,写成{5a, 8, 5b, 2, 9}。第一轮找到最小值2,与5a交换,数组变为{2, 8, 5b, 5a, 9}。此时原本在前面的5a跑到了5b后面,相对顺序被破坏了。

这在某些需要保持原始顺序的场景(比如按成绩排序时希望同分者按学号顺序排列)是不合适的。如果业务要求稳定排序,应该选择插入排序或归并排序。

5. 选择排序动画讲解与手绘思路

5.1 动画讲解的核心逻辑

很多人学习算法时喜欢看动画演示,因为动画能把抽象的下标变化转化为视觉过程。选择排序的动画演示通常包含以下几个视觉元素:

  • 使用不同颜色区分已排序区间和未排序区间。
  • 用高亮标记当前minIndex的位置。
  • 用扫描光标的移动表示j的遍历。
  • 找到最小值后,展示一次交换动画。

如果你想自己制作动画,或者在学习时自己手动模拟,可以遵循以下步骤:

  1. 用 Excel 或纸笔画出一个表格,每个格子代表一个数组元素。
  2. 用绿色标记已排序区间,用白色标记未排序区间。
  3. 每一轮开始时,用红色框标记i位置。
  4. 用黄色扫描未排序区间,找到比当前最小值更小的元素时,更新红色框的位置。
  5. 扫描结束后,交换两个格子的值,用动画展示交换过程。

5.2 用字符界面对比冒泡排序动画效果

如果你希望用代码模拟动画效果,可以在每一轮结束后输出当前数组状态,这就是最简单的“字符动画”。下面是一个模拟选择排序扫描过程的代码:

#include <stdio.h> void printWithHighlight(int arr[], int n, int scanPos, int minIdx) { int i; for (i = 0; i < n; i++) { if (i == scanPos) { printf("[%d] ", arr[i]); // 用方括号表示当前扫描位置 } else if (i == minIdx) { printf("{%d} ", arr[i]); // 用花括号表示当前最小值 } else { printf(" %d ", arr[i]); } } printf("\n"); } int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr) / sizeof(arr[0]); int i, j, minIndex; int temp; printf("初始状态: "); printWithHighlight(arr, n, -1, -1); for (i = 0; i < n - 1; i++) { minIndex = i; printf("第 %d 轮开始,假设最小值在位置 %d\n", i + 1, i); for (j = i + 1; j < n; j++) { printWithHighlight(arr, n, j, minIndex); if (arr[j] < arr[minIndex]) { minIndex = j; printf("发现更小值 %d 在位置 %d\n", arr[j], j); } } if (minIndex != i) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } printf("本轮结束,数组变为: "); for (j = 0; j < n; j++) { printf("%d ", arr[j]); } printf("\n\n"); } return 0; }

这段代码通过printWithHighlight函数,在每一轮内循环中打印扫描位置和当前最小值位置。虽然它不是真正的动画,但通过观察输出,可以清晰理解jminIndex的变化过程。

5.3 动画中的常见误区

有些动画会把选择排序画成“相邻元素不断交换”,这其实是冒泡排序的画面。选择排序的动画应该是一轮只交换一次。如果你看到的动画中每一轮有多次交换,那描述的是冒泡排序或者某种优化变体。理解这一点有助于区分两种算法。

6. 选择排序常见错误与排查

6.1 初学者最容易犯的四个错误

错误一:内层循环从 0 开始

有些初学者会把内层循环写成for (j = 0; j < n; j++),这会导致已经排好序的左侧元素再次参与比较。虽然最终结果可能正确,但效率更低,而且在某些情况下会破坏已排序区间。

正确的写法是for (j = i + 1; j < n; j++)

错误二:忘记更新 minIndex

if (arr[j] < arr[i]) { // 错误示范 // 没有更新 minIndex }

这段代码的问题在于,它总是和arr[i]比较,而不是和当前最小值比较。如果内层循环中先遇到了一个比arr[i]小的值,但后面又有一个更小的值,minIndex仍然指向第一个较小值,导致交换错误。

错误三:交换时写错下标

temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = arr[i]; // 错误!此时 arr[i] 已经被修改

这是一个非常经典的错误。交换三个语句中,第一句已经把arr[i]保存在temp中,第二句又把新值赋给了arr[i],所以第三句必须使用temp而不是arr[i]

错误四:数组越界

如果外循环写成i < n,当i = n-1时,内循环j = i + 1 = n,访问arr[n]就会越界。C语言不会自动检查数组越界,但运行时可能产生未定义行为。

6.2 排查清单

问题现象常见原因解决思路
排序后第一个元素不对minIndex 初始化错误或内层循环起点错误检查 minIndex = i 和内层循环 j = i + 1
数组中有元素丢失交换逻辑错误检查 temp 是否被正确使用
程序崩溃或卡死数组越界检查外循环 i < n-1,内循环 j < n
原数组被意外修改数组传参后直接修改如需要保留原数组,用副本排序
排序结果不稳定(相等元素顺序改变)选择排序本身不稳定需要稳定排序时改选插入排序

6.3 如何快速验证排序是否正确

写完选择排序后,可以用以下方法验证:

  1. 使用一个很小的数组(比如 5 个元素)手工推演,对照程序输出。
  2. 用随机数据填充数组,排序后检查是否满足arr[i] <= arr[i+1]
  3. 使用边界数据:空数组、只有一个元素的数组、所有元素相同的数组、已经有序的数组、逆序数组。
  4. 在编译时加上-Wall -Wextra选项,让编译器帮忙检查警告。
gcc -Wall -Wextra selection_sort.c -o selection_sort

7. 选择排序与冒泡排序的详细对比

7.1 对比表格

比较维度选择排序冒泡排序
核心思想每轮选出最小值放到前面每轮把最大值冒泡到最后
交换次数最多 n-1 次最多 n(n-1)/2 次
比较次数n(n-1)/2 次n(n-1)/2 次
最好时间复杂度O(n^2)O(n)(优化后)
最坏时间复杂度O(n^2)O(n^2)
空间复杂度O(1)O(1)
稳定性不稳定稳定
适用场景交换成本高的场景基本有序、数据量小的场景

7.2 什么时候用选择排序

选择排序的优点是代码简单、交换次数少。如果排序过程中交换元素的代价比比较元素更大(例如元素是大型结构体,复制成本高),选择排序的少量交换就有实际意义。但在大多数现代应用中,选择排序的时间复杂度劣势更明显,因此更适合作为教学算法,而不是大数据量的生产排序方案。

7.3 选择排序的优化变体

一个常见的优化是二元选择排序:每轮同时找到最大值和最小值,最小值放前面,最大值放后面。这样可以减少一半的轮次,比较次数不会减少,但常数因子会变小。

void selectionSortOptimized(int arr[], int n) { int left = 0, right = n - 1; int i, minIndex, maxIndex; int temp; while (left < right) { minIndex = left; maxIndex = left; for (i = left + 1; i <= right; i++) { if (arr[i] < arr[minIndex]) { minIndex = i; } if (arr[i] > arr[maxIndex]) { maxIndex = i; } } // 最小值放到 left temp = arr[left]; arr[left] = arr[minIndex]; arr[minIndex] = temp; // 如果最大值原来在 left 位置,需要更新 maxIndex if (maxIndex == left) { maxIndex = minIndex; } // 最大值放到 right temp = arr[right]; arr[right] = arr[maxIndex]; arr[maxIndex] = temp; left++; right--; } }

这个优化版需要注意的是,如果最大值原本在left位置,交换最小值后,最大值的位置被移到了minIndex,所以需要更新maxIndex。这是一个容易出错的细节,也是面试中常见的变体考题。

8. 实际工程中的最佳实践

8.1 排序算法的选型建议

在生产环境中,C语言项目通常不会手写选择排序。标准库qsort提供了基于快速排序的通用排序函数,使用起来更安全、性能更好。但在以下场景中,手写选择排序仍然有意义:

  • 学习算法原理,为后续学习更复杂排序打基础。
  • 嵌入式系统中数据量很小且代码需要保持极简。
  • 需要拓扑排序、优先队列等场景中的部分逻辑借鉴。
  • 面试考察基础编码能力时。

8.2 qsort 函数与选择排序的关系

C语言标准库的qsort函数是通用的排序接口,它接收比较函数作为参数,因此可以对任意类型数组排序。如果你在工程中需要排序,优先使用qsort而不是手写排序算法:

#include <stdio.h> #include <stdlib.h> int compareInt(const void *a, const void *b) { return (*(int *)a - *(int *)b); } int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr) / sizeof(arr[0]); int i; qsort(arr, n, sizeof(int), compareInt); for (i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

运行结果:

11 12 22 25 64

但需要说明的是,qsort内部实现通常是不稳定的(快速排序本身也不稳定),所以在要求稳定排序时,仍需手动实现归并排序或插入排序。

8.3 代码工程化规范

如果在学习项目或小型工具中确实需要手写选择排序,建议遵循以下规范:

  • 函数命名使用selectionSort,参数为(int arr[], int n),避免全局变量。
  • 使用const关键字修饰不会修改的入参。
  • 数组长度通过sizeof计算,不要硬编码。
  • 排序函数不打印内容,打印由调用方完成,保持职责单一。
  • 添加必要的注释,说明算法复杂度和稳定性特征。
  • 使用size_t类型表示数组长度,避免类型转换问题。

下面是符合规范的函数头部示例:

/** * 使用选择排序算法对整型数组进行升序排序 * @param arr 待排序数组 * @param n 数组长度 * @note 时间复杂度 O(n^2),空间复杂度 O(1),不稳定排序 */ void selectionSort(int arr[], size_t n);

8.4 排序前考虑数据特征

在真实项目中,排序并非无脑套用算法。你应该先考虑数据特征:

  • 数据规模有多大?
  • 数据是否基本有序?
  • 是否要求稳定性?
  • 能否使用额外空间?
  • 排序是内排序还是外排序?

选择排序只在数据量极小或交换成本极高时具有优势。对于海量数据,更应该考虑快速排序、归并排序或堆排序。不同算法之间不是“谁替代谁”的关系,而是各有适用场景。

9. 从选择排序到更广阔的算法世界

9.1 排序算法的学习路线图

掌握选择排序后,建议按以下顺序继续学习:

  1. 冒泡排序:理解相邻交换和提前退出优化。
  2. 插入排序:理解“将新元素插入已排序区间”的思想,适合小规模数据。
  3. 希尔排序:理解“分组插入”的概念,是插入排序的改进。
  4. 归并排序:理解分治法和合并过程,稳定排序的代表。
  5. 快速排序:理解分区操作和递归思想,工业界最常用的排序算法。
  6. 堆排序:理解完全二叉树和堆化过程。

这些排序算法在CSDN上都有大量高质量文章,搜索时可以对比阅读,重点看“过程演示”和“代码注释”部分。

9.2 选择排序思想在其他地方的应用

选择排序的“每轮选择最小”思想并不仅限于排序。比如:

  • 堆排序本质上是对“选择最小值”操作进行了优化,把查找最小值的时间从 O(n) 降为 O(log n)。
  • 图论的 Prim 最小生成树算法不断选择最小权值边,与选择排序的“选择”思想同源。
  • Dijkstra 最短路径算法中,每次从未处理集合中选出距离最小的节点,也是类似思路。

所以理解选择排序,不仅仅是为了学会一个排序算法,更是为了理解“贪心选择”这个更通用的算法思维。

9.3 动手实践建议

学习算法最忌讳“只看不练”。建议按以下步骤动手:

  1. 先用笔在纸上手动模拟一遍选择排序过程,不要看代码。
  2. 写代码时先不参考本文,自己尝试实现。
  3. 编写完成后,用随机数组验证结果。
  4. 修改代码,实现降序排序。
  5. 尝试对字符数组或者字符串数组排序。
  6. 尝试实现二元选择排序优化版本。
  7. 使用调试器设置断点,观察每一轮变量变化。

每完成一步,你对算法的理解就会加深一层。特别是第 4 步,只需要把arr[j] < arr[minIndex]改成arr[j] > arr[maxIndex],但这个过程能帮你真正理解算法结构。

9.4 最后说点实在的

选择排序在生产环境中的出场率并不高,但它是算法入门阶段极好的一块“磨刀石”。通过它你可以掌握几个终身受用的核心技能:用循环控制区间、用变量记录状态、用交换完成元素移动、用复杂度分析评估算法优劣。这些能力在后续学习链表、二叉树、图论时都会反复用到。

如果你能把本文的例子完整手敲一遍,再独立完成降序排序和二元选择排序的改编题,那么选择排序这一关就算真正过关了。继续往下学,后面还有更有趣的排序算法在等你。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/1 12:41:08

用Python打造A股量化选股工具包:10大策略整合实践

简介&#xff1a;面向A股个人投资者与量化初学者的Python选股工具包&#xff0c;基于TuShare获取实时行情与基本面数据&#xff0c;内置10种常见技术选股逻辑&#xff0c;如250日均线突破、平台突破、回踩均线、停机坪形态、低ATR波动筛选、持续上涨趋势识别等&#xff0c;适合…

作者头像 李华
网站建设 2026/9/1 12:40:51

智能体研发步骤证据工具:从输入校验到离线报告的完整实现

智能体研发步骤证据工具&#xff1a;从输入校验到离线报告的完整实现 项目编号&#xff1a;20260901-003。本文代码、测试、文档、示例数据和效果图均为独立编写&#xff0c;不包含热点产品或开源项目源码、品牌素材与官方截图。 问题与目标 关联需求、设计、实现、测试、评审…

作者头像 李华
网站建设 2026/9/1 12:38:02

清华大学C语言教程:198集系统学习与开发环境搭建指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/1 12:35:20

基于STM32的五种验证方式融合门禁系统设计与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/1 12:33:22

Fluent UDF造波全解析:二阶Stokes波浪模拟从公式到调参

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/1 12:30:32

Unity 2D俯视角射击游戏源码实战解析:角色控制、敌人AI与血条UI

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华