【数据结构】选择类排序:简单选择与堆排序

考点频率 :★★★★★(数据结构必考,选择题常考堆的性质与建堆过程,简单选择排序常考时间复杂度)

难度 :⭐⭐⭐⭐(简单选择排序简单,堆排序需要重点理解堆的调整过程)

建议:重点掌握简单选择排序的时间复杂度与稳定性;重点掌握堆的定义(大根堆/小根堆)、建堆过程与堆排序流程

1️⃣ 什么是选择类排序?

选择类排序 的核心思想是:每一轮从待排序序列中选出最小(或最大)的元素,放到已排序序列的末尾

打个比方:你有一堆大小不一的石头,要从小到大排列。你每次都从剩下的石头中找出最小的那颗,放到已经排好的石头堆后面。重复这个过程,直到所有石头都排完。

两类最经典的选择排序:

算法 核心策略 一句话概括
简单选择排序 每轮扫描剩余序列,选最小元素放到前面 "每轮找最小的,放到最前面"
堆排序 利用堆(完全二叉树)快速找到最大/最小元素 "用堆结构加速选择"

2️⃣ 简单选择排序

2.1 核心思想

每一轮从待排序序列 中选出最小的元素,放到已排序序列的末尾。

c 复制代码
void SelectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            Swap(&arr[i], &arr[minIdx]);
        }
    }
}

2.2 执行过程示例

对数组 [5, 3, 8, 1, 4] 进行简单选择排序:

轮次 未排序部分 最小元素 交换后结果
初始 [5, 3, 8, 1, 4] --- ---
第1轮 [5, 3, 8, 1, 4] 1(下标3) [1, 3, 8, 5, 4]
第2轮 [3, 8, 5, 4] 3(下标1) [1, 3, 8, 5, 4]
第3轮 [8, 5, 4] 4(下标4) [1, 3, 4, 5, 8]
第4轮 [5, 8] 5(下标3) [1, 3, 4, 5, 8]

2.3 复杂度与特点

情况 时间复杂度 说明
最好情况 O(n2)O(n^2)O(n2) 即使有序,仍需扫描找最小值
最坏情况 O(n2)O(n^2)O(n2) 与最好情况相同
平均情况 O(n2)O(n^2)O(n2) 与最好情况相同
空间复杂度 O(1)O(1)O(1) 原地排序

稳定性 :❌ 不稳定 (交换操作可能改变相等元素的相对顺序,如 [2, 2, 1] 第一轮会将第一个2与1交换,两个2的相对顺序被破坏)
关键特点 :简单选择排序的比较次数与初始序列无关 ,始终为 n(n−1)/2n(n-1)/2n(n−1)/2 次。

3️⃣ 堆排序(Heap Sort)

3.1 什么是堆?

堆(Heap) 是一种特殊的完全二叉树,满足以下条件:

类型 条件 用途
大根堆(Max Heap) 任意节点的值 ≥ 其子节点的值 升序排序(每次取堆顶最大值)
小根堆(Min Heap) 任意节点的值 ≤ 其子节点的值 降序排序(每次取堆顶最小值)

堆的存储 :用数组 存储完全二叉树,下标从 111 开始(或从 000 开始,软考中下标从1开始更常见)。

数组下标从 111 开始时:节点 iii 的左子节点为 2i2i2i,右子节点为 2i+12i+12i+1,父节点为 ⌊i/2⌋\lfloor i/2 \rfloor⌊i/2⌋。

3.2 堆排序的核心思想

堆排序利用堆结构快速找到最大(或最小)元素,分为两步:

  1. 建堆:将无序序列构建成一个大根堆(或小根堆)
  2. 排序:反复取出堆顶元素(最大值),放到数组末尾,然后对剩余元素重新调整成堆

打个比方:你有一堆大小不一的石头,想从大到小排列。你先花点功夫把所有石头按"父大子小"的规则堆成一个"金字塔"(建堆)。然后每次从塔顶拿走最大的石头(堆顶),把剩下的石头重新调整成金字塔(堆调整)。这样每次都能拿到剩余石头中最大的,不用每次都扫描所有石头。

3.3 建堆过程(核心考点)

建堆的步骤:

  1. 将待排序序列看作一棵完全二叉树
  2. 最后一个非叶子节点开始,依次向前调整,使每个子树都满足堆的性质

最后一个非叶子节点的位置 :n/2n/2n/2(下标从 111 开始)

建堆示例 :将序列 [4, 10, 3, 5, 1, 2, 8] 建成大根堆

复制代码
初始完全二叉树:
        4
      /   \
     10    3
    / \   / \
   5   1 2   8

节点编号(从1开始):1:4, 2:10, 3:3, 4:5, 5:1, 6:2, 7:8
n=7,最后一个非叶子节点 = 7/2 = 3(节点3,值为3)

调整过程

步骤 调整节点 操作 结果
1 节点3(值3) 子节点为6(2)、7(8),8最大且>3,交换3和8 节点3变为8,节点7变为3
2 节点2(值10) 子节点为4(5)、5(1),10最大,无需交换 不变
3 节点1(值4) 子节点为2(10)、3(8),10最大且>4,交换1和2 节点1变为10,节点2变为4
4 节点2(值4) 子节点为4(5)、5(1),5最大且>4,交换2和4 节点2变为5,节点4变为4

最终大根堆

复制代码
        10
      /    \
     5      8
    / \    / \
   4   1  2   3

数组表示[10, 5, 8, 4, 1, 2, 3]

3.4 堆排序过程

建堆完成后,排序过程如下:

  1. 将堆顶(最大值)与堆的最后一个元素交换
  2. 堆的大小减1
  3. 对新的堆顶进行向下调整(重新满足堆的性质)
  4. 重复步骤1-3,直到堆中只剩1个元素

示例(接上面的建堆结果):

步骤 堆顶 交换到末尾 调整后的堆
初始 10 --- [10, 5, 8, 4, 1, 2, 3]
第1次 10 与 3 交换 [3, 5, 8, 4, 1, 2, 10] [8, 5, 3, 4, 1, 2, 10]
第2次 8 与 2 交换 [2, 5, 3, 4, 1, 8, 10] [5, 4, 3, 2, 1, 8, 10]
第3次 5 与 1 交换 [1, 4, 3, 2, 5, 8, 10] [4, 2, 3, 1, 5, 8, 10]
... ... ... ...

最终得到升序序列:[1, 2, 3, 4, 5, 8, 10]

3.5 复杂度与特点

情况 时间复杂度 说明
最好情况 O(nlog⁡n)O(n \log n)O(nlogn) 建堆 O(n)O(n)O(n),排序 O(nlog⁡n)O(n \log n)O(nlogn)
最坏情况 O(nlog⁡n)O(n \log n)O(nlogn) 同最好
平均情况 O(nlog⁡n)O(n \log n)O(nlogn) 同最好

空间复杂度 :O(1)O(1)O(1)(原地排序,仅需几个临时变量)

稳定性 :❌ 不稳定(堆顶与末尾元素交换时可能改变相等元素的相对顺序)

4️⃣ 简单选择 vs 堆排序(完整对比表,重点!)

对比项 简单选择排序 堆排序
核心策略 每轮扫描剩余序列找最小 利用堆结构快速找最大/最小
最好时间复杂度 O(n2)O(n^2)O(n2) O(nlog⁡n)O(n \log n)O(nlogn)
最坏时间复杂度 O(n2)O(n^2)O(n2) O(nlog⁡n)O(n \log n)O(nlogn)
平均时间复杂度 O(n2)O(n^2)O(n2) O(nlog⁡n)O(n \log n)O(nlogn)
空间复杂度 O(1)O(1)O(1) O(1)O(1)O(1)
稳定性 ❌ 不稳定 ❌ 不稳定
比较次数与初始状态是否相关 无关(始终为 n(n−1)/2n(n-1)/2n(n−1)/2) 相关(建堆调整次数与初始序列有关)
适用场景 数据量小、对稳定性无要求 数据量大、追求高效排序

5️⃣ 经典例题

例题1(堆的定义):以下哪个序列可以构成一个大根堆?

A. [10, 7, 8, 5, 6, 4, 3]

B. [10, 7, 8, 5, 6, 9, 3]

C. [10, 7, 8, 5, 6, 4, 9]

D. [10, 7, 8, 5, 6, 4, 11]

解析:大根堆要求每个节点的值 ≥ 其子节点。检查各选项:

  • A:10≥7,8;7≥5,6;8≥4,3 → ✅ 全部满足
  • B:节点3(值8)的子节点为4和9,8≥9?❌ 不满足
  • C:节点2(值7)的子节点为4和9,7≥9?❌ 不满足
  • D:节点1(值10)的子节点为2和11,10≥11?❌ 不满足

A


例题2 (堆排序过程):用堆排序对序列 [3, 6, 5, 8, 2, 1, 7] 进行升序排序,建堆后(大根堆)的数组为( )。

解析

  • n=7,最后一个非叶子节点为 7/2 = 3(节点3,值5)
  • 调整节点3:5的子节点为6(1)、7(7),7最大且>5 → 交换5和7
  • 调整节点2:6的子节点为4(8)、5(2),8最大且>6 → 交换6和8
  • 调整节点1:3的子节点为2(8)、3(7),8最大且>3 → 交换3和8
  • 继续向下调整节点2:值3,子节点为4(6)、5(2),6>3 → 交换3和6

最终建堆结果:[8, 6, 7, 3, 2, 1, 5]

答案[8, 6, 7, 3, 2, 1, 5]

6️⃣ 记忆口诀

选择排序分两种,简单堆排要分清。

简单选择每轮扫,O(n2)O(n^2)O(n2) 不变样。

堆排先建大根堆,堆顶最大末尾归。

向下调整保堆性,O(nlog⁡n)O(n \log n)O(nlogn) 效率高。

7️⃣ 小测验(评论区对答案)

序列 [1, 5, 4, 8, 2, 6, 3] 要建成大根堆,需要从哪个节点开始调整?

A. 节点 1

B. 节点 2

C. 节点 3

D. 节点 4

🔔 本专栏日更,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容

#软考中级 #软件设计师 #选择排序 #堆排序 #排序算法 #数据结构 #软考备考

相关推荐
那年窗外下的雪.1 小时前
Linux 学习笔记
linux·笔记·学习
吴声子夜歌1 小时前
Java面试——基础
java·开发语言·面试
爱奥尼欧2 小时前
【daily practice】最长数字串怎么找?岛屿数量如何沉岛秒数?六棍拼两三角形有几种验法?(5.字符串中找出连续最长的数字串、岛屿数量、拼三角)
笔记·学习
智码看视界2 小时前
Day 56:AI辅助开发全面提效:Copilot + Cursor的Java开发
java·单元测试·copilot·cursor·后端开发·代码审查·ai辅助开发
晚风醉蝶2 小时前
1-16-计数排序-CountingSort
python·算法·排序算法
疯狂打码的少年2 小时前
【数据结构】排序算法:归并排序与基数排序
数据结构·笔记·算法·排序算法
asdfg12589632 小时前
一个例子理解Java中的函数式接口
java·函数式接口
toolsmith2 小时前
一个商业软件的License校验,居然把验签的证书放在了License文件里
java
wuminyu2 小时前
JDK21 FFM异步读取MSG_ZEROCOPY错误队列揭秘
java·linux·c语言·jvm·c++