一、问题描述
给定一个数组,数组中的数据无序,在一个数组中找出其第k个最小的数,例如对于数组x,x = {3,2,1,4,5,6},则其第2个最小的数为2。
二、解题思路
本算法跟快排的思想相似,首先在数组中选取一个数centre作为枢纽,将比centre小的数,放到centre的前面将比centre大的数,放到centre的后面。如果此时centre的位置刚好为k,则centre为第k个最小的数;如果此时centre的位置比k前,则第k个最小数一定在centre后面,递归地在其右边寻找;如果此时centre的位置比k后,则第k个最小数一定在centre后面,递归地在其左边寻找。
注意:centre的位置=其下标值+1,因为数组中的第一个元素的下标为0。
从上面的描述中,我们可以看到这个算法运用了减治的方法求解。减治的思想与分治非常相似,同样是在一次操作中,削减问题的规模,只是分治把每个子问题求解后,要合并每个子问题的解才能得到问题,而减治的方法,却不用合并子问题的解,子问题的解,直接就是原问题的解。举个例子来说,就像快排和二分查找算法,前者是分治,后者是减治。因为快排要等到所有的子数组都排完序,原数组才有序,而二分查找却不用,它每执行一次查找,直接丢弃一半的数组,而不用合并子问题的解。不过也有不少书,把他们都归为分治法。
- /**
- * 输入n个整数,找出其中最小的k个整数,找出其中最小的k个数。
- * 例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字为1,2,3,4
- */
- public class E30KLeastNumbers {
- // 使用partition函数
- public int partition(int[] arr, int left, int right) {
- int result = arr[left];
- if (left > right)
- return -1;
- while (left < right) {
- while (left < right && arr[right] >= result) {
- right--;
- }
- arr[left] = arr[right];
- while (left < right && arr[left] < result) {
- left++;
- }
- arr[right] = arr[left];
- }
- arr[left] = result;
- return left;
- }
- public int[] getLeastNumbers(int[] input,int k){
- if(input.length == 0 || k<= 0)
- return null;
- int[] output = new int[k];
- int start = 0;
- int end = input.length-1;
- int index = partition(input,start,end);
- while(index != k-1){
- if(index > k-1){
- end = index -1;
- index = partition(input,start ,end);
- }
- else{
- start = index+1;
- index = partition(input,start ,end);
- }
- }
- for(int i = 0;i<k;i++){
- output[i] = input[i];
- }
- return output;
- }
- public static void main(String[] args){
- int[] arr= {4,5,1,6,2,7,3,8};
- E30KLeastNumbers test = new E30KLeastNumbers();
- int[] output=test.getLeastNumbers(arr, 4);
- for(int i = 0;i<output.length ;i++){
- System.out.print(output[i]+",");
- }
- }
- }