能,但需注意数组修改、奇偶处理和边界检查;nth_element仅将第k小元素置位并划分左右,奇数时直接取索引n/2,偶数时需额外获取左半段最大值以计算平均中位数。

nth_element 能直接得到中位数吗?
能,但得小心用。它不排序整个数组,而是把第 k 小的元素“拎”到位置 k 上,左边全 ≤ 它,右边全 ≥ 它——这刚好够算中位数,不用全排,时间复杂度从 O(n log n) 降到平均 O(n)。
注意:它会修改原数组;且中位数定义依赖数组长度奇偶性:
- 奇数长度:中位数是索引
n/2(0-based)处的元素 - 偶数长度:严格中位数是中间两数的平均值,
nth_element只能帮你快速拿到这两个位置,不能一步到位
偶数长度时怎么安全取两个中间值?
不能只调一次 nth_element 就认为 [n/2-1] 和 [n/2] 都已就位——它只保证第 k 位正确,其余位置无序。稳妥做法是两次调用或一次调用后局部整理:
- 先用
nth_element(v.begin(), v.begin() + n/2, v.end())把第n/2小的放到索引n/2 - 再对左半部分(
v.begin()到v.begin() + n/2)调用nth_element,找其中最大值(即第n/2 - 1小),放在v[n/2 - 1] - 或者更简单:调一次
nth_element定位n/2,再用max_element扫左半段得前中位数
示例(偶数情况):
立即学习“C++免费学习笔记(深入)”;
vector<int> v = {3, 1, 4, 1, 5, 9};
int n = v.size();
nth_element(v.begin(), v.begin() + n/2, v.end()); // 确保 v[n/2] 是第 n/2 小
int right_mid = v[n/2];
int left_mid = *max_element(v.begin(), v.begin() + n/2); // 左半段最大值
double median = (left_mid + right_mid) / 2.0;
为什么不用 partial_sort 或 sort?
如果只是求中位数,partial_sort(比如排前 n/2+1 个)仍做多余工作;sort 更是全量排序,浪费。而 nth_element 的底层是 introselect,最坏也是 O(n),实际性能优势明显,尤其在大数组或嵌入式等对延迟敏感场景。
但要注意:nth_element 不稳定——相等元素的相对顺序可能改变;若业务逻辑依赖稳定性(比如带索引的结构体),就得改用 stable_sort + 随机访问,或手写 partition。
常见错误:迭代器范围写错或 k 超界
传给 nth_element 的第三个参数必须是尾后迭代器(v.end()),第二个参数必须在 [first, last) 范围内。k 超出范围(比如空数组调用、k >= v.size())会导致未定义行为,运行时可能崩溃或静默错乱。
- 务必检查
v.empty()再调用 - 计算索引时用
v.size() / 2,不是(v.size() - 1) / 2(后者在偶数时偏左) - 对
vector<double></double>同样适用,但注意浮点比较不用于中位数判定,函数本身不关心类型,只依赖比较
中位数看似简单,但数组修改、奇偶分支、边界检查这三处最容易漏掉——尤其是线上服务里传入空容器或单元素容器时,nth_element 不报错但后续取值会越界。










