算法:选择问题——求无序数组在有序情况下第i个元素

一 、引入

1 . 求无序数组最大值 和最小值 复杂度O(n)
2. 求无序数值中值?
——一般性问题 求无序数组第i小的元素

二、解决方案

1. 通过一般排序得到一个有序序列,取第i个元素

分析:最好的基于比较的排序——快排 时间复杂度Onlog(n)

2. 计数排序思想

(1)思想:
借助辅助数组C[array.max],统计array数组每个元素的个数,并存入C数组,满足C[i]为array中值为i的元素个数
从左往右遍历C数组,统计非零c数组元素的个数和记为sum,当sum>=i时输出下标并跳出遍历
(2)伪代码
select(array[])
{
C[array.max]={0} //初始统计数组值为0
for(j=0;j<array.length;j++)
C[array[j]]=array[j] //统计array每个元素的个数
sum=0 //sum 表示小于等于array[j]的个数
for(j=0;j<array.max;j++)

sum+=c[j]
if(sum>=i)
return j

}
(2) 分析时间和空间
时间复杂度 O(n) 空间复杂度O(max(array)),适合数组元素不是很大的情况,能有效减小空间复杂度,但当数组某个元素值很大的时候非常浪费空间。

3 桶排序思想

(1)思想:
求出数组的最大值和最小值,求其差值,计算桶的区间长度((max-min)/n)
将数组元素映射到对应的桶区间(桶的结构是单链表)
从前往后统计非空桶的元素个数,判断得到第i个元素所在的桶的位置
对当前桶进行插入排序 取第i个元素即得。
(2)伪代码
BucketSelect(array,i)
{
min=Min(array)
max=Max(array)
step=(max-min)/array.legth
for(j=0;j<array.length;j++)
{
b[k]<-a[j] //k满足 k*step<a[j]<(k+1)step,将数组元素映射到相应的桶里 b的每个桶是一个链表
}
count=0
for(j=0;j<array.length+1;j++)
{
if(i<count) //找到包含第i小的元素的桶 b[j]
break;
if(b[j].lenth!=0)
count+=b[j].length
}
对b[j]内部进行插入排序,排序后取第i-(count-b[j].length)个元素。
}
(3)分析:空间复杂度:O(n)
时间复杂度:最优 O(n) 无须排序的情况
最差O(n
n) n-1个元素落到同一个桶里的情况
平均 O(n) (n个元素映射到n个桶里,平均一个桶里一个,排序复杂度O(1),粗暴的分析,正确性不是很确定,欢迎指正)
空间和时间复杂度在一定程度上能得到改善

4 分治思路

(1)思路
使用快排的随机划分思想将数组划分为两个区间,第一次划分中a[low,q-1]<a[q]<a[q+1,high]
由于一次划分能够确定一个元素的最终位置,即a[q]的最终位置为q+1
比较i和q+1,若i=q+1 则a[q]即为所求答案
若i<q+1 则在a[q]的左边查找第i小的元素
若i>q+1 则在a[q]的右边查找第i-(q+1)小的元素
(2)伪代码
randomSelect(array,low,high,i)
{
If(low=high)
Return array[low] //数组长度为1时,无需再划分直接返回
q<- randomDevide(array,low,high)
k <-q-low+1 左区间长度+1
if(k=i)
return array[q]
else
if(i<k)
return selectIth(array,low,q-1,i) //左区间查找
else
return selectIth(array,q+1,high,i-k) //右区间查找
}
(3)分析
时间复杂度O(n) 空间复杂度O(n)
时间复杂度的证明:
设high-low+1=n,将随机选择算法所需时间看做随机变量T(n)。随机划分在n个元素中选择任何一个元素作为划分元素的可能性相等。所以,对于1~n之间的每一个k,子区间A[p…q]有k个小于等于划分元的概率是1/n(k是A[p…q]的长度,即划分元是n个元素中第k个最小元)
求E(T(n))的上界:
假定T(n)单调增,故可认为递归调用总是发生在较大的子区间上,即:要找的ith最小元总是落在较大的子区间中
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
也许还有其他时间和空间复杂度都比较好的算法,欢迎补充
注:以上算法证明部分摘自MIT 算法导论第2版 ppt 9.1章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值