查找中位数(分治策略)
程序员文章站
2022-06-17 19:46:28
...
问题描述
设计与实现查找数组的中项问题的算法;
解决思路
要找出一个数组的中位数,最简单的方法当然是将数组排序,但快速排序的时间复杂度也需要
O(nlogn),我们可以寻找更快的算法来解决。首先对于一个长度为n的有序数组a[n],若n为偶数,
则中位数为(a[n/2]+a[n/2-1])/2,若n为奇数,则中位数为a[n/2],那么问题的关键就是找到a[n/2]
和a[n/2-1],然而这是在有序数组中的,那么换到无序的数组中,我们可以把问题转换为求数组中
第n/2大的和第n/2+1的数,再一般点就是求一个无序数组中第k大的数。那么如何求第k大的数呢,
我们可以先在数组中取一个值value,将数组划分为小于value的small,等于value的equal,大于value
的big三个部分,分别记三个部分的元素个数为numS、numE、numB,若k<=numS,则说明我们要找
的数就在small中,若numS<k<=numS+numE,则说明我们要找的值在equal中,而又因为equal中的值
都相等,因此我们要找的值就等于equal中元素的值,若k>numS+numE,则我们要找的数就在big中;
在一趟比较完成之后,若我们没有得到我们需要的值,只得到了我们需要的数所在的范围,那么我们
可以再对得到的small或big再使用以上算法,直到得到需要的值。
算法描述
选择数组中第k大的数的算法流程图如下:
算法代码
int selectK(int a[], int length, int k)
{
int *small = new int[length];
int *equal = new int[length];
int *big = new int[length];
int value = a[0];
int numS = 0, numE = 0, numB = 0;
for (int i = 0; i < length; i++)
{
if (a[i] < value)
{
small[numS] = a[i];
numS++;
}
else if (a[i] == value)
{
equal[numE] = a[i];
numE++;
}
else
{
big[numB] = a[i];
numB++;
}
}
if (k <= numS)return selectK(small, numS, k);
else if (k > numE + numS)return selectK(big, numB, k-numS-numE);
else return value;
}
int main()
{
srand((int)time(0));
int n;
cin >> n;
int *array = new int[n];
for (int i = 0; i < n; i++)
{
array[i] = rand()%10000;
}
cout << endl;
cout << "中位数是:";
if (n % 2 == 0)
cout << (float)(selectK(array, n, n / 2) + selectK(array, n, n / 2 + 1)) / 2 << endl;
else
cout << selectK(array, n, n / 2 + 1) << endl;
delete[] array;
system("pause");
return 0;
}
算法分析
以下是本算法与快速排序的耗时对比:
数据规模n | 本算法 | 快速排序 |
1000 | 1ms | 1ms |
10000 | 2ms | 2ms |
100000 | 7ms | 28ms |
1000000 | 60ms | 1396ms |
10000000 | 4537ms | 14353ms |
上一篇: 穿透内网防线|USB自动渗透手法总结
下一篇: 恢复二叉搜索树