欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

topK问题

程序员文章站 2022-03-24 17:54:49
...
import com.again.common.arithmetic.Sort.HeapSort;
import com.google.common.base.Preconditions;

import java.util.Arrays;

/**
 * TopK问题:在指定数据中查找最大(或最小)的k个数。
 */
public class TopK {

    /**
     * 打印出数组中最小的k个数
     *
     * 优点:时间复杂度为O(n)
     * 缺点:改变了原数组中元素的位置
     *
     * @param array
     * @param k
     */
    public static void getLeastElements(int[] array, int k) {

        Preconditions.checkArgument(null != array && k > 0 && array.length > k);

        int left = 0;
        int right = array.length - 1;

        int indexOfK = k - 1;

        Partition.makePivotIndexEqualsK(array, left, right, indexOfK);

        for (int i = 0; i <= indexOfK; i++) {
            Partition.print(array[i]);
        }
    }


    /**
     * 打印出数组中最大的k个数
     *
     * 优点:时间复杂度为O(n)
     * 缺点:改变了原数组中元素的位置
     *
     * @param array
     * @param k
     */
    public static void getLargestElements(int[] array, int k) {

        Preconditions.checkArgument(null != array && k > 0 && array.length > k);

        int left = 0;
        int right = array.length - 1;

        int indexOfK = array.length - k;

        Partition.makePivotIndexEqualsK(array, left, right, indexOfK);

        for (int i = indexOfK; i < array.length; i++) {
            Partition.print(array[i]);
        }
    }


    /**
     * 打印出数组中最小的k个数
     *
     * 思路:
     *  1)创建一个大小为k的辅助数组来存储数组中最小的k个数,用数组的前k个元素来初始化辅助数组。
     *  2)使用堆排序来对辅助数组进行排序,故排序后,辅助数组的最后一个元素即最大值。
     *  3)从数组的第k+1个元素开始遍历数组,若遍历的元素小于辅助数组中的最大值,则将辅助数组中的最大值替换为当前遍历的元素。
     *
     * 时间复杂度为O(nlogk)
     *      遍历的数组的时间复杂度为O(n)
	 *		对长度为k的辅助数组进行排序的时间复杂度为O(klogk),这里我们并不需要对辅助数组的k个元素都进行排序,我们只需要将k个元素中个的最大值放到辅助数组的最后一个位置即可(即排序时只调整一次即可),故时间复杂度为O(logk)
     *      故:总的时间复杂度 = O(n)*O(logk) = O(nlogk)
     *      
     * 优点:
     *  1)不需要改变数组中元素的位置。
     *  2)适合在海量数据中查找topk:
     *      1>若输入的数据量很大,则很可能无法一次性将所有的数据都加载到内存中,此时就无法利用Partition函数来求topk的问题了。
     *      2>若输入的数据量很大,我们每次从输入源(eg:文件)中获取一个数字,然后对该数字进行判断即可。故这种方式仅仅要求内存能容纳下 一个大小为k的辅助数组 即可。
     *
     * @param array
     * @param k
     */
    public static void getLeastElementsByHeapSort(int[] array, int k) {

        Preconditions.checkArgument(null != array && k > 0 && array.length > k);

        // 用数组的前k个元素来初始化辅助数组
        int[] heapArray = Arrays.copyOfRange(array, 0, k);

        // 使用堆排序来对辅助数组进行排序
        HeapSort heapSort = new HeapSort();
        heapSort.sort(heapArray);

        // 从数组的第k+1个元素开始遍历数组,若遍历的元素小于辅助数组中的最大值,则将辅助数组中的最大值替换为当前遍历的元素。
        for (int i = k; i < array.length; i++) {
            if (array[i] < heapArray[k - 1]) {
                heapArray[k - 1] = array[i];
                heapSort.sort(heapArray);
            }
        }
        System.out.println(Arrays.toString(heapArray));
    }


    public static void main(String[] args) {


        int[] array = {55, 2, 3, 4, 5, 6, 7, 8, 1, 9};
        getLeastElementsByHeapSort(array, 5);
        getLeastElements(array, 5);

        System.out.println("");
    }

}