查找中位数(分治策略)

  • Post author:
  • Post category:其他




问题描述

设计与实现查找数组的中项问题的算法;



解决思路

要找出一个数组的中位数,最简单的方法当然是将数组排序,但快速排序的时间复杂度也需要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

在这里插入图片描述



版权声明:本文为qq_40401156原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。