剑指笔记——40 最小的k个数

题目:输入n个整数,找出其中最小的K个数

思路:剑指中提供了两种不同时间复杂度的思路和代码。

思路1:我们可以利用快速排序的思路解决这个问题。如果基于数组的第K个数字来调整,则使得比第K个数字小的所有数字 都位于数组的左边,比第K个数字大的都位于数组的右边。这样调整后,位于数组中左边K个数字就是最小的k个数字(这k个数字不一定是排序的)。这个思路的时间复杂度是O(n),但是需要修改输入的数组。

代码链接:https://blog.csdn.net/shakespeare001/article/details/51280814

代码1:

public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
        if(input == null)
            return null;

        ArrayList list = new ArrayList(k);
        if(k > input.length)  //k大于数组的长度,返回空链表
            return list;
        int low = 0;
        int high = input.length - 1;
        int index = partition(input,low,high);  //以第一个元素作为划分的元素,返回它在排序后数组中的位置
        while(index != k-1){ 
            if(index > k-1){  //如果返回的位置在k-1的右边
                high = index - 1;
            }else{               //返回的位置在k-1的左边
                low = index + 1;
            }
            index = partition(input,low,high);  //重新划分
        }
       for(int i = 0; i < k; i++){  //当返回的位置正好等于k-1时,将结果保存到list中
           list.add(input[i]);
       }
        return list;
    }
    //划分操作
    public int partition(int[] array,int start,int end){
        int pivot = array[start];   //将第一个元素作为划分节点保存起来
        while(start < end){
            while(start < end && array[end] >= pivot) end--;  //从后往前找到第一个比pivot小的元素
            array[start] = array[end];  //这块不是采用的交换而是直接覆盖掉
            while(start < end && array[start] <= pivot) start++;//从前往后找到第一个比pivot大的元素
            array[end] = array[start];
        }
        array[start] = pivot;  //把pivot放在正确的位置上
        return start;
    }

 

思路2:我们可以先创建一个大小为k的数据容器来存储最小的k个数字,接下来每次从输入的n个整数中读入一个数。如果容器中已有的数字少于k个,则直接把这次读入的整数放入到容器中;如果容器中已经有k个数字了,此时需要替换已有的数字。找出这已有的k个数字中的最大值,然后拿这次待插入的整数和最大值进行比较,如果比当前的最大值小,则替换已有的最大值;如果大的话,可以抛弃这个整数。我们可以使用最大堆(堆排序),也可以使用红黑树来完成这个容器的创建。

这种思路的时间复杂度是O(nlogk)。这种解法虽然慢一些,但是有两个明显的优点:(1)没有修改输入的数据(2)该算法非常适合海量数据的输入。

以下代码使用了java中的TreeSet,它实现了红黑树的功能,底层使用TreeMap实现的,其中的数据会按照插入顺序自动升序排列。

代码2:

public static ArrayList GetLeastNumbers_Solution3(int [] input, int k) {
        if(input == null)
            return null;
        ArrayList list = new ArrayList(k);
        if(k > input.length)
            return list;
        TreeSet tree = new TreeSet();
        for(int i = 0 ; i < input.length; i++){
            tree.add(input[i]);
        }
        int i = 0;
        for(Integer elem : tree){
            if(i >= k)
                break;
            list.add(elem);
            i++;
        }
        return list;
    }

代码3:这个是最大堆中的代码

public ArrayList GetLeastNumbers_Solution(int [] input, int k) {
ArrayList list = new ArrayList<>();
if (input == null || k <= 0 || k > input.length) {
return list;
}
int[] kArray = Arrays.copyOfRange(input,0,k);//复制原数组的0到k个,但是不包括k
// 创建大根堆
buildHeap(kArray);  //构建一个堆,其中对顶元素是k个数中最大 的一个元素

for(int i = k; i < input.length; i++) {
if(input[i] < kArray[0]) {  //若当前元素大于对顶元素,就把当前元素放在对顶元素的位置
kArray[0] = input[i];
maxHeap(kArray, 0);  //重新调整堆的结构,使之成为大根堆
}
}

for (int i = kArray.length - 1; i >= 0; i--) {
list.add(kArray[i]);
}
return list;
}

//创建堆
public void buildHeap(int[] input) {
for (int i = input.length/2 - 1; i >= 0; i--) {
maxHeap(input,i);
}
}
private void maxHeap(int[] array,int i) {
int left=2*i+1;
int right=left+1;
int largest=0;
if(left < array.length && array[left] > array[i])
largest=left;
else
largest=i;
if(right < array.length && array[right] > array[largest])
largest = right;
if(largest != i) {
int temp = array[i];
array[i] = array[largest];
array[largest] = temp;
maxHeap(array, largest);
}
}

 

 

你可能感兴趣的:(剑指)