问题: 给定N个元素,寻找N个元素中最大的K个元素?
具体解法的算法思想就不详细解答了,详见《编程之美》第140页,下面给出几种解法的Java代码:
解法一:小顶堆原理
/**
* 描述: 使用小顶堆来找出n个元素中,最大的k个;结果存放在[1...k],第0个元素位置不用;
*
* @param array
* @param k
*/
public static <T extends Comparable<T>> void heapKmax(T[] array, int k){
// 建立K个元素的小顶堆, 元素从1到k
for(int i=k/2; i>0; i--){
minHeapAdjust(array, i, k);
}
T temp;
// 对于剩余的元素放入K个元素的小顶堆中
for(int i=k+1; i<array.length; i++){
if(array[1].compareTo(array[i])<0){
temp = array[1];
array[1] = array[i];
array[i] = temp;
minHeapAdjust(array, 1, k);
}
}
}
/**
* 描述: 小顶堆筛选过程;
*
* @param array
* @param root
* @param limit
*/
public static <T extends Comparable<T>> void minHeapAdjust(T[] array, int root, int limit){
T tempRoot = array[root];
for(int pos = 2*root; pos<=limit; pos*=2){
if(pos<limit && array[pos].compareTo(array[pos+1])>0) pos++;
if(tempRoot.compareTo(array[pos])<=0){
break;
}else{
array[root] = array[pos];
root = pos;
}
}
array[root] = tempRoot;
}
测试代码:
@Test
public void testHeapSort(){
Integer[] array = new Integer[] {0, 5, 10, 20, 1, 2, 23, -1, 22, 88, 77, 3 };
System.out.println("----------排序前--------");
for(int i=1; i<array.length; i++){
System.out.print(array[i]+"\t");
}
System.out.println("\n----------排序后--------");
heapSort(array);
for(int i=1; i<array.length; i++){
System.out.print(array[i]+"\t");
}
}
解法二: 类似快排序;
代码:
private static <T extends Comparable<T>> int partition(T[] array, int low,
int high) {
T pivot = array[low];
while (low < high) {
while (low < high && array[high].compareTo(pivot) >= 0) {
high--;
}
if (low < high) {
array[low] = array[high];
low++;
}
while (low < high && array[low].compareTo(pivot) <= 0) {
low++;
}
if (low < high) {
array[high] = array[low];
high--;
}
}
array[low] = pivot;
return low;
}
public static <T extends Comparable<T>> int kmax(T[] array, int start, int end, int k) {
if(end-start+1<=k){
return start;
}
if(start<end){
int pos = partition(array, start, end);
int num = end-pos+1;
if(num == k){
return pos;
}else if(num > k){
return kmax(array, pos+1, end, k);
}else{
return kmax(array, start, pos-1, k-num);
}
}else{
return start;
}
}
测试代码:
@Test
public void testKmax(String[] args) {
Integer[] array = new Integer[] {0, 5, 10, 20, 1, 2, 23, -1, 22, 88, 77, 3 };
int pos = kmax(array, 0, array.length-1, 7);
for(int i=pos; i<array.length; i++){
System.out.print(array[i]+"\t");
}
System.out.println();
quickSort(array, 0, array.length-1);
for(int i=0; i<array.length; i++){
System.out.print(array[i]+"\t");
}
}
解法三: 类似二分查找;
代码:
public static int[] binaryKmax(int[] array, int k){
int[] kArray = new int[k];
// get max & min
int max=array[0], min=array[0];
for(int i=1; i<array.length; i++){
if(array[i]>max){
max = array[i];
}
if(array[i]<min){
min = array[i];
}
}
// binary search k max element
int mid = max, m;
while(max>=min){
mid = min + (int)((max-min)*0.5);
m = mMax(array, mid, max);
if(m == k){
break;
}else if(m>k){
min = mid+1;
}else{
max = mid-1;
k -= m;
}
}
// get k elements
for(int i=0, size=0; i<array.length; i++){
if(array[i]>=mid){
kArray[size++]=array[i];
}
}
return kArray;
}
/**
* 描述:找出[min,max]区间比key不小于key的值的个数;
*
* @param array
* @param key
* @param max
* @return
*/
private static int mMax(int[] array, int key, int max){
int count = 0;
for(int i=0; i<array.length; i++){
if(array[i]<=max && array[i]>=key){
count++;
}
}
return count;
}
测试代码:
@Test
public void testBinaryKmax(){
int[] array = new int[] {1, 5, 10, 20, 1, 2, 23, -1, 22, 88, 77, 3 };
System.out.println("----------排序前--------");
for(int i=0; i<array.length; i++){
System.out.print(array[i]+"\t");
}
System.out.println("\n----------排序后--------");
int[] result = binaryKmax(array, 6);
for(int i=0; i<result.length; i++){
System.out.print(result[i]+"\t");
}
}