/**
* 获取第x小的元素
*
* 解决办法:
* 1.利用排序算法排序,然后获取第x个元素
* a.当输入不受到任何限制的时候,无法采用基数排序和桶排序,只能采用比较算法排序,而比较算法排序的时间复杂度最小也是NlgN,不合适
* b.当输入受到限制的时候,可以采用基数排序或者桶排序,这时排序是线性的,选择是常量的,那么表示依然可以线性时间范围获取到第x个元素
*
* 2.采取切分算法获取切分点,并沿着切分点的一个方向查找指定元素
*
*/
private Random random=new Random();
@Test
public void findNoXLeast(){
//样本数组
int[] array = { 5, 2, 4, 6, 1, 3 };
//查找第x小的元素
int x=5;
int value=findNoXLeast(array,x);
System.out.println("第"+x+"小的元素为"+value);
}
private int findNoXLeast(int[] array, int x) {
if(x<=0||x>array.length)
throw new RuntimeException("不存在第"+x+"个元素");
return findNoXLeast(array,0,array.length-1,x);
}
private int findNoXLeast(int[] array, int i, int j, int x) {
//只有一个元素的时候,直接返回
if(i==j)
return array[i];
//将数组进行随机切分
int p=randomPartition(array,i,j);
//包含切分点的左半部分的元素数量,其中切分点p位于最后一个位置
int k=p-i+1;
if(x==k){
//如果x刚好在切分点上,直接返回该位置的元素
return array[p];
}else if(x<k){
//如果在左边,从左边继续查找
return findNoXLeast(array, i, p-1, x);
}else{
//如果在右边,从右边继续查找。这时左边部分的k个元素也要计入次序中,所以从右边查找的时候应该查找第x-k个元素
return findNoXLeast(array, p+1, j, x-k);
}
}
private int randomPartition(int[] array, int i, int j) {
//获取i-j之间的随机值
int ran=random(i,j);
//将随机位置的值与j处的值互换位置
exchange(array, ran, j);
return partition(array, i, j);
}
private int random(int i, int j) {
return random.nextInt(j-i+1)+i;
}
private int partition(int[] array,int i,int j){
//记录将作为切分点的值
int key=array[j];
int k=i-1;
for(int m=i;m<j;m++){
if(array[m]<=key){
exchange(array,m,++k);
}
}
exchange(array, j, k+1);
return k+1;
}
private void exchange(int[] array, int m, int n) {
int t=array[m];
array[m]=array[n];
array[n]=t;
}