C#递归算法寻找数组中第K大的数
1.概述
国人向来喜欢论资排辈的,每个人都想当老大,实在当不成,当个老二,老三,老K也不错,您一定看过这样的争论:两个人吵架,一个人非常强势,另外一个忍受不住了便说:"你算老几呀?",下面就通过这篇文章就是要解决找出老几的问题!
2.应用场景
在向量V[first,last)中查找出第K大元素的值
3.分析
如果利用排序算法将向量V排好序,那么第K大元素就是索引为v.length-k的元素了,这样能解决问题,但效率不高,因为这相当于为了歼灭敌人一个小队而动用了我们全军的力量,得不偿失,回想快速排序中的分表,每次都将目标向量分为两个子表,左子表中全部小于中间元素v[mid],右边都大于中间元素v[mid],这样就可以减小了查找范围,因为我可以只查找左子表或者右子表就能找到目标元素了。如下图所示,我们可以将向量v划分成如下
按照这样的思路,我们仍使用快速排序中的分表策略,首先将向量V从中间位置分开,分成左和右,分好后,中间值的索引如果恰恰等于K,就找到了,否则如果中间元素索引大于K,则在左子表中继续查找,忽略右子表,如果中间值索引小于K,则在右子表中继续查找,如此循环往复。
快速排序中的子表划分函数为:
///<summary>
///交换位置
///</summary>
///<paramname="v"></param>
///<paramname="index1"></param>
///<paramname="index2"></param>
privatevoidSwrap(int[]v,intindex1,intindex2)
{
inttemp=v[index1];
v[index1]=v[index2];
v[index2]=temp;
}
///<summary>
///将向量V中索引{first,last)划分成两个左子表和右子表
///</summary>
///<paramname="v">向量V</param>
///<paramname="first">开始位置</param>
///<paramname="last">结束位置</param>
privateintPivotIndex(int[]v,intfirst,intlast)
{
if(last==first)
{
returnlast;
}
if(last-first==1)
{
returnfirst;
}
intmid=(first+last)/2;
intmidVal=v[mid];
//交换v[first]和v[mid]
Swrap(v,first,mid);
intscanA=first+1;
intscanB=last-1;
for(;;)
{
while(scanA<=scanB&&v[scanA]<midVal)
{
scanA++;
}
while(scanB>first&&midVal<=v[scanB])
{
scanB--;
}
if(scanA>=scanB)
{
break;
}
Swrap(v,scanA,scanB);
scanA++;
scanB--;
}
Swrap(v,first,scanB);
returnscanB;
}
设计一个函数,FindKLargest(int[]v,intfirst,intlast,intk);这个函数包括四个参数:向量V,开始位置first,结束位置last,和第k大中的K,则该函数为:
调用FindKLargest后,因为数组是从小到大排序,所以第K大元素的值为V[v.Length-k];
voidFindKLargest(int[]v,intfirst,intlast,intk)
{
//表示分表中值的索引
intindex=0;
index=PivotIndex(v,first,last);
if(index==k)
{
//找到了K大
return;
}
if(index>k)
{
//只在左子表中查找
FindKLargest(v,first,index,k);
}
else
{
//只在右子表中查找
FindKLargest(v,index,last,k);
}
}
4.运行结果:
原向量:v ={100,200,50,23,300,560,789,456,123,258}
first=0;last=v.Length;k=3
输出:456
5.结论
利用递归算法可以将比较复杂的问题划分为越来越小的小问题,这样能够使复杂问题简单化,这样的思路在系统设计和架构中同样有着至关重要的作用,一个好的架构师,面对复杂的问题,能庖丁解牛般化腐朽为神奇,而坏的却往往适得其反,他们的特长是简单问题复杂化。
6.项目文件
http://xiazai.jb51.net/201606/yuanma/FindK(jb51.net).rar