Top K 问题的解法:使用快速排序、堆排序思想

什么是 Top K 问题

求(随机的)序列中第 n 大的元素?
求(随机的)序列中前 n 大的元素, 或者说 最大的 n 个元素?
反之求第 n 小的,最小的 n 个元素,都可以说是 Top K 问题

以下讨论的求 最大的情况


快速排序解

快排的思想:
每次指定序列中的一个(比如是首位的)值,通过一次快排,返回的索引之前的值就是大于(或等于) 指定值的,之后的就是小于指定值的。
那么要解决 最大 n 的问题:可以在递归函数中,判断 目标 n 的索引 是在 快排函数的返回索引值的 前面还是后面,而不需要完整的递归调用,以至完全排序。
代码:

int destIndex = n-1;
private void quickSort(int[] ary, int l, int r) {
	if (l >= r)
		return;
	int p = qs(ary, l, r);
	//比较 目标索引,与 qs 返回索引的大小
	if (destIndex == p) {
		//return;
    } else if (destIndex > p) {
		quickSort(ary, p + 1, r);
    } else {//destIndex < p
		quickSort(ary, l, p - 1);
    }
}
private void qs(int[] ary, int l, int r) {
... //省略一次快排实现
}

堆排序解

堆排序思想:
要倒序排序,这时要先创建的是小顶堆。然后循环中交换首尾,再下移; 每次下移时,末端是较小的元素,这样末端就是有序的;再继续循环至完成,就成了 从大到小的 倒序序列了。
那么要解决 最大 n 的问题:就只需要反过来,用堆排-顺序排序来解决。
这样,堆排的过程中,保持了序列尾部向前,是从大到小的序列。

int destIndex = n - 1;
public void heapSort(int[] ary) {
        long startTime = System.nanoTime();

        int len = ary.length;
        for (int i = (len - 1) / 2; i >= 0; i--) {
            siftDown(ary, i, len - 1);
        }
        //限定 i 的区间,这样只循环  destIndex+1次
        for (int i = len - 1; i >= len -1 - destIndex; i--) {
            Util.swap(ary, 0, i);
            siftDown(ary, 0, i - 1);
        }


        System.out.println("total time:" + ((System.nanoTime() - startTime) / 1.0e9));
    }

    private void siftDown(int[] ary, int start, int end) { //省略下移实现
    }

如上,交换+下移的循环, 只要执行 n 次就可以了, 即 destIndex+1次。

你可能感兴趣的:(数据结构和算法)