面试题 17.14. 最小K个数(TOP-K问题,堆排,快排)

面试题 17.14. 最小K个数

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

提示:

0 <= len(arr) <= 100000
0 <= k <= min(100000, len(arr))

解题思路: 这是经典的TOP-K问题,大致的解法主要有堆排序,快速排序。堆排序的时间复杂度是 O ( n l o g k ) O(nlogk) O(nlogk).下面先用堆排序解再用快速排序解题。TOP-K问题这篇文章讲的非常清楚,可以参考。4种解法秒杀TopK(快排变形/堆/二叉搜索树/计数排序)
堆排的时间复杂度: O ( n l o g k ) O(nlogk) O(nlogk),快排时间复杂度: O ( n ) O(n) O(n).

  1. 堆排序
class Solution {
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        priority_queue<int> q;
        for (int &a : arr) {
            q.push(a);
            if (q.size() > k) q.pop();
        }
        vector<int> res;
        while (!q.empty()) {
            res.push_back(q.top()); q.pop();
        }
        return res;
    }
};
  1. 快排

用快排解此题时,需要对快排模板非常熟,快排的基本思路是在一次排序之后,大于某个值的序列全在它的右边,而小于这个值的序列全部位于他的左边。具体的步骤是,每次选序列的第一个元素(val)作为参考值,然后两个指针序列的两端(left,right),然后left向后移动直至找到比val大的数,right向前移动直至找到比val小的数,然后将这两个数交换,最后right停留的位置的数小于等于val,将它与参考值交换,参考值交换到right处即可达到此次排序后小于val的值在他左边,而大于val的值在它右边。那么用快排解决TOP-K的问题是这样的,因为快排的核心是每次能够确定第j大的数,那么如果我们某次迭代时找到的j

注:快排模板写在下面附录中。

class Solution {
public:
    int partition(vector<int>&arr, int lo, int hi) {
        int v = arr[lo];
        int left = lo, right = hi + 1;
        while (true) {
            while (++left <= hi && arr[left] < v);
            while (--right >= lo && arr[right] > v);
            if (left >= right) break;
            swap(arr[left], arr[right]);
        }
        arr[lo] = arr[right];
        arr[right] = v;
        return right;
    }
    vector<int> quickSearch(vector<int> &arr, int lo, int hi, int k) {
        int j = partition(arr, lo, hi);
        if (j == k) return vector<int>(arr.begin(), arr.begin() + j + 1);
        return j > k ? quickSearch(arr, lo, j - 1, k) : quickSearch(arr, j + 1, hi, k);
    }
    vector<int> smallestK(vector<int>& arr, int k) {
        if (k == 0 || arr.empty()) return {};
        return quickSearch(arr, 0, arr.size() - 1, k - 1);
    }
};

快排模板:

#include 
#include 
using namespace std;

int partition(vector<int> &nums, int lo, int hi) {
	int v = nums[lo];
	int i = lo, j = hi + 1;
	while (true) {
		while (++i <= hi && nums[i] < v) ;
		while (--j >= lo && nums[j] > v) ;
		if (i >= j) break;
		swap(nums[i], nums[j]);
	}
	nums[lo] = nums[j];
	nums[j] = v;
	return j;
}

void quickSearch(vector<int> &nums, int lo, int hi) {
	if (lo > hi) return;
	int j = partition(nums, lo, hi);
	quickSearch(nums, lo, j - 1);
	quickSearch(nums, j + 1, hi);
}

int main() {
	vector<int> v = {1,5,2,7,23};
	quickSearch(v, 0, v.size() - 1);
	for (auto t : v) {
		cout << t << ",";
	}
	return 0;
} 

你可能感兴趣的:(leetcode)