题目描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
知识点:
- 最大堆和最小堆是二叉堆的两种形式。
- 最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
- 最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。
- 最大堆和最小堆是二叉堆的两种形式。
思路:堆排序,构建最大堆,实现时间复杂度O(n*logk);
1.构建前k个元素构造成最大堆;
2.遍历下标在k以后的数组,与堆顶比较,如果小于堆顶,置换二者,并重新构建最大堆,否则不处理;
3.将k之前的元素从小到大加入集合,返回集合;
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) { ArrayList<Integer> array = new ArrayList<Integer>(); if (input == null || input.length == 0 || k <= 0 || k > input.length) { return array; } int length = input.length; // 1.构建最大堆 for (int i = k / 2 - 1; i >= 0; i--) { buildMaxHeap(input, i, k); } // 2.对比k之后元素,若小于堆顶值,交换二者,并重新构建最大堆 for (int i = k; i < length; i++) { if (input[i] < input[0]) { swap(input, i, 0); buildMaxHeap(input, 0, k); } } // 3.将最小的k个数,从小到大加入集合 for (int i = k-1; i >= 0; i--) { array.add(input[i]); } return array; } private void buildMaxHeap(int[] input, int i, int k) { int leftChild = 2 * i; int rightChild = 2 * i + 1; int large = i; if (leftChild < k && input[leftChild] > input[large]) { large = leftChild; } if (rightChild < k && input[rightChild] > input[large]) { large = rightChild; } // 将最大值向前移动,i所对应的值,移动到没有比它大的值将停止移动 if (large != i) { swap(input, i, large); buildMaxHeap(input, large, k); } } private void swap(int[] input, int i, int large) { int temp = input[i]; input[i] = input[large]; input[large] = temp; }