算法设计:如何求数组中第i大 或 第i小的数 (续上)

http://blog.csdn.net/yanzi1225627/article/details/8111806 、http://www.eyeandroid.com/thread-9629-1-1.html这里讨论了求第二大的思路。现在研究如果是第i大怎么办? 先清晰一下概念,如果有个数组a[6] = 2 60 10 32 84 6; 那么第1大的数是最大值84, 第2大是60, 第3大是32, 第1小是2, 第二小是6., 第三小是10, 第4小是32. 也即如果让找第i小, 等价于找第n-i+1大。 如果让找第i大, 等效于找第n+1-i小,两者可以互换。

  思路还是快速排序,为了方便我们假设找第i小的数。

先看快速排序的partition函数:

int partition(int a[], int i, int j)
{
	int pivot = a[i];
	while(i<j)
	{
		while(i<j && a[j] >= pivot)
			j--;	
		if(i<j)
			a[i++] = a[j];
		while(i<j && a[i] <= pivot)
			i++;
		if(i<j)
			a[j--] = a[i];
	
	}
	a[i] = pivot;
	return i;
}


下面就是找第i小的数:

int find_n_min(int a[], int low, int high, int n_min)
{
	int p = partition(a, low, high);
	if(p-low+1 == n_min)
		return a[p];
	if(p-low+1 > n_min)
	 return	 find_n_min(a, low, p-1, n_min);
	else
	 return find_n_min(a, p+1, high, n_min-(p-low+1));
	
}


    解释下思路,先找到划分的基准索引p,p左边的都是小于a[p]的, 那么可以得到a[p]就是第p-low+1小的数。 为了方便举个例子, 如果经过一次partition函数之后,数组为 5 3 6 12 50 21,p = 3,也就是a【3】 = 12是划分的基准,那么a【3】肯定是3-0+1 = 4,第4小的数。

       如果p-low+1 >n_min, 比如,p-low+1 = 5, a[p]是第5小的数,而程序让找第2小的数,这个时候需要递归到 【low, p-1】区间去找第2小的数,这个举了例子应该不难理解了。

         对应的如果小于n_min的话,就要从p+1开始到high区间进行调用搜索第n_min - (p - low +1)小的数。 

     这里需要注意两个事,

第一,递归的时候前面不要忘了return

第二,递归的时候新的区间一定是【low, p-1】,这个减一一定不能少!为什么?这是我们这里采用的a[low]作为基准值,而http://blog.csdn.net/yanzi1225627/article/details/8109035这里采用的是(high + low)/2作为基准值,所以递归的时候,临界区间的一边没有减一。

       为了清晰说明这个问题,我们举个例子,假设初始数组a[] = 12 3 6 5 50 21,我们要找第3小的数。

第一次进到partition函数后,以12作为基准,partition函数返回的p = 3,数组这个时候更新成5 3 6 12 50 21,因为p-0+1 = 4大于3,所以索引从0到2搜索即可!如果上面临界区间没有减1,也就是从0到3搜索的话(数组为5 3 6 12),我们看看会有啥后果?

第二次进到partion,基准为5,函数返回的p = 1,数组更新成3 5 6 12,由于p-0+1 = 2,因此需要进到第三次partition,临界区间不加1的话(正确的应该从p+1开始),就是从p(也就是1)到3,数组为 5 6 12,问题转换为在5 6 12中找第1小的数

第三次进到partion,坏就坏在这,因为是用的第一个数5作为基准的,所以这个partition返回的p = 1. 也就是5,所以最终函数的找到的第三小的数也就是5.这显然是错误的!因此,在这里用a[low]作基准的话,临界区间必须减1。如果像这里http://blog.csdn.net/yanzi1225627/article/details/8109035用mid = (low +high)/2坐的基准,更新后的区间则不用减一。

     至此,在数组中找第i小的问题解决了,如果要找第i大的数只需将参数转化下就中,这在开头交代过。或者将上面函数里的p-low+1 改成high-p+1,a[p]为第i大的数,简单调整下即可。

 

 

阅读更多

没有更多推荐了,返回首页