package com.refe.algorithm;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
/*输入n个整数,输出其中最小的k个。
* 例如输入1,2,3,4,5,6,7和8这8个数字,
* 则最小的4个数字为1,2,3和4。
*
* 1、第一次遍历取出最小的元素,第二次遍历取出第二小的元素,依次直到第k次遍历取出第k小的元素。
* 这种方法最简单,时间复杂度是O(k*n)。看上去效率很差,但当k很小的时候可能是最快的。
* 2、对这n个元素进行排序,然后取出前k个数据即可,可以采用比较普遍的堆排序或者快速排序,时间复杂度是O(n*logn)。
* 这种方法有着很大的弊端,题目并没有要求这最小的k个数是排好序的,更没有要求对其它数据进行排序,
* 对这些数据进行排序某种程度上来讲完全是一种浪费。而且当k=1时,时间复杂度依然是O(n*logn)。
* 3、可以把快速排序改进一下,应该和楼主的kth_elem一样,这样的好处是不用对所有数据都进行排序。
* 平均时间复杂度应该是O(n*logk)。(在本文最后一节,你或将看到,复杂度可能应该为O(n))
* 4、使用我开始讲到的平衡二叉树或红黑树,树只用来保存k个数据即可,这样遍历所有数据只需要一次。
* 时间复杂度为O(n*logk)。后来我发现这个思路其实可以再改进,使用堆排序中的堆,堆中元素数量为k,
* 这样堆中最大元素就是头节点,遍历所有数据时比较次数更少,当然时间复杂度并没有变化。
* 5、使用计数排序的方法,创建一个数组,以元素值为该数组下标,数组的值为该元素在数组中出现的次数。
* 这样遍历一次就可以得到这个数组,然后查询这个数组就可以得到答案了。时间复杂度为O(n)。
* 如果元素值没有重复的,还可以使用位图方式。这种方式有一定局限性,元素必须是正整数,
* 并且取值范围不能太大,否则就造成极大的空间浪费,同时时间复杂度也未必就是O(n)了。
* 当然可以再次改进,使用一种比较合适的哈希算法来代替元素值直接作为数组下标。
*
* 最好的方案是第一节中所提到的思路3:当然,更好的办法是维护k个元素的最大堆,原理与上述第2个方案一致,
* 即用容量为k的最大堆存储最小的k个数,此时,k1<k2<...<kmax(kmax设为大顶堆中最大元素)。
* 遍历一次数列,n,每次遍历一个元素x,与堆顶元素比较,x<kmax,更新堆(用时logk),否则不更新堆。
* 这样下来,总费时O(n*logk)。
* */
public class MinK {
@Test
public void test() {
int[] array = { 6, 5, 7, 2, 8, 1, 3, 4 };
if (4 <= array.length)
// topk(array, 4);
topkheap(array, 4);
}
private void topkheap(int[] array, int n) {
List heap = new ArrayList();// 维护一个大顶堆
for (int i : array) {
heap.add(i);
formatheap(heap);
if (heap.size() == n)
break;
}
for (int i = n; i < array.length; i++) {
System.out.println(heap);
if (array[i] < (int) heap.get(0)) {
heap.set(0, array[i]);
formatheap(heap);
}
}
System.out.println(heap);
}
private void formatheap(List heap) {// 格式化堆,将大数推向堆顶
for (int i = heap.size() - 1; i > 0; i = (i - 1) >> 1) {
int max = 0;
if (i % 2 != 0) // 左孩子
max = i + 1 < (heap.size() - 1) ? getmax(heap, i, i + 1) : i;
else
// 右孩子
max = getmax(heap, i, i - 1);
if ((int) heap.get(max) > (int) heap.get((i - 1) >> 1))
swap(heap, max, (i - 1) >> 1);
}
}
private int getmax(List heap, int i, int j) {
return (int) heap.get(i) > (int) heap.get(j) ? i : j;
}
private void swap(List heap, int i, int j) {
int tmp = (int) heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, tmp);
}
// 位图法,适用没有重复的元素,并且元素的值是正整数并且值不大
private void topk(int[] array, int n) {
long mask = 0;
for (int i : array)
mask = mask | (1 << (i));
// System.out.println(mask);
int index = 0, m = 0;
while (m < n) {
if ((mask & (1 << index)) != 0) {
System.out.print(index);
m++;
}
index++;
}
}
}
寻找最小的k个数
最新推荐文章于 2022-11-23 20:01:05 发布