【Offer】[40] 【最小的K个数】
题目描述
输入n个整数,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
思路分析
- 利用Patition函数,将数组分成两部分,判断返回的值是否在第k个位置,如果是,那么前k个数即为所求的数,如果小于k,那么在右边,如果大于k,在左边。
- 利用大顶推,将数组中前k个值放入大顶推中,后面的继续放入大顶推,如果后面的数值小于大顶推中最大数,就与之交换,如果大于最大值,就继续往后遍历。
测试用例
- 功能测试:输入的数组中有相同的数字;输入的数组中没有相同的数字。
- 边界值测试:输入的k等于1或者等于数组的长度。
- 特殊输入测试:k小于1;k大于数组的长度;指向数组的指针为NULL。
Java代码
public class Offer040 {
public static void main(String[] args) {
test1();
test2();
test3();
}
public static ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
return Soltuion2(input, k);
}
/**
* partition的思路: 利用Partition 第k个位置的分界线,左边的值比第k个位置的值小,右边的比它大, 那么,左边的k个数就是所要求的值
*
* @param input
* @param k
* @return
*/
private static ArrayList<Integer> Soltuion1(int[] input, int k) {
ArrayList<Integer> list = new ArrayList<Integer>();
if (input == null || input.length < 0 || k <= 0 || 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) {
low = index + 1;
index = Partition(input, low, high);
} else {
high = index - 1;
index = Partition(input, low, high);
}
}
for (int i = 0; i < k; i++) {
list.add(input[i]);
}
return list;
}
private static int Partition(int[] input, int low, int high) {
int pivot = input[low];
while (low < high) {
while (low < high && input[high] >= pivot)
high--;
input[low] = input[high];
while (low < high && input[low] <= pivot)
low++;
input[high] = input[low];
}
input[low] = pivot;
return low;
}
/**
* 思路二:
*
* @param input
* @param k
* @return
*/
private static ArrayList<Integer> Soltuion2(int[] input, int k) {
ArrayList<Integer> list = new ArrayList<Integer>();
if (input == null || input.length < 0 || k <= 0 || k > input.length) {
// 这里要判断k的取值
return list;
}
int[] numbers = new int[k];
for(int i=0;i<k;i++) {
numbers[i]=input[i]; //放入前k个数
}
for(int i=k/2-1;i>=0;i--) {
adjustDown(numbers,i); //向下调整大顶堆
}
for(int i =k;i<input.length;i++) {
if(input[i]<numbers[0]) {
numbers[0] = input[i];
adjustDown(numbers, 0);
}
}
for (int num : numbers) {
list.add(num);
}
return list;
}
private static void adjustDown(int[] numbers, int k) {
int tmp = numbers[k];//保存要调整的值
for(int i=k*2+1;i<=numbers.length-1;i=i*2+1) {//因为k要遍历到索引0,索引需要+1
if(i<numbers.length-1 && numbers[i]<numbers[i+1]) {
i++; //将i的索引指向左右子节点中最大的值
}
if(tmp>=numbers[i]) break; // 如果要调整的值大于它左右子节点中最大的数,就不需要调整了
else {
numbers[k] = numbers[i];//与最大的值交换
k=i;//修改k值,继续向下调整
}
}//for
numbers[k] = tmp; //被筛选的值放入最终位置
}
private static void test1() {
int[] input = { 4, 5, 1, 6, 2, 7, 3, 8 };
ArrayList<Integer> list = GetLeastNumbers_Solution(input, 4);
System.out.println(list);
}
private static void test2() {
}
private static void test3() {
}
}
代码链接
************ **供自己学习查阅使用(我只想静静的写篇博客,自我总结下[手动狗头]) by Pavel** *********