计算中值和选择问题
作者:Keifer Gu
算法来源:算法设计与分析基础(美)Anany Levitin
一、如何快速获得一个数列的第K大的元素
第我门的第一反应一般是应该先将这个数列进行排序,然后找出数列中的第K个最小的数,但是这样做太浪费效率。基于减治法的思想,我们这里有一个高效的算法。
二、算法思想
首先对寻找中值的情况进行分析:
中值的左边全部比这个数小,右边则全部比它大,由此也可知,我们根本不需要给这个数列进行排序,只需要寻找到这个数即可。给出下面这组数:1 10 9 7 12 8 2 15
这里一共有九个数,那么中值就是第五个最小的数。我们以第一个数4为基准,将这个数列排列为4的左边均比4小,右边均比4大的数列。具体代码的实现过程我们后面来看,第一次排列后的结果为:现在数字4到了第三位,我们可以看到4它的前面的数都比它小,后面的数都比它大,可以得知,数字4是这个数列中第三小的数,因为我们要找的中位数是第五小的数,也就是说在4前面的数只可能第一小,第二小,那么前面的数我们都可以不看了,只看后面的。2 1 9 7 12 8 10 15
现在对后面的数进行处理:同样的,以第一个数为基准,处理后结果为:9 7 12 8 10 15
8 7 9 12 10 15
而9是第六小的数,因为删除了前三小的数,就在这里是第三,在整个数列中就是第六。六大于五,那么我们要找的数就在它的前面了:以8为基准,处理后为:8 7
现在的8就是第五小的数了,及我们需要的中值。7 8
三、代码实现
#include <iostream> using namespace std; int ValSelect(int a[],int l,int r,int k) //l为需要处理的数组起点,r为终点,k为需要找到的第几小的值 { int i=l,j=r; //i 是起点,最前面的数,j 是最后面的数 while(i<j&&a[j]>=a[i]) //寻找到比a[i]大的 最小的j j--; //j从最后一个向前循环,找到比a[i]小的数 if(i<j) //此时则将比a[i]小的数放到最前面 swap(a[i],a[j]); while(i<j&&a[i]<=a[j]) //此时是让i从前面往后递增,直到找到比a[j]小的数,注意此时的a[j]经过了交换,等于前面的a[i]也就是第一个数 i++; if(i<j) swap(a[i],a[j]); //再次交换 if(i<k) return ValSelect(a,i+1,r,k); //递归调用,i的值就是现在的数是第几小的数,k就是我们需要的第几小的数 else if(i>k) return ValSelect(a,l,i-1,k); else return a[i]; //此处就是i=k,就是找到了我们需要的数,返回该值 } int main() { int a[9]={4,1,10,9,7,12,8,2,15}; int b = ValSelect(a,0,8,4); cout<<b<<endl; return 0; }