算法思路如下
#include<iostream>
#include<stdlib.h>
#include <math.h>
using namespace std;
//交换函数
void swap(int a[], int i, int j)
{
int tmp=a[i];
a[i] = a[j];
a[j] = tmp;
}
//你们所熟悉的快速排序,但是这里只用了一个指针
//也可以如第一篇博客所写的那样采用两个指针(头尾)
int Partition(int *arr, int left, int right,int pivot)
{
int x = arr[pivot];
swap(arr,right,pivot);
int i = left -1;
for (int j=left;j<right;j++)
{
if (arr[j]<=x)
{
i++;
swap(arr,i,j);
}
}
swap(arr,i+1,right);
return i+1;
}
//通过插入排序找到中位数
int InsertSortAndFindMid(int *arr, int left,int right)
{
int len = right-left;
int mid = floor((left+right)/2);
for (int i=left+1;i<=right;i++)
{
int key=arr[i]; //保存此时交换节点的值
int j=i-1; //i之前的数组
while((j>=left) && (key<arr[j])){
arr[j+1]=arr[j];//将值比key大的右移1位
j--;
}
arr[j+1]=key;//此时j+1的位置是空出来的,右侧全是比key大的值
}
return mid;
}
int Select(int *arr, int left, int right, int position)
{
if (right-left+1<5)
{
InsertSortAndFindMid(arr,left,right);
return arr[left+position-1];
}
int group = (right-left+5)/5; //划分组数
for(int i = 0;i<group;i++)
{
int l = left+5*i;
int r = (left + i * 5 + 4) > right ? right : left + i * 5 + 4; //如果超出右边界就用右边界赋值
int mid = InsertSortAndFindMid(arr, l, r);
swap(arr, left + i, mid); // 将各组中位数与前i个
}
int pivot = Select(arr,left,left+group,(group+1)/2);//中位数的中位数
int q = Partition(arr,left,right,pivot);
int len = q-left+1; //[left,q]的长度
if (position==len)
return arr[q];
else if (position<len)
return Select(arr,left,q-1,position);
else
return Select(arr,q+1,right,position-len);
}
//这里所有的函数都包含right
//position 是从1开始的 不包含0
int main()
{
int midNum1;
//int data[3] = {5,3,2};
int data[21] = { 2,6,8,99,45,63,102,556,10,41,11,23,25,64,62,75,83,46,53,29,95 };
midNum1 = Select(data, 0, sizeof(data)/sizeof(*data)-1,20);
cout<<"midNum1="<<midNum1<<endl;
return 0;
}
代码如上,排序结果如下,可以自行测试
运行时间下界的计算
ceil(向上取整) floor(向下取整) x(中位数的中位数)
1、 上述将数组分为 ceil(n/5),经过x为“主元”的划分之后,最起码可以确定除了划分不足5个的那组+主元x所在的那组这两组,**ceil(n/5)**组中至少有一半的组中存在3个元素大于x,因此不计算上述两组,比x大的元素至少有
3(ceil(1/2×ceil(n/5)-2)≥3×(1/2×n/5 -2) =3n/10-6
2、 因此在文初的第五步中,需要用Select进行递归遍历的最多只有 7n/10+6个元素
3、 假设文初步骤 1、2、4需要的时间为 O(n),步骤3为 T(ceil(n/5)) , 步骤5最多为 T(7n/10+6)
因此得到下式子,(先暂时不管140这个值)
4、 进行替代, 令O(n)≤an, T(n)≤cn,其中a和c为适当打的常数 (大于0),将两式子带入上式得到
5、 让上式最多是cn,因此 -cn/10+7c+an ≤ 0
进行化简得到 c(7-n/10)≤-an 即 c≥an/(n/10-7)=10a × n/(n-70) ,观察式子可知当 n>70的时候,方可满足a,c均大于0
又因为 n≥140,所以 n(n-70)≤2 即当 c≥20a 的时候 仅存在一个cn ,证得最坏情况下 运行时间为线性 的
思考,为什么是划分成5个一组,如果是7个、3个一组还是线性的吗
1、当7个一组的时候: 最后计算会得到 T(n)≤cn+(-cn/7+9c+an),可得到当n>63的时候,方可满足a,c均大于0
又因为 n≥140,所以 n(n-63)<2 即当 c≥14a 的时候 仅存在一个cn ,证得最坏情况下 运行时间为线性 的
2、当3个一组的时候: 最后计算会得到 T(n)≤cn+5c+an,因为 5c+an>0,所以证得最坏情况下 运行时间不是线性 的