O(n)的时间复杂度求中位数
O(n)的时间复杂度求中位数
O(n)
中位数问题是指:在O(n)
的时间复杂度内找到一个无序序列的中位数。
在开始O(n)
时间复杂度求中位数之前,先手写一下快速排序。
快速排序的实现
Reference:
快速排序|菜鸟教程
白话经典算法系列之六 快速排序 快速搞定
快速排序的原理
如果想正序排序一个序列:
- 从序列中找到一个 基准数 。
- 分区:将序列中比基准数大的数字都放到基准数的右边,将序列中比基准数小的数字都放在基准数的左边。
- 此时,序列被分成了三部分:
左序列
+基准数
+右序列
。 - 实现
2
的方法可以是 挖坑法 。
- 此时,序列被分成了三部分:
- 对左序列和有序列进行排序即可(递归),直到左/右序列长度为
1
。
快速排序的复杂度
- 时间复杂度:
- 最好:
O(nlogn)
- 最坏:
O(n^2)
- 平均:
O(nlogn)
- 最好:
- 空间复杂度:
O(nlogn)
- 稳定性:不稳定
快速排序的实现1
void quick_sort(int a[], int left, int right){if (left >= right){return;}int l = left, r = right;int x = a[left]; // 选取当前序列最左边的数字为基准数while (l < r){while (l < r && a[r] >= x){r --;}if (l < r){a[l] = a[r];l ++;}while (l < r && a[l] < x){l ++;}if (l < r){a[r] = a[l];r ++;}}a[l] = x;quick_sort(a, left, l - 1);quick_sort(a, l + 1, right);
}
- 每一次快排都会确定一个位置,这个位置是
l
,大小是基准数
。 - 如果我们想每一次都知道 快速排序确定的位置 ,那么可以写一个
partition函数
。(事实上,这是在为O(n)解决中位数问题做铺垫。)
快速排序的实现2——partition函数
int partition(int a[], int l, int r){int x = a[l];while (l < r){while (l < r && a[r] >= x){r --;}if (l < r){ a[l] = a[r];l ++;}while (l < r && a[l] < x){l ++;}if (l < r){a[r] = a[l];r --;}}a[l] = x;return l;
}void Q_sort(int a[], int l, int r){if (l < r){int index = partition(a, l, r);Q_sort(a, l, index-1);Q_sort(a, index+1, r);}
}
partiton()
负责将获得每一次进行步骤2:分区
得到的基准数
在最终递增序列中的 位置 。Q_sort()
中的index
就是该位置。根据该位置将序列分为左右序列。
有了partition
函数,就可以实现O(n)
时间复杂度找中位数的工作了。
基于partition函数的O(n)中位数
显然,如果没有O(n)
的限制,那么一个直白的想法是:将无序序列排序,然后输出序列的 第n/2
个位置 的元素。
n
是序列的长度。- 其实,对于中位数应该分情况讨论:
- 当
n
是奇数时,中位数是a[n/2]
; - 当
n
是偶数时,中位数是(a[n/2] + a[n/2 - 1]) / 2.0
。
- 当
上述算法的时间复杂度是O(nlogn)
。
考虑到,对于partition函数,每一个可以确定一个位置! 那么,假设这个位置是index
,那么对于中位数的位置pos
:
- 如果
index = pos
,显然,找到了中位数a[index]
。 - 如果
index > pos
,显然,中位数位于区间[l, index-1]
。- 此时,只需对区间
[l, index-1]
再次进行partition
操作即可。
- 此时,只需对区间
- 如果
index < pos
,显然,中位数位于区间[index+1, r]
。- 同上。
根据上述思想,编写函数getMidNumber()
:
int getMidNumber(int a[], int l, int r, int pos){while (true){int index = partition(a, l, r); // 获得基准数的位置if (index == pos){return a[index];}else if (index > pos){ // 只需要在[l, index-1]区间内找pos位置即可r = index - 1;}else { // 只需要在[index, r]区间内找pos位置即可l = index + 1;}}return -1; // 一般程序不会到这里
}
- 其中,
pos
的含义是中位数的位置:- 当序列长度为奇数时,pos = n/2
- 当序列长度为偶数时,pos = n/2 或 n/2 - 1
比如,可以编写如下代码测试:
int main(){int a[] = {10, 8, 3, 5, 6, 2, 1, 7, 9, 4};int aLen = sizeof(a) / sizeof(int);if (aLen&1){cout << "Mid= " << getMidNumber(a, 0, aLen-1, aLen/2) << endl;}else{cout << "Mid= " << (getMidNumber(a, 0, aLen-1, aLen/2) + getMidNumber(a, 0, aLen-1, aLen/2-1)) / 2.0 << endl;}return 0;
}
aLen
是序列a
的长度。
时间复杂度分析
最坏:O(n^2)
- 假设一种极端情况,每一次选取的基准数都是序列中最小的那个数字,因此partition函数会依次返回
0,1,2...n/2
,每一次partition函数都需要O(n)
的时间复杂度。因此,最坏的时间复杂度为O(n^2)
。
最好: O(n)
- 假设一种完美情况,第一次得到的基准数就是中位数,那么只需要执行一次partition函数,因此时间复杂度是
O(n)
。
平均: O(n)
数学不好,证明不会。据 他们 说该算法的 期望 时间复杂度是O(n)
。
这好像是涉及 主定理MasterTheorem。搜了好多网页博客也没看懂。
Reference:
主定理 Master Theorem
拓展:找序列中第K
大的数字
其实找中位数就是找序列中第n/2
大的数字。
因此找需要调用getMidNumber(a, 0, aLen-1, k)
即可找到序列中第k
大的数字了。
大佬的一种更简洁的写法
O(n)求中位数和第k大数
2020.9.26 14:41 周六
O(n)的时间复杂度求中位数相关推荐
- 堆实战(动态数据流求top k大元素,动态数据流求中位数)
动态数据集合中求top k大元素 第1大,第2大 ...第k大 k是这群体里最小的所以要建立个小顶堆 只需要维护一个大小为k的小顶堆 即可当来的元素(newCome)> 堆顶元素(smallTo ...
- 【两个有序数组求中位数】
/* 两个有序数组求中位数问题; 这个题有很多方法: 方法一:排序,找到中位数: 方法二:归并排序的思想 方法三:转换成求第k小值 */ /* 思路:使用二分查找,时间复杂度为log(m+n). ...
- Leetcode算法题:两个有序数组求中位数
Leetcode算法题:两个有序数组求中位数 要求时间复杂度为O(log(m+n)) 思路: 暴力解决:合并数组并排序,简单且一定能实现,时间复杂度O(m+n) 由于两个数组已经排好序,可一边排序一边 ...
- 线性O(N)时间复杂度求素数 , 筛法
线性O(N)时间复杂度求素数 , 筛法 1 /* 2 线性时间求出1-N 的素数 , 时间复杂度为O( N) : 3 一个合数可以表示成若干个素数的积 4 比如说 i = 6 =2 * 3 , A = ...
- 算法练习day6——190323(求中位数、堆排序、稳定性)
1.求中位数 有一个流,不断输出数,然后会不停地询问已输出的那些书的中位数. 解决方法:用一个大根堆,一个小根堆存那些已输出的数. 大根堆中存储较小的N/2个数: 小根堆中存储较大的N/2个数: 这样 ...
- 求中位数_图解面试题:如何分析中位数?
学校每次考试完,都会有一个成绩表.例如,表中第1行表示编号为1的用户选择了C++岗位,该科目考了11001分. 问题:写一个sql语句查询每个岗位的中位数位置的范围,并且按岗位升序排序,结果如下: 解 ...
- 线性时间复杂度求数组中第K大数
求数组中第K大的数可以基于快排序思想,步骤如下: 1.随机选择一个支点 2.将比支点大的数,放到数组左边:将比支点小的数放到数组右边:将支点放到中间(属于左部分) 3.设左部分的长度为L, 当K &l ...
- 给定一个整数序列,求中位数
问题描述: 给定一个整数序列,求中位数.如果序列个数为奇数,中位数为升序的中间位置,如果是偶数,这位升序的中间两个数的平均值. 输入: 输入包含多组测试数据,每一组第一行为n(n<104)表示这 ...
- hive、impala 求中位数
hive求中位数: (0.5参数可调) select percentile(cast(p as int),0.5) from student2; impala求中位数: select APPX_MED ...
最新文章
- g++使用C++11编译源文件
- 关于C#写的记事本中一个问题
- python中定义的函数不掉用不会执行_如果出现异常/错误,如何不在python中停止执行其他函数...
- jhipster 配置 mysql_java – 将jhipster后端和前端分成两个项目?
- 错误提示之:已超过传入消息(65536)的最大消息大小配额。若要增加配额,请使用相应绑定元素上的 MaxReceivedMessageSize 属性...
- Qt元对象QMetaObject的indexOfSlot等函数获取类方法注意问题
- LINUX前期知识回顾
- Qt工作笔记-线程池作用之一:限制系统中执行线程的数量
- linux shell grep -v grep|awk ‘{print $1}‘ 是什么意思
- VC++6.0安装完成后MSDEV.EXE出错的解决办法
- CSDN开发者云平台体验
- 武汉理工大学刷课,刷在线作业程序,做作业脚本
- 使用端口扫描工具消除端口安全威胁
- 通过有限差分和matlab矩阵运算直接求解一维薛定谔方程,通过有限差分和MATLAB矩阵运算直接求解一维薛定谔方程...
- latex设置页面大小边距行距等
- python 处理 Excel 表格
- 关于程序员的职业规划分析
- 福利分享:1024程序员节,给大家推荐一个极简win10
- 计算机二级c语言预测,计算机二级C语言考前预测上机试题及解析
- latex 插入Logo
热门文章
- 计算机 计算能力测试题,高中数学计算能力训练题.doc
- 学习笔记四:word2vec和fasttext
- 最佳情侣身高差 (10 分)
- Android 拨打电话各安卓版本适用
- 从新建工程开始使用C++开发单片机(以STM32为例):一、项目介绍
- form-login属性详解
- Open×××优化之-巨型帧
- Python+Vue计算机毕业设计停车场管理系统8f46a(源码+程序+LW+部署)
- revit中在三维视图下显示房间文字和“房间集成”
- DeepLearning: 归纳几种常用的激活函数(activation)