寻找无序数组的第K项(分治)

寻找n个数的第K项只需要对它们进行排序,然后找到即可。但是耗时O(nlongn);在书上看到了很好的方法,想着实现一下:

输入:一个数列A,一个整数K

输出:数列A中第K小的数  

我们假定数组的一个数V,现在把数组A分成三份。{

         A1 = {  X  |  X  <  V  }

         A2 = {  X  |  X  =  V  }

         A3= {  X  |  X  >  V  }

}

例如任意一个数组A={2,36,5,21,8,13,11,20,5,4,1};

假设V等于5     A1=2 4 1 ;      A2=5 5;       A3=36  21  8  13  11 20

搜索范围就减小了。现在只需要在A的三个子集中寻找,如果想寻找第8小元素,他一定在A3中,同理可以得到递归

            f(A1,v)    if  k<=|A1|

f(A,k)= printf(A2)   if  k==|A1+A2|

            f(A3,v-|A1|-|A2|)   if  k>|A1+A2|

思路清晰后,重要的是V的选取,因为V的选取决定了整个算法的复杂度。最理想的情况就是|A1|,|A3|=1/2|A|

如果我们一直背运,我们选出来的V很可能一直是数组的最大元素或者最小元素,那么算法需要执行

   n+(n-1)+(n-2)+....+n/2=O(n*n)

但是这种情况出现的概率非常低

最好的情形是选取的V正好能把数组一分为二,从而复杂度为O(n)

从O(n)->O(n*n) 算法的平均运行时间基本符合最佳运行时间




当然代码肯定有瑕疵。才接触分治  又不好的地方大家一起交流下


#include<cstdio>
#include<iostream>
using namespace std;
void f(int a[], int zuo, int you, int v){
	//printf("%d\n", v);
	//for (int i = zuo; i <= you; i++){
	//	printf("%d ", a[i]);
	//}
	//system("pause");
	//printf("\n");
	int a1[10] = { 0 }, a2[10] = { 0 }, a3[10] = {0};
	int num = v / 2;
	int j = 0, k = 0, l = 0;
	for (int i = zuo; i <= you; i++){
		if (a[i] < a[num])
			a1[j++] = a[i];
		else if (a[i] == a[num])
			a2[k++] = a[i];
		else
			a3[l++] = a[i];
	}
	if (v <= j){
		f(a1, 0, j - 1, v);
	}
	else if (v>j&&v<=k+j) {
		printf("%d ", a2[k-1]);
		return;
	}
	else if (v > j + k){
		f(a3, 0, l - 1, v - j - k);
	}
}
int main(){
	int a[10] = { 1, 3, 5, 2, 4, 6, 7,  8 };
	int b[10] = { 1, 3, 9, 5, 2, 4, 6, 7, 8 };
	//int n = 8;
	int k;
	//scanf("%d", &k);
	printf("偶数的第n小的值分别为:\n");
	for (int k = 1; k <= 8;k++)
	f(a, 0, 7,k);
	printf("\n奇数的第n小的值分别为:\n");
	for (int k = 1; k <= 9; k++)
		f(b, 0, 8, k);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值