二叉堆的定义是?
考点:堆的定义(A1)。
解析:二叉堆 = 完全二叉树 + 堆序:大根堆每个节点 所有孩子,小根堆每个节点 所有孩子。✅ 正确
排除法:无(判断题)。混淆点:堆不是二叉搜索树——堆序是父子关系,BST 序是左中右关系。
关联 · 堆与二叉搜索树(A4):两种序的本质区别。
大根堆中,最大值的位置是?
考点:大根堆与小根堆(A2)。
解析:大根堆的堆序保证最大值在根(堆顶)——这是堆 取最值的根基。✅ 正确
排除法:无(判断题)。混淆点:第二大在哪?在第二层(根的孩子之一),但具体哪个孩子不确定。
关联 · 取堆顶(I4): 取顶的代码。
堆用数组存储(下标从 1 开始),节点 的左孩子、右孩子、父节点的下标分别是?
考点:堆的数组存储(A3)。
解析:完全二叉树用数组存(下标 1 起):节点 的左孩子 、右孩子 、父 。✅ 正确
排除法:无(判断题)。混淆点:下标 0 起则左 、右 、父 (P5)。
关联 · 父子下标(I1):代码版。
判断题:大根堆中序遍历的结果一定是从小到大有序的——因为堆也是一种"排序树"。
考点:堆与二叉搜索树(A4)。
解析:堆的中序遍历不一定有序——堆序只管父子,不管左右分支间的大小关系;"堆是排序树"是概念混淆。正确答案 B。
排除法:无(判断题)。混淆点:BST 中序有序;堆只保证"父 ≥ 子"的局部序。
关联 · 堆序不是全局有序(H1):同一误区的双题强化。
个节点的完全二叉树(堆)的高度约为?
考点:堆的高度(A5)。
解析: 节点完全二叉树高度 (约 )——这是堆操作 的依据。✅ 正确
排除法:无(判断题)。混淆点:完全二叉树形态紧凑,不会退化成链。
关联 · 上浮下沉的复杂度(B5):高度决定操作复杂度。
判断题:对同一组元素,堆的形态是唯一的。
考点:堆形态不唯一(A6)。
解析:同一组元素可有多种合法堆形态(如 可成 或 )——堆序约束比全序宽松。正确答案 B。
排除法:无(判断题)。混淆点:不同建堆方法(逐插/自底向上)常得到不同但都合法的堆。
关联 · 两种建堆对比输出(K7):形态不唯一的实证。
堆的「上浮」操作用于?
考点:上浮操作(B1)。
解析:上浮 = 插入后,新元素与父比较,违反堆序就交换、逐层上升——用于插入。✅ 正确
排除法:无(判断题)。混淆点:上浮是"新元素从底往上找到家"。
关联 · 上浮过程输出(J1):代码版。
堆的「下沉」操作用于?
考点:下沉操作(B2)。
解析:下沉 = 某节点与较大的孩子(大根堆)交换、逐层下移直到满足堆序——用于删除堆顶后的修复和建堆。✅ 正确
排除法:无(判断题)。混淆点:下沉选错孩子(选小的)会破坏堆序(H2)。
关联 · 下沉过程输出(J2):代码版。
大根堆删除堆顶(最大值)的标准步骤是?
考点:删除堆顶的步骤(B3)。
解析:删除堆顶 = 末元素移到堆顶 → 堆大小减 1 → 新堆顶下沉。✅ 正确
排除法:无(判断题)。混淆点:不能直接删堆顶留空洞——完全二叉树形态必须保持。
关联 · 删除堆顶输出(J4):代码版。
大根堆插入新元素的标准步骤是?
考点:插入的步骤(B4)。
解析:插入 = 元素放末尾 → 堆大小加 1 → 新元素上浮。✅ 正确
排除法:无(判断题)。混淆点:放末尾保证完全二叉树形态,上浮恢复堆序。
关联 · 插入过程输出(J3):代码版。
堆的上浮与下沉操作的单次时间复杂度是?
考点:上浮下沉的复杂度(B5)。
解析:上浮/下沉最多走堆高那么多层 → 。✅ 正确
排除法:无(判断题)。混淆点:不是 (可能走到根/叶)、不是 。
关联 · 堆的高度(A5):高度即操作上界。
大根堆下沉时,节点应与哪个孩子交换?
考点:下沉选哪个孩子(B6)。
解析:大根堆下沉与较大的孩子交换——换上去后仍是"父 ≥ 子",堆序得以维持。✅ 正确
排除法:无(判断题)。混淆点:选较小孩子交换 → 较大孩子成为"孙辈"却大于新父,堆序破坏。
关联 · 下沉方向(H2):错误选择的后果。
判断题:堆支持的操作中,「插入」「删除堆顶」都能在 内完成,且「取堆顶(不删除)」只需 。
考点:堆操作的完整流程(B7)。
解析:插入 、删除堆顶 、取堆顶(不删)——三种操作复杂度不同。✅ 正确
排除法:无(判断题)。混淆点:取顶不删无需维护,所以 。
关联 · 堆三种操作的复杂度(G2):总表。
逐个插入建堆:把 个元素依次插入空堆,每次上浮,总复杂度是?
考点:逐个插入建堆(C1)。
解析: 次插入、每次上浮 → 。✅ 正确
排除法:无(判断题)。混淆点:是"每次 的 次操作"。
关联 · 逐个插入建堆输出(K3):代码版。
自底向上建堆:从最后一个非叶节点开始,逐个下沉,总复杂度是?
考点:自底向上下沉建堆(C2)。
解析:自底向上(最后一个非叶节点起逐个下沉)建堆 ——大部分节点是叶子(下沉 0 层),每层节点数 × 下沉深度求和是 。✅ 正确
排除法:无(判断题)。混淆点:不是直觉的 ——关键在"多数节点不深"。
关联 · 建堆 O(n) 的直觉(C4):求和论证。
个元素(下标 1~n)自底向上建堆,应从哪个节点开始下沉?
考点:建堆的起点(C3)。
解析:从 (最后一个非叶节点)开始向前下沉到 1——叶子(下标 )天然是单节点堆,无需处理。✅ 正确
排除法:无(判断题)。混淆点:从 开始会做无意义的叶子下沉;从 1 开始只下沉根(P3 错误)。
关联 · 建堆起点输出(K2):代码版。
判断题:自底向上建堆 的直觉是——堆中大部分节点是叶子(下沉 0 层)、倒数第二层节点下沉 1 层……各层节点数 × 下沉深度求和是 级别,而非 。
考点:建堆 O(n) 的直觉(C4)。
解析:第 层(从底数)有 个节点、每个最多下沉 层 → 总下沉 。✅ 正确
排除法:无(判断题)。混淆点:求和收敛于 而非 。
关联 · 自底向上下沉建堆(C2):同结论的数学化。
逐个插入建堆与自底向上建堆的正确对比是?
考点:两种建堆对比(C5)。
解析:逐个插入 、自底向上 ——自底向上更快。✅ 正确
排除法:无(判断题)。混淆点:复杂度对比是考点;两者结果都合法但形态可能不同。
关联 · 两种建堆对比输出(K7):形态差异。
对数组 {3, 1, 2} 自底向上建大根堆(下标 1 起),建堆后的数组是?
考点:建堆过程示例(C6)。
解析::,只有节点 1 需下沉; 不动 → 结果 3 1 2。✅ 正确
排除法:无(判断题)。混淆点:建堆不排序——3 1 2 是堆但非有序。
关联 · 堆化后的数组形态(C7):建堆 ≠ 排序。
判断题:建堆完成后,数组整体是升序排列的。
考点:堆化后的数组形态(C7)。
解析:建堆后数组不是升序——堆序只保证父子关系(如 4 2 3 1 是堆但非有序)。正确答案 B。
排除法:无(判断题)。混淆点:升序需要堆排序的"反复取顶"过程。
关联 · 建堆过程示例(C6):同坑双题。
堆排序的两大步是?
考点:堆排序两步(D1)。
解析:堆排序 = **建堆 → 反复「堆顶与末尾交换 + 缩堆 + 下沉」**直到堆空。✅ 正确
排除法:无(判断题)。混淆点:第二步才是"排序",建堆只是准备。
关联 · 堆排序完整输出(L1):代码版。
堆排序每取出一个堆顶(最大值)后,如何维护剩余堆?
考点:取顶后如何维护(D2)。
解析:取顶后 = 堆顶换当前末尾、堆大小减 1、新堆顶下沉——已就位的最大值留在末尾不再参与。✅ 正确
排除法:无(判断题)。混淆点:不缩堆则已就位元素被拉回(P4)。
关联 · 缩堆填空(L4):sz-- 的纪律。
堆排序的时间复杂度是?
考点:堆排序复杂度(D3)。
解析:建堆 + 次取顶下沉 → 总 。✅ 正确
排除法:无(判断题)。混淆点:与输入无关,最好=最坏 。
关联 · 堆排序与快排归并总表(G1):对比。
判断题:堆排序是不稳定排序。
考点:堆排序不稳定(D4)。
解析:堆排序不稳定——下沉交换可能跨过相等元素。✅ 正确
排除法:无(判断题)。混淆点:不稳定性是真题高频判定点。
关联 · 稳定性总结(G5):总表。
判断题:堆排序是原地排序——除常数个变量外不需要额外数组( 额外空间)。
考点:堆排序原地(D5)。
解析:堆排序原地——只在原数组上交换, 额外空间。✅ 正确
排除法:无(判断题)。混淆点:这是堆排序相对归并的优势。
关联 · 场景选择(G6):内存紧张时的选择。
对 {4, 1, 3, 2} 建大根堆后(下标 1 起,自底向上建堆),堆顶与末尾交换、缩堆、下沉——第一轮之后数组(前 3 位为堆、末位为已就位最大值)是?
考点:堆排序过程(D6)。
解析: 建堆得 ;第一轮:4 与 1 交换 → ,缩堆后下沉 → 。✅ 正确
排除法:无(判断题)。混淆点:第一轮后最大值 4 已在末位、前 3 位仍是堆。
关联 · 取顶序列(L2):完整过程。
要把数组排成升序,堆排序应建哪种堆?
考点:升序用大根堆(D7)。
解析:升序 = 大根堆——最大值先就位到末尾,依次得到降序后缀 → 整体升序。✅ 正确
排除法:无(判断题)。混淆点:方向常记反——"升序用大根堆"(大先落尾)。
关联 · 升序降序方向(L5):代码验证。
优先队列与普通队列的区别是?
考点:优先队列的定义(E1)。
解析:普通队列 FIFO;优先队列按优先级出队(默认最高优先级先出)。✅ 正确
排除法:无(判断题)。混淆点:优先队列 ≠ 栈 ≠ 普通队列——存取规则不同。
关联 · 堆与栈队列的对比(F4):三结构规则对比。
C++ 中 priority_queue<int> 默认是?
考点:priority_queue 默认大根堆(E2)。
解析:priority_queue<int> 默认大根堆——top() 是最大值。✅ 正确
排除法:无(判断题)。混淆点:与直觉"队列先入先出"不同——它按值大小出队。
关联 · 默认大根堆输出(M1):代码版。
C++ 中声明小根堆 priority_queue 的正确写法是?
考点:小根堆的写法(E3)。
解析:小根堆 = priority_queue<int, vector<int>, greater<int>>。✅ 正确
排除法:无(判断题)。混淆点:less<int> 是默认(大根堆);greater 才是小根堆——别记反。
关联 · 小根堆 greater 输出(M2):代码版。
下列哪个场景最适合用优先队列?
考点:优先队列的典型应用(E4)。
解析:动态维护最值(合并果子、任务调度、top-k)是优先队列主场——插入/删除 、取最值 。✅ 正确
排除法:无(判断题)。混淆点:FIFO 任务处理用普通队列;函数调用用栈;括号匹配用栈。
关联 · 堆与排序的选择(E6):流式数据选堆。
判断题:手写二叉堆与 STL priority_queue 的功能等价——手写更灵活(支持删除任意元素/修改值),STL 更省事且不易写错。
考点:手写堆与 STL(E5)。
解析:手写堆 = STL 优先队列的底层实现;手写可支持删除任意元素/修改值(STL 不支持),STL 省事不易错。✅ 正确
排除法:无(判断题)。混淆点:需要"可删除任意元素"的对顶堆/任务系统时必须手写。
关联 · 手写堆与 STL 对照(M4):等价性验证。
只需「每次都取当前最大值、过程中不断插入」的场景(数据流式),选哪种结构最合适?
考点:堆与排序的选择(E6)。
解析:数据流式"不断插入 + 每次取最大" → 堆(取顶 、插入 ),全量排序每次 太贵。✅ 正确
排除法:无(判断题)。混淆点:判断标准是"是否只需要部分最值"。
关联 · 堆求 top-k(F2):只保留 k 个的典型。
「对顶堆」求动态中位数的结构是?
考点:对顶堆求中位数(F1)。
解析:对顶堆 = 大根堆存较小一半 + 小根堆存较大一半,中位数在两堆顶之间(元素奇数时在大根堆堆顶)。✅ 正确
排除法:无(判断题)。混淆点:动态插入/查询中位数 ,优于每次排序。
关联 · 对顶堆中位数(N1):代码版。
求 个数中最大的 个(),用堆的做法是?
考点:堆求 top-k(F2)。
解析:最大 个 → 维护大小 的小根堆:新元素大于堆顶(当前第 大)就替换——堆顶始终是"第 大"门槛。✅ 正确
排除法:无(判断题)。混淆点:用小根堆而非大根堆——大根堆堆顶是最大,没法淘汰。
关联 · top-k 输出(M6):代码版。
多路归并(合并 个有序序列)用小根堆的作用是?
考点:多路归并(F3)。
解析:合并 个有序序列:小根堆存各序列当前头元素,每轮 取最小 → 总 。✅ 正确
排除法:无(判断题)。混淆点: 时退化为普通归并的双指针。
关联 · 多路归并(N4):三路归并代码。
判断题:栈是 LIFO、队列是 FIFO、堆是「按优先级出」——三者存取顺序规则完全不同。
考点:堆与栈队列的对比(F4)。
解析:栈 LIFO、队列 FIFO、堆按优先级出——三种结构存取规则完全不同。✅ 正确
排除法:无(判断题)。混淆点:堆是"最值优先",与进出顺序无关。
关联 · 优先队列的定义(E1):定义对照。
构造哈夫曼树(合并果子问题)时每次取两个最小权值合并,常用结构是?
考点:堆与哈夫曼(F5)。
解析:哈夫曼(合并果子)= 每次取两个最小合并——小根堆每次 取两个最小、插回合并结果。✅ 正确
排除法:无(判断题)。混淆点:贪心策略"每次合并最小的两个"正是小根堆的用武之地。
关联 · 合并果子(N2):代码版。
判断题:数组 {50, 30, 40, 10, 20}(下标 1 起)满足大根堆的堆序。
考点:堆性质判定综合(F6)。
解析::、 ✓ 大根堆。✅ 正确
排除法:无(判断题)。混淆点:判定只查非叶节点()与孩子的堆序。
关联 · 判断堆序(I2):代码版。
下列关于堆排序、快速排序与归并排序的复杂度、空间与稳定性,说法正确的是?( )
考点:堆排/快排/归并对比(G1)。
解析:堆排序 恒成立、 空间、不稳定;快排平均 最坏 、不稳定;归并 、 空间、稳定。✅ 正确
排除法:无(判断题)。混淆点:堆排序是唯一"恒 且原地"的比较排序——但常数大、不稳定。
关联 · 堆排序实际偏慢的原因(G4):常数来源。
堆的三种操作——插入、删除堆顶、取堆顶(不删除)的复杂度分别是?
考点:堆三种操作的复杂度(G2)。
解析:插入 、删除堆顶 、取堆顶 。✅ 正确
排除法:无(判断题)。混淆点:取顶不删是 ——最值就放在根。
关联 · 堆操作的完整流程(B7):同表双题。
判断题:自底向上建堆 优于逐个插入建堆 ,因此工程中建堆都用自底向上。
考点:建堆两种方法的对比(G3)。
解析:自底向上 优于逐个插入 ——工程建堆用自底向上。✅ 正确
排除法:无(判断题)。混淆点:逐个插入的优势是"在线"(边来边建)。
关联 · 两种建堆对比(C5):同结论双题。
判断题:堆排序虽然复杂度是 ,但实际常比快排慢——因为下沉过程数据跳跃访问、缓存不友好,且常数因子较大。
考点:堆排序实际偏慢的原因(G4)。
解析:堆排序虽然 ,但下沉跳跃访问(父↔子跨大步)缓存不友好、常数大——实际常慢于快排。✅ 正确
排除法:无(判断题)。混淆点:复杂度相同 ≠ 实际一样快——常数与缓存局部性很重要。
关联 · 堆排序与快排归并总表(G1):理论 vs 实践。
判断题:堆排序不稳定,归并排序稳定,快速排序不稳定——三者中只有归并稳定。
考点:稳定性总结(G5)。
解析:堆排序不稳定、快排不稳定、归并稳定——三者中只有归并稳定。✅ 正确
排除法:无(判断题)。混淆点:稳定五虎(冒泡/插入/归并/基数/计数)vs 不稳定三雄(选择/快排/堆)。
关联 · 堆排序不稳定(D4):堆不稳定的原因。
内存极紧张(几乎无额外空间可用)、需要 排序,选哪种?
考点:场景选择(G6)。
解析:内存极紧张 + → 堆排序(原地;归并要 、计数要值域数组、冒泡 )。✅ 正确
排除法:无(判断题)。混淆点:原地 是堆排序的独门优势。
关联 · 堆排序原地(D5):原地性来源。
判断题:大根堆只保证"父 子"的局部关系,中序遍历堆并不得到有序序列。
考点:堆序不是全局有序(H1)。
解析:堆只保证"父 ≥ 子"局部关系;中序遍历堆不得到有序序列。✅ 正确
排除法:无(判断题)。混淆点:BST 中序有序,堆不是 BST。
关联 · 堆与二叉搜索树(A4):同坑双题。
大根堆下沉时,若节点与较小的孩子交换(而非较大的),后果是?
考点:下沉方向(H2)。
解析:与较小孩子交换 → 较小孩子当父、较大孩子当子 → 可能违反堆序,堆被破坏。✅ 正确
排除法:无(判断题)。混淆点:大根堆必须"较大者上浮"。
关联 · 下沉选哪个孩子(B6):正确选择。
判断题:自底向上建堆从 (根)开始向下逐层下沉也能正确建堆。
考点:建堆起点(H3)。
解析:从根 向下下沉不能正确建堆——孩子的子树可能还未成堆,下沉到半途就断了。正确答案 B。
排除法:无(判断题)。混淆点:必须自底向上(),保证处理 时其子树已是堆。
关联 · 建堆的起点(C3):正确起点。
判断题:把大根堆的上浮/下沉比较条件(> 与 <)全部取反,就得到小根堆的实现。
考点:大小根堆方向(H4)。
解析:大根堆的比较条件(> 上浮、选较大孩子下沉)全部取反(<、选较小孩子)即得小根堆实现。✅ 正确
排除法:无(判断题)。混淆点:方向取反是大小根堆切换的唯一差异。
关联 · 大小根堆方向反(P2):取反后堆排序变降序。
判断题:以下结论全部正确——"堆是完全二叉树的数组实现;建堆 ;堆排序 且不稳定;priority_queue 默认大根堆"。
考点:堆综合判断(H5)。
解析:四结论全对:堆 = 完全二叉树数组实现;建堆 ;堆排序 不稳定;priority_queue 默认大根堆。✅ 正确
排除法:无(判断题)。混淆点:本章核心结论的收官自查。
关联 · 本章全部核心结论:收官综合判断题。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int i = 3; // 堆数组下标从 1 起 05 cout << i / 2 << " " << 2 * i << " " << 2 * i + 1; 06 return 0; 07}
单选题:程序输出是?(节点 3 的父、左孩子、右孩子下标)
考点:父子下标(I1)。
解析:下标 1 起:节点 3 的父 = 、左孩子 、右孩子 。输出 1 6 7。正确答案 A。
实现要点:堆的数组三公式(1 起)——父 i/2(整除)、左 2i、右 2i+1;0 起版见 P5。手算:直接套公式。
排除法:B 把父算成 2(i-1 混淆);C/D 无依据。
关联 · 堆的数组存储(A3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int a[6] = {0, 50, 30, 40, 10, 20}; // 下标 1~5,a[0] 占位 05 bool ok = true; 06 for (int i = 1; i <= 2; i++) // 只检查非叶节点 1、2 07 for (int j = 2 * i; j <= 2 * i + 1 && j <= 5; j++) 08 if (a[i] < a[j]) ok = false; 09 cout << (ok ? "YES" : "NO"); 10 return 0; 11}
单选题:程序输出是?(该数组是否为大根堆)
考点:判断堆序(I2)。
解析:只查非叶节点 1、2:、 全满足 → YES。正确答案 A。
实现要点:堆序检查 = 对每个非叶节点()验证与孩子的大小关系。手算:列出父子对逐一查。
排除法:B 是违反堆序时的输出;C/D 无依据。
关联 · 堆性质判定综合(F6):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 10, 8, 7, 3, 5}; // 小根堆,下标 1 起 04int main() { 05 // 输出按层:根 / 左子-右子 / 左子-右子-左子-右子 06 for (int i = 1; i <= 5; i++) cout << a[i] << " "; 07 return 0; 08}
单选题:程序输出是?(按完全二叉树层序读出的堆)
考点:堆的完全二叉树形态(I3)。
解析:层序输出堆数组即按完全二叉树层序:10 8 7 3 5。正确答案 A。
实现要点:堆数组下标顺序 = 完全二叉树层序——第 层元素连续存放在一段。手算:画树标下标。
排除法:B 是升序(堆不保证);C/D 层序错乱。
关联 · 堆的数组存储(A3):层序存储。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 50, 30, 40, 10, 20}; // 大根堆 04int main() { 05 cout << a[1]; // 取堆顶(不删除) 06 return 0; 07}
单选题:程序输出是?
考点:取堆顶(I4)。
解析:大根堆 a[1] 即最大值 50。正确答案 A。
实现要点:取顶不删 = 直接读 a[1],。手算:无需操作。
排除法:B/C/D 是其他位置的值。
关联 · 取堆顶(不删除)的复杂度(G2): 的来源。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int a[6] = {0, 1, 3, 2, 5, 4}; // 下标 1~5 05 bool minH = true, maxH = true; 06 for (int i = 1; i <= 2; i++) 07 for (int j = 2 * i; j <= 2 * i + 1 && j <= 5; j++) { 08 if (a[i] > a[j]) minH = false; 09 if (a[i] < a[j]) maxH = false; 10 } 11 cout << (minH ? "MIN" : (maxH ? "MAX" : "NEITHER")); 12 return 0; 13}
单选题:程序输出是?
考点:大小根堆判定(I5)。
解析::、 全满足 → MIN(小根堆)。正确答案 A。
实现要点:大小根堆判定 = 双向检查:全"父 子"为小根堆、全"父 子"为大根堆、否则都不是。手算:逐父子对验证。
排除法:B 是大根堆情形;C 两者都不满足;D 无依据。
关联 · 判断堆序(I2):判定代码的扩展。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int n = 7; 05 int h = 0; 06 while (n) { n /= 2; h++; } // 每次去掉一层 07 cout << h; 08 return 0; 09}
单选题:程序输出是?(7 个节点的完全二叉树的高度,即根到最深叶的层数)
考点:堆高与节点数(I6)。
解析::,共 3 次 → 高度 3(7 节点满二叉树三层)。正确答案 A。
实现要点:堆高 = 反复 n /= 2 直到 0 的次数(等于 )。手算:画满二叉树。
排除法:B 是 3 节点的高度;C/D 无依据。
关联 · 堆的高度(A5):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[7] = {0, 9, 7, 8, 3, 1}; // 大根堆,下标 1~5 04int sz = 5; 05void up(int i) { 06 while (i > 1 && a[i] > a[i / 2]) { 07 swap(a[i], a[i / 2]); 08 i /= 2; 09 } 10} 11int main() { 12 a[++sz] = 10; // 插入 10 13 up(sz); 14 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 15 return 0; 16}
单选题:程序输出是?(插入 10 并上浮后的堆)
考点:上浮过程输出(J1)。
解析:插 10: 交换 → ; 交换 → ;到根停。正确答案 A。
实现要点:上浮循环 = while (i > 1 && a[i] > a[i/2]) swap + i /= 2。手算:沿父链逐层比较。
排除法:B 是插入未上浮;C/D 交换路径错。
关联 · 上浮操作(B1):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 3, 7, 8, 9, 5}; // 下标 1~5,仅堆顶 3 破坏堆序 04int sz = 5; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; // 左孩子 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; // 取较大孩子 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 down(1); 16 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 17 return 0; 18}
单选题:程序输出是?(堆顶下沉修复后的堆)
考点:下沉过程输出(J2)。
解析:3 下沉:孩子 7、8 取较大 8 → 交换 → ;3 无孩子停。正确答案 A。
实现要点:下沉循环 = 找较大孩子 j、不满足堆序就交换、i = j 继续。手算:沿较大孩子路径下移。
排除法:B 继续下沉了;C 是初始状态;D 是排序结果。
关联 · 下沉操作(B2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[7] = {0, 10, 5, 8, 3, 1}; // 大根堆 04int sz = 5; 05void up(int i) { 06 while (i > 1 && a[i] > a[i / 2]) { swap(a[i], a[i / 2]); i /= 2; } 07} 08int main() { 09 a[++sz] = 9; // 插入 9 10 up(sz); 11 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 12 return 0; 13}
单选题:程序输出是?
考点:插入过程输出(J3)。
解析:插 9: 交换 → ; 停。正确答案 A。
实现要点:插入 = 末尾放数 + 上浮;上浮到"父更大(大根堆)"即停。手算:找插入位置的父链。
排除法:B 未上浮;C/D 路径错。
关联 · 插入的步骤(B4):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 10, 7, 9, 3, 1}; // 大根堆 04int sz = 5; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 a[1] = a[sz--]; // 末元素移到堆顶,删除堆顶 16 down(1); 17 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 18 return 0; 19}
单选题:程序输出是?(删除堆顶 10 后的堆)
考点:删除堆顶输出(J4)。
解析:末元素 1 移到堆顶 → ;下沉:与较大孩子 9 交换 → ;1 无孩子停。正确答案 A。
实现要点:删除堆顶 = a[1] = a[sz--] + 下沉——末元素"顶替"保证完全二叉树形态。手算:模拟三步骤。
排除法:B 是 1 又下沉了一次;C/D 无依据。
关联 · 删除堆顶的步骤(B3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[7] = {0, 9, 7, 8, 3, 1, 10}; // 刚插入 10(末尾) 04int sz = 6; 05void up(int i) { 06 while (______) { // 未到根且大于父 07 swap(a[i], a[i / 2]); 08 i /= 2; 09 } 10} 11int main() { up(6); cout << a[1]; return 0; }
单选题:横线处应填入?(使输出为 10——10 上浮到堆顶)
考点:上浮循环条件填空(J5)。
解析:上浮条件 = i > 1 && a[i] > a[i / 2]——未到根且大于父。10 浮到堆顶。正确答案 A。
实现要点:两条件缺一不可:i > 1 防越界(根无父)、a[i] > a[i/2] 是堆序判定。手算:验证边界 i=1。
排除法:B 用 sz 限制(循环无法上到根以上——语义错);C 方向反;D 无依据。
关联 · 上浮循环(J1):条件填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 3, 7, 8, 9, 5}; // 大根堆待修复 04int sz = 5; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (______) j++; // 右孩子更大时选右孩子 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { down(1); cout << a[1]; return 0; }
单选题:横线处应填入?(使输出为 8——堆顶 3 与较大的孩子 8 交换)
考点:下沉选孩子填空(J6)。
解析:取较大孩子 = j + 1 <= sz && a[j + 1] > a[j] 时 j++——边界 + 比较两条件。正确答案 A。
实现要点:j + 1 <= sz 防右孩子不存在时越界(P1 的坑);比较取大者。手算:节点只有一个孩子时的边界。
排除法:B 取较小孩子(方向反);C 漏边界(越界风险);D 恒加 1 错。
关联 · 下沉选哪个孩子(B6):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; // 下标 1~4 04int sz = 4; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i); // 自底向上建堆 16 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 17 return 0; 18}
单选题:程序输出是?(自底向上建出的大根堆)
考点:自底向上建堆输出(K1)。
解析:: 下沉(1 与孩子 2 交换)→ ; 下沉(4 最大不动)。正确答案 A。
实现要点:自底向上 = for (i = n/2; i >= 1; i--) down(i)——叶子天然成堆,只需下沉非叶。手算:从最后一个非叶倒序处理。
排除法:B 是排序结果;C/D 无依据。
关联 · 自底向上下沉建堆(C2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int n = 5; 05 cout << n / 2; // 自底向上建堆的起始下标 06 return 0; 07}
单选题:程序输出是?(5 个元素自底向上建堆,第一个下沉的节点下标)
考点:建堆起点输出(K2)。
解析:,——从下标 2(最后一个非叶节点)开始。正确答案 A。
实现要点:最后一个非叶节点 = n/2(其孩子 )。手算:找最后一个有孩子的下标。
排除法:B 是叶子;C 是根;D 无依据。
关联 · 建堆的起点(C3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0}; // 下标 1 起 04int sz = 0; 05void up(int i) { 06 while (i > 1 && a[i] > a[i / 2]) { swap(a[i], a[i / 2]); i /= 2; } 07} 08int main() { 09 int d[4] = {4, 1, 3, 2}; 10 for (int k = 0; k < 4; k++) { a[++sz] = d[k]; up(sz); } // 逐个插入 11 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 12 return 0; 13}
单选题:程序输出是?(逐个插入建出的大根堆)
考点:逐个插入建堆输出(K3)。
解析:插 4、1、3、2:插 2 时 交换 → 。正确答案 A。
实现要点:逐个插入 = 每次末尾插 + 上浮;结果与自底向上可能不同但都合法(K7)。手算:逐步插逐步浮。
排除法:B/C/D 无依据。
关联 · 逐个插入建堆(C1):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4, cnt = 0; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); cnt++; 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i); 16 cout << cnt; 17 return 0; 18}
单选题:程序输出是?(对 {4, 1, 3, 2} 自底向上建堆的交换次数)
考点:建堆交换次数(K4)。
解析::仅 下沉交换 1 次(1↔2), 不动 → 共 1 次。正确答案 A。
实现要点:交换次数 = 各非叶节点下沉交换数之和。手算:逐节点记录。
排除法:B/C/D 无依据。
关联 · 自底向上建堆输出(K1):计数版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = ______; i >= 1; i--) down(i); // 自底向上建堆 16 cout << a[1]; 17 return 0; 18}
单选题:横线处应填入?(使输出为 4——建成大根堆后堆顶)
考点:建堆起点填空(K5)。
解析:sz / 2——从最后一个非叶节点开始向前下沉。正确答案 A。
实现要点:起点 = sz/2;sz(叶子)无意义、1(只沉根)不完整。手算:验证 n=4 时 sz/2=2。
排除法:B 从叶子起;C 从根起(P3);D 无依据。
关联 · 建堆起点输出(K2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (______) break; // 堆序已满足 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i); 16 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 17 return 0; 18}
单选题:横线处应填入?(使输出为 4 2 3 1)
考点:建堆下沉填空(K6)。
解析:堆序已满足即停:a[i] >= a[j]。正确答案 A。
实现要点:下沉的停止条件 = 当前节点不小于较大孩子(大根堆)。手算:验证已满足时 break。
排除法:B 方向反(会破坏堆序);C/D 无依据。
关联 · 下沉选孩子填空(J6):下沉两填空。
01#include <bits/stdc++.h> 02using namespace std; 03// 建堆 A:逐个插入(up);建堆 B:自底向上(down) 04// 对输入 {1, 5, 2, 3, 4}:A 得到 {5, 4, 2, 1, 3},B 得到 {5, 4, 2, 3, 1} 05int main() { 06 int x[6] = {0, 5, 4, 2, 1, 3}; // 建堆 A 的结果 07 int y[6] = {0, 5, 4, 2, 3, 1}; // 建堆 B 的结果 08 cout << x[5] << " " << y[5]; 09 return 0; 10}
判断题:对同一组输入,逐个插入建堆与自底向上建堆得到的堆不一定相同——但两者都是合法的大根堆(堆形态不唯一)。
考点:两种建堆对比输出(K7)。
解析::逐个插入得 、自底向上得 ——形态不同但都合法。✅ 正确
实现要点:堆形态不唯一(A6)的实证——两种建堆都是正确的堆,只是同层元素摆放不同。手算:两种方法各建一遍对照。
排除法:无(判断题)。混淆点:别把"结果不同"误读成"有错"。
关联 · 堆形态不唯一(A6):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; // 下标 1 起 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i, sz); // 建堆 16 for (int n = sz; n > 1; n--) { // 反复取顶 17 swap(a[1], a[n]); 18 down(1, n - 1); 19 } 20 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 21 return 0; 22}
单选题:程序输出是?
考点:堆排序完整输出(L1)。
解析:建堆 + 反复取顶 → 最终升序 1 2 3 4。正确答案 A。
实现要点:堆排序 = 建堆 O(n) + n-1 轮「交换 + 缩堆 + 下沉」。手算技巧:验证框架即可,不必全模拟。
排除法:B 是降序(小根堆方向);C 是原数组;D 是建堆结果。
关联 · 堆排序两步(D1):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 2, 3, 1}; // 已是大根堆 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int n = sz; n >= 1; n--) { 16 cout << a[1] << " "; // 输出当前堆顶 17 swap(a[1], a[n]); 18 down(1, n - 1); 19 } 20 return 0; 21}
单选题:程序输出是?(每次输出的堆顶序列,即从大到小)
考点:取顶序列(L2)。
解析:堆顶依次 4、3、2、1 → 4 3 2 1(从大到小)。正确答案 A。
实现要点:取顶序列 = 大根堆每次弹出最大值——"先大后小"。手算:每轮看堆顶。
排除法:B 是升序;C/D 无依据。
关联 · 堆排序过程(D6):取顶即就位。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 2, 3, 1}; // 大根堆 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 ______; // 堆顶(最大值)与当前堆末尾交换 16 cout << a[4]; 17 return 0; 18}
单选题:横线处应填入?(使输出为 4——最大值已移到末位)
考点:堆顶换末填空(L3)。
解析:swap(a[1], a[sz])——最大值移到当前堆末尾。正确答案 A。
实现要点:堆排序每轮第一步 = 堆顶与当前末尾交换(最大值就位)。手算:验证 a[4] 变 4。
排除法:B 换错位置;C 换的是末两位;D 赋值丢数据。
关联 · 取顶后如何维护(D2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 2, 3, 1}; 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 swap(a[1], a[sz]); 16 ______; // 堆缩小 17 down(1, sz); 18 cout << a[1] << " " << a[2] << " " << a[3]; 19 return 0; 20}
单选题:横线处应填入?(使输出为 3 2 1——缩小并下沉后的堆)
考点:缩堆填空(L4)。
解析:sz--——堆缩小 1,已就位的最大值排除在外。正确答案 A。
实现要点:缩堆 = sz--(或下沉时传 n-1)——不缩堆则已就位元素被拉回(P4)。手算:验证缩后堆为 {3,2,1}。
排除法:B 扩大堆;C/D 无依据。
关联 · 忘缩堆(P4):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i, sz); 16 for (int n = sz; n > 1; n--) { swap(a[1], a[n]); down(1, n - 1); } 17 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 18 return 0; 19}
判断题:该程序建大根堆做堆排序,输出是升序 1 2 3 4——升序排序要用大根堆,因为最大值先就位到末尾。
考点:升序降序方向(L5)。
解析:大根堆 → 最大值先就位末尾 → 升序 1 2 3 4。✅ 正确
实现要点:升序用大根堆、降序用小根堆——"大先落尾则升"。手算:追踪最大值走向。
排除法:无(判断题)。混淆点:方向是高频记反点。
关联 · 升序用大根堆(D7):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4, cnt = 0; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); cnt++; 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i, sz); 16 for (int n = sz; n > 1; n--) { swap(a[1], a[n]); cnt++; down(1, n - 1); } 17 cout << cnt; 18 return 0; 19}
单选题:程序输出是?(建堆 + 堆排序全程的交换次数)
考点:堆排序交换次数(L6)。
解析:建堆 1 次;排序 3 轮:swap 3 次 + 下沉交换 2 次(第一轮 1↔3、第二轮 1↔2)→ 共 6 次。正确答案 D。
实现要点:计数 = 建堆下沉交换 + 每轮「换顶」+「下沉交换」。手算:逐轮记录三种交换。
排除法:A/B/C 漏计。
关联 · 堆排序完整输出(L1):计数版。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 priority_queue<int> q; // 默认大根堆 05 int d[5] = {3, 1, 4, 1, 5}; 06 for (int i = 0; i < 5; i++) q.push(d[i]); 07 while (!q.empty()) { cout << q.top() << " "; q.pop(); } 08 return 0; 09}
单选题:程序输出是?
考点:默认大根堆输出(M1)。
解析:priority_queue<int> 默认大根堆 → 依次弹出最大值 → 5 4 3 1 1。正确答案 A。
实现要点:默认大根堆 = 弹出序列降序;top() 恒为当前最大。手算:每轮看队首。
排除法:B 是小根堆输出;C 是入队序;D 无依据。
关联 · priority_queue 默认大根堆(E2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 priority_queue<int, vector<int>, greater<int>> q; // 小根堆 05 int d[5] = {3, 1, 4, 1, 5}; 06 for (int i = 0; i < 5; i++) q.push(d[i]); 07 while (!q.empty()) { cout << q.top() << " "; q.pop(); } 08 return 0; 09}
单选题:程序输出是?
考点:小根堆 greater 输出(M2)。
解析:greater<int> 小根堆 → 升序弹出 1 1 3 4 5。正确答案 A。
实现要点:小根堆三件套 = priority_queue<int, vector<int>, greater<int>>。手算:每轮弹最小。
排除法:B 是大根堆输出;C/D 无依据。
关联 · 小根堆的写法(E3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 priority_queue<int> q; 05 q.push(3); q.push(1); q.push(4); 06 cout << q.top() << " "; q.pop(); // 取走当前最大 07 q.push(2); 08 cout << q.top() << " "; q.pop(); 09 cout << q.top() << " "; q.pop(); 10 cout << q.top() << " "; q.pop(); 11 return 0; 12}
单选题:程序输出是?
考点:动态取最值(M3)。
解析:插 3,1,4 → 取 4;插 2 → 取 3、2、1 → 4 3 2 1。正确答案 A。
实现要点:动态场景 = 插入与取最大交替——堆 维护、 取顶。手算:跟踪堆内容变化。
排除法:B 是静态排序;C/D 无依据。
关联 · 堆与排序的选择(E6):流式取最值的实证。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int h[5] = {0}; // 手写小根堆,下标 1 起 05 int sz = 0; 06 auto up = [&](int i) { while (i > 1 && h[i] < h[i / 2]) { swap(h[i], h[i / 2]); i /= 2; } }; 07 int d[4] = {3, 1, 4, 2}; 08 for (int k = 0; k < 4; k++) { h[++sz] = d[k]; up(sz); } 09 cout << h[1] << " "; // 手写堆顶(最小) 10 priority_queue<int, vector<int>, greater<int>> q; 11 for (int k = 0; k < 4; k++) q.push(d[k]); 12 cout << q.top(); // STL 堆顶(最小) 13 return 0; 14}
单选题:程序输出是?
考点:手写堆与 STL 对照(M4)。
解析:手写小根堆堆顶 = 1、STL 小根堆堆顶 = 1 → 1 1。正确答案 A。
实现要点:手写堆与 STL 等价——手写用数组 + up/down、STL 用 priority_queue。手算:两者同操作同结果。
排除法:B/C/D 无依据。
关联 · 手写堆与 STL(E5):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03struct cmp { // 自定义比较:a 优先于 b 当 a > b 04 bool operator()(int a, int b) { return a > b; } 05}; 06int main() { 07 priority_queue<int, vector<int>, cmp> q; // 小根堆 08 int d[3] = {3, 1, 2}; 09 for (int i = 0; i < 3; i++) q.push(d[i]); 10 while (!q.empty()) { cout << q.top() << " "; q.pop(); } 11 return 0; 12}
单选题:程序输出是?
考点:自定义比较器(M5)。
解析:cmp 返回 a > b → 小根堆 → 弹 1 2 3。正确答案 A。
实现要点:自定义比较器 operator()(a, b) 返回 true 表示"a 排 b 前";a > b 让小的优先。手算:验证队首恒最小。
排除法:B 是默认大根堆;C 是入队序;D 无依据。
关联 · 比较器填空(O7):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int d[8] = {3, 1, 4, 1, 5, 9, 2, 6}; 05 int k = 3; 06 priority_queue<int, vector<int>, greater<int>> q; // 大小为 k 的小根堆 07 for (int i = 0; i < 8; i++) { 08 if ((int)q.size() < k) q.push(d[i]); 09 else if (d[i] > q.top()) { q.pop(); q.push(d[i]); } 10 } 11 // 堆内是最大的 k 个(升序输出) 12 int out[3], t = 0; 13 while (!q.empty()) { out[t++] = q.top(); q.pop(); } 14 for (int i = t - 1; i >= 0; i--) cout << out[i] << " "; 15 return 0; 16}
单选题:程序输出是?(8 个数中最大的 3 个,降序)
考点:top-k 输出(M6)。
解析:k=3 小根堆维护"当前最大的 3 个":最终堆内 → 降序 9 6 5。正确答案 A。
实现要点:top-k = 小根堆当"门槛"——新元素大于堆顶(第 k 大)才替换。手算:逐元素判断进堆/跳过。
排除法:B 是堆内升序;C/D 门槛判断错。
关联 · 堆求 top-k(F2):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 // 对顶堆:maxh 存较小一半(大根堆),minh 存较大一半(小根堆) 05 priority_queue<int> maxh; 06 priority_queue<int, vector<int>, greater<int>> minh; 07 int d[5] = {5, 2, 8, 1, 9}; 08 for (int i = 0; i < 5; i++) { 09 int x = d[i]; 10 if (maxh.empty() || x <= maxh.top()) maxh.push(x); 11 else minh.push(x); 12 // 平衡:maxh 大小至多比 minh 大 1 13 if ((int)maxh.size() > (int)minh.size() + 1) { minh.push(maxh.top()); maxh.pop(); } 14 if ((int)minh.size() > (int)maxh.size()) { maxh.push(minh.top()); minh.pop(); } 15 } 16 cout << maxh.top(); // 中位数(maxh 存较小一半,堆顶为其中最大) 17 return 0; 18}
单选题:程序输出是?(序列 {5, 2, 8, 1, 9} 的中位数)
考点:对顶堆中位数(N1)。
解析:动态插入 5,2,8,1,9 并平衡后,maxh 堆顶 = 5(中位数)。正确答案 A。
实现要点:对顶堆 = 大根堆存小半 + 小根堆存大半;插入按大小分流、每次平衡两堆大小差 ≤ 1。手算:逐元素分流并平衡。
排除法:B/C/D 无依据。
关联 · 对顶堆求中位数(F1):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 priority_queue<int, vector<int>, greater<int>> q; // 小根堆 05 int d[3] = {1, 2, 9}; 06 for (int i = 0; i < 3; i++) q.push(d[i]); 07 int cost = 0; 08 while (q.size() > 1) { 09 int a = q.top(); q.pop(); 10 int b = q.top(); q.pop(); 11 cost += a + b; // 合并代价 12 q.push(a + b); // 合并结果放回 13 } 14 cout << cost; 15 return 0; 16}
单选题:程序输出是?(合并果子最小总代价:先 1+2=3,再 3+9=12)
考点:合并果子(N2)。
解析::1+2=3(代价 3)→ 3+9=12 → 总代价 15。正确答案 A。
实现要点:合并果子 = 小根堆每次取两个最小、累加并放回——贪心保证总代价最小。手算:最小两个先合并。
排除法:B 只算了第二次;C 是先 2+9;D 无依据。
关联 · 堆与哈夫曼(F5):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 // 排队接水:n 个人用时 a[i],按用时从小到大排队,求总等待时间 05 priority_queue<int, vector<int>, greater<int>> q; 06 int d[3] = {3, 1, 2}; 07 for (int i = 0; i < 3; i++) q.push(d[i]); 08 long long wait = 0, cur = 0; 09 while (!q.empty()) { 10 cur += q.top(); q.pop(); // cur = 该人完成时刻 11 wait += cur; // 累计所有人的完成时刻之和 12 } 13 cout << wait; 14 return 0; 15}
单选题:程序输出是?(总等待 = 完成时刻 1 + 3 + 6)
考点:任务调度(N3)。
解析:短任务优先:完成时刻 1、3、6 → 总和 10。正确答案 A。
实现要点:排队接水/任务调度 = 小根堆按用时升序出队,累计完成时刻。手算:排序后逐个累加。
排除法:B 只算最后完成时刻;C/D 无依据。
关联 · 优先队列的典型应用(E4):应用代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 // 三路归并:三个有序序列的头元素入堆,每次取最小 05 int s[3][3] = {{1, 4, 7}, {2, 5, 8}, {3, 6, 9}}; 06 int p[3] = {0, 0, 0}; 07 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; 08 for (int i = 0; i < 3; i++) q.push({s[i][0], i}); 09 while (!q.empty()) { 10 auto cur = q.top(); q.pop(); 11 cout << cur.first << " "; 12 int id = cur.second; 13 if (++p[id] < 3) q.push({s[id][p[id]], id}); 14 } 15 return 0; 16}
单选题:程序输出是?
考点:多路归并(N4)。
解析:三路归并每次取三个头元素最小 → 1 2 3 4 5 6 7 8 9。正确答案 A。
实现要点:多路归并 = 小根堆存(值, 序列号),弹最小后把该序列下一个元素入堆。手算:追踪三路指针。
排除法:B 是按序列拼接;C 是降序;D 无依据。
关联 · 多路归并(F3):概念题代码化。
01#include <bits/stdc++.h> 02using namespace std; 03int a[7] = {0, 9, 7, 8, 3, 1}; // 大根堆 04int sz = 5; 05void up(int i) { while (i > 1 && a[i] > a[i / 2]) { swap(a[i], a[i / 2]); i /= 2; } } 06void down(int i) { 07 while (2 * i <= sz) { 08 int j = 2 * i; 09 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 10 if (a[i] >= a[j]) break; 11 swap(a[i], a[j]); 12 i = j; 13 } 14} 15int main() { 16 a[++sz] = 12; up(sz); // 插入 12 17 a[1] = a[sz--]; down(1); // 删除堆顶(12) 18 a[++sz] = 5; up(sz); // 插入 5 19 cout << a[1] << " " << a[2] << " " << a[3]; 20 return 0; 21}
单选题:程序输出是?
考点:上浮下沉混合(N5)。
解析:插 12 浮到顶 → 删顶(8 顶替下沉)→ 插 5 → 堆顶 9、孩子 7、8 → 输出 9 7 8。正确答案 A。
实现要点:混合操作 = 插入用 up、删除用 down,两者交替堆仍保持。手算:逐步画堆。
排除法:B 是 7/8 交换后;C 是删顶前;D 无依据。
关联 · 上浮下沉(B1/B2):综合应用。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0}; // 大根堆,下标 1 起 04int sz = 0; 05void up(int i) { while (i > 1 && a[i] > a[i / 2]) { swap(a[i], a[i / 2]); i /= 2; } } 06int main() { 07 int op[5] = {2, 7, 1, 3, 5}; // 依次插入 08 for (int k = 0; k < 5; k++) { a[++sz] = op[k]; up(sz); } 09 cout << a[1] << " " << a[2] << " " << a[3] << " " << a[4] << " " << a[5]; 10 return 0; 11}
单选题:程序输出是?(依次插入 2、7、1、3、5 后的堆)
考点:数组模拟堆操作(N6)。
解析:依次插 2、7、1、3、5:7 浮到顶;3 与 2 交换;5 与 3 交换 → 7 5 1 2 3。正确答案 A。
实现要点:数组模拟 = 每次插入后上浮,堆顶恒最大。手算:逐个插入并上浮。
排除法:B 是排序结果;C/D 无依据。
关联 · 插入的步骤(B4):连续插入。
01#include <bits/stdc++.h> 02using namespace std; 03int a[7] = {0, 9, 7, 8, 3, 1, 10}; // 刚插入 10 04int sz = 6; 05void up(int i) { 06 while (i > 1 && a[i] > a[i / 2]) { 07 ______; // 与父节点交换 08 i /= 2; 09 } 10} 11int main() { up(6); for (int i = 1; i <= sz; i++) cout << a[i] << " "; return 0; }
单选题:横线处应填入?(使输出为 10 7 9 3 1 8)
考点:上浮填空(O1)。
解析:与父交换:swap(a[i], a[i / 2])。正确答案 A。
实现要点:上浮体 = 交换 + i /= 2 上移一层。手算:验证 10 的两次交换。
排除法:B 交换对象错;C 赋值丢数据;D 与孩子交换(下沉逻辑)。
关联 · 上浮操作(B1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 3, 7, 8, 9, 5}; 04int sz = 5; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 ______; // 继续向下 12 } 13} 14int main() { down(1); for (int i = 1; i <= sz; i++) cout << a[i] << " "; return 0; }
单选题:横线处应填入?(使输出为 8 7 3 9 5)
考点:下沉填空(O2)。
解析:继续向下:i = j。正确答案 A。
实现要点:下沉循环尾部 i = j——以新位置继续检查孩子。手算:验证 3→8→停的路径。
排除法:B 跳过中间层;C 越界方向;D 提前退出。
关联 · 下沉操作(B2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = ______; i >= 1; i--) down(i); // 自底向上建堆 16 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 17 return 0; 18}
单选题:横线处应填入?(使输出为 4 2 3 1)
考点:建堆填空(O3)。
解析:自底向上起点:sz / 2。正确答案 A。
实现要点:for (i = sz/2; i >= 1; i--) down(i) 是自底向上建堆的标准骨架。手算:验证起点。
排除法:B/C/D 无依据。
关联 · 建堆的起点(C3):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 2, 3, 1}; 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int n = sz; n > 1; n--) { 16 swap(a[1], a[n]); 17 ______; // 对缩小后的堆做下沉 18 } 19 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 20 return 0; 21}
单选题:横线处应填入?(使输出为 1 2 3 4)
考点:堆排序填空(O4)。
解析:换顶后对缩小堆下沉:down(1, n - 1)。正确答案 A。
实现要点:n-1 把已就位的末尾排除——缩堆的第二种写法(等价于 sz--)。手算:验证堆区域。
排除法:B 把已就位元素卷回;C/D 参数错。
关联 · 缩堆填空(L4):参数版。
01#include <bits/stdc++.h> 02using namespace std; 03priority_queue<int> maxh; // 较小一半 04priority_queue<int, vector<int>, greater<int>> minh; // 较大一半 05void add(int x) { 06 if (maxh.empty() || x <= maxh.top()) maxh.push(x); 07 else minh.push(x); 08 if ((int)maxh.size() > (int)minh.size() + 1) { minh.push(maxh.top()); maxh.pop(); } 09 if (______) { maxh.push(minh.top()); minh.pop(); } // 平衡:较小一半不可少于较大一半 10} 11int main() { 12 int d[5] = {5, 2, 8, 1, 9}; 13 for (int i = 0; i < 5; i++) add(d[i]); 14 cout << maxh.top(); 15 return 0; 16}
单选题:横线处应填入?(使输出为 5——动态中位数)
考点:对顶堆填空(O5)。
解析:平衡条件:minh.size() > maxh.size() 时把 minh 顶移入 maxh(较小一半不可少)。正确答案 A。
实现要点:对顶堆平衡 = 两堆大小差 ≤ 1,且大根堆(小半)允许多 1。手算:验证奇数个元素时中位数在 maxh 顶。
排除法:B 方向反;C 只在相等时搬;D 无依据。
关联 · 对顶堆中位数(N1):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 priority_queue<int, vector<int>, greater<int>> q; 05 int d[3] = {1, 2, 9}; 06 for (int i = 0; i < 3; i++) q.push(d[i]); 07 int cost = 0; 08 while (q.size() > 1) { 09 int a = q.top(); q.pop(); 10 int b = q.top(); q.pop(); 11 ______; // 累计合并代价并放回 12 q.push(a + b); 13 } 14 cout << cost; 15 return 0; 16}
单选题:横线处应填入?(使输出为 15)
考点:合并果子填空(O6)。
解析:累计合并代价:cost += a + b。正确答案 A。
实现要点:每次合并的代价 = 两堆大小之和,总代价逐次累加。手算:验证 3+12=15。
排除法:B 覆盖式赋值(丢历史);C 漏 b;D 无依据。
关联 · 合并果子(N2):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03struct cmp { 04 bool operator()(int a, int b) { return ______; } // 队首为最小 05}; 06int main() { 07 priority_queue<int, vector<int>, cmp> q; 08 q.push(3); q.push(1); q.push(2); 09 cout << q.top(); 10 return 0; 11}
单选题:横线处应填入?(使输出为 1)
考点:比较器填空(O7)。
解析:队首最小 → 比较器返回 a > b。正确答案 A。
实现要点:operator()(a,b) 返回 true 表示 a 优先级更高(先出);a > b 使小值优先。手算:验证 top() = 1。
排除法:B 是大根堆;C 全 false 不排序;D 无依据。
关联 · 自定义比较器(M5):填空版。
01#include <bits/stdc++.h> 02using namespace std; 03int a[6] = {0, 3, 7, 8, 9, 5}; 04int sz = 5; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (______) j++; // 错误:漏了 j + 1 <= sz 的边界判断 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { down(1); cout << "run"; return 0; }
单选题:横线处应填入?(使程序在节点无右孩子时可能访问越界——这是下沉的经典 bug)
考点:下沉越界(P1)。
解析:a[j + 1] > a[j] 无 j + 1 <= sz 边界——节点无右孩子时 a[j+1] 越界。正确答案 A。
实现要点:下沉的右孩子访问必须带边界 j + 1 <= sz——完全二叉树可能只有左孩子。手算:找只有左孩子的节点。
排除法:B 是正确写法;C/D 无依据。
关联 · 下沉选孩子填空(J6):边界的重要性。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] < a[j]) j++; // 错误:取较小孩子 → 小根堆方向 09 if (a[i] <= a[j]) break; // 错误:比较方向反 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = sz / 2; i >= 1; i--) down(i, sz); 16 for (int n = sz; n > 1; n--) { swap(a[1], a[n]); down(1, n - 1); } 17 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 18 return 0; 19}
单选题:程序输出是?(比较方向全反 → 小根堆堆排序)
考点:大小根堆方向反(P2)。
解析:比较方向全反 → 小根堆堆排序 → 降序 4 3 2 1。正确答案 A。
实现要点:方向取反(选较小孩子、<= 停止)整段变成小根堆逻辑——堆排序输出随之反转。手算:追踪最小值的走向。
排除法:B 是正确方向;C 是原数组;D 无依据。
关联 · 大小根堆方向(H4):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 1, 3, 2}; 04int sz = 4; 05void down(int i) { 06 while (2 * i <= sz) { 07 int j = 2 * i; 08 if (j + 1 <= sz && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int i = 1; i >= 1; i--) down(i); // 错误:只从根下沉,叶子层未处理 16 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 17 return 0; 18}
单选题:程序输出是?({4, 1, 3, 2} 中根 4 已是最大、一次下沉都没发生,结果不满足堆序)
考点:建堆起点错(P3)。
解析:只从根下沉一次:4 已最大不动 → 输出 4 1 3 2,节点 2 的孩子 2 > 1 堆序破坏。正确答案 A。
实现要点:只处理根 = 子树可能未成堆,下沉半途而废——必须自底向上(n/2 → 1)。手算:检查叶子层是否满足堆序。
排除法:B 是正确建堆结果;C 无依据;D 语法合法。
关联 · 建堆起点(H3):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int a[5] = {0, 4, 2, 3, 1}; 04int sz = 4; 05void down(int i, int n) { 06 while (2 * i <= n) { 07 int j = 2 * i; 08 if (j + 1 <= n && a[j + 1] > a[j]) j++; 09 if (a[i] >= a[j]) break; 10 swap(a[i], a[j]); 11 i = j; 12 } 13} 14int main() { 15 for (int n = sz; n > 1; n--) { 16 swap(a[1], a[n]); 17 down(1, n); // 错误:n 未减 1,已就位的元素被拉回堆中 18 } 19 for (int i = 1; i <= sz; i++) cout << a[i] << " "; 20 return 0; 21}
单选题:程序输出是?
考点:忘缩堆(P4)。
解析:down(1, n) 用 n 而非 n-1——已就位的 4 被卷回堆中 → 最终 3 2 1 4(错误)。正确答案 A。
实现要点:每轮必须缩堆(n-1 或 sz--),已就位元素绝不能再参与下沉。手算:追踪 4 的位置变化。
排除法:B 是正确结果;C 是降序;D 无依据。
关联 · 缩堆填空(L4):错误示范。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 // 下标从 0 起的堆:节点 i 的孩子与父 05 int i = 2; 06 cout << (i - 1) / 2 << " " << 2 * i + 1 << " " << 2 * i + 2; 07 return 0; 08}
单选题:程序输出是?(下标 0 起时,节点 2 的父、左孩子、右孩子下标)
考点:下标 0 起与 1 起(P5)。
解析:0 起:节点 2 的父 、左 、右 。正确答案 A。
实现要点:0 起三公式 = 父 (i-1)/2、左 2i+1、右 2i+2——与 1 起整体平移 1。手算:套公式。
排除法:B 混用 1 起的父公式;C/D 无依据。
关联 · 堆的数组存储(A3):两种下标的对照。
判断题:以下结论全部正确——"堆排序原地且 但不稳定;priority_queue 默认大根堆;自底向上建堆 ;对顶堆能动态维护中位数"。
考点:综合判断(P6)。
解析:四结论全对:堆排序原地 不稳定;priority_queue 默认大根堆;自底向上建堆 ;对顶堆动态维护中位数。✅ 正确
实现要点:本章四大核心结论的收官自查。手算:逐条对照本章代码。
排除法:无(判断题)。混淆点:堆排序"原地"指 额外空间,与快排的原地同义。
关联 · 本章全部核心结论:收官综合判断题。