TopK问题

来源:互联网 发布:unity3d室内插件 编辑:程序博客网 时间:2024/05/16 18:18

1. 无序数组找最大的K个数

方法1: 排序+查找 时间复杂度O(NlogN)

int findKthLargest(vector<int> & nums, int k) {    sort(nums.begin(), nums.end());    return nums[k-1];}


方法2: 类似快排的分治思想 O(N)

int partition(vector<int> &nums, int left, int right) {    int l = left, r = right;    int pivot = nums[l];    while (l < r) {        while (l < r && nums[r] <= pivot)            r--;        nums[l] = nums[l];        while (l < r && nums[l] >= pivot)            l++;        nums[r] = nums[l];    }    nums[l] = pivot;    return l; }int findKthLargest_QuickSort(vector<int> nums, int k) {    int left = 0, right = nums.size() - 1;    while (true) {        int pos = partition(nums, left, right);        if (pos == k - 1) return nums[pos];        if (pos > k - 1) right = pos;        else left = pos;    }}

方法3:堆排序思想,调整堆k次

void max_heapify(vector<int> &nums, int p, int heap_size) {    int child = 2 * p + 1;    int p_val = nums[p];    while (child < heap_size) {        if (child + 1 < heap_size && nums[child] < nums[child+1]) {            child += 1;        }        if (nums[child] > p_val) {            nums[p] = nums[child] ;            p = child;            child = 2 * p + 1;        } else             break;    }    nums[p] = p_val;}void build_max_heap(vector<int> &nums, int heap_size) {    int heap_size = nums.size();    for (int i = (heap_size >> 1) -1; i >= 0; i--) {        max_heapify(nums, i, heap_size);    }}int findKthLargest_Heap(vector<int> nums, int k) {    build_max_heap(nums, heap_size);    for (int i = 0; i < k; i++) {        swap(nums[0], nums[heap_size-1]);        heap_size --;        max_heapify(nums, 0);    }    return nums[heap_size];}

方法4: 利用STL的priority_queue

int findKthLargest(vector<int>& nums, int k) {        priority_queue<int> pq(nums.begin(), nums.end());        for (int i = 0; i < k - 1; i++) {            pq.pop();        }        return pq.top();    }

2. 无序数组找K最频繁(要求时间复杂度小于O(NlogN))

因为k最大可以等于n,所以在最坏情况下需要统计所有数字的出现次数。那么这个问题就分成了两个部分:
  1. 统计所有不同的数字出现的次数
  2. 找出出现次数前k大的数字
因为最后只需要返回k个数字,所以可以维护一个大小为k的小根堆。当新的数字出现的次数大于堆中最小的次数时,对堆进行更新。时间复杂度是O(n log k)。
// hash + priority_queuevector<int> topKFrequent(vector<int>& nums, int k) {        unordered_map<int,int> map;        for(int num : nums){            map[num]++;        }                vector<int> res;        // pair<first, second>: first is frequency,  second is number        priority_queue<pair<int,int>> pq;         for(auto it = map.begin(); it != map.end(); it++){            pq.push(make_pair(it->second, it->first));            if(pq.size() > (int)map.size() - k){                res.push_back(pq.top().second);                pq.pop();            }        }        return res;    }

可以采用桶排序做进一步优化,该算法复杂度只和需要排序的数字的大小有关。可以用一个数组bucket记录每个数字出现的次数,每次把数字丢到相应编号的桶中,然后从后往前穷举每一个桶,取出其中的元素直到取满k个。时间复杂度是O(n)。
vector<int> topKFrequent(vector<int>& nums, int k) {        vector<int> res;        if (!nums.size()) return res;        unordered_map<int, int> cnt;        for (auto num : nums) cnt[num]++;        vector<vector<int>> bucket(nums.size() + 1);        for (auto kv : cnt) {            bucket[kv.second].push_back(kv.first);        }        for (int i = bucket.size() - 1; i >= 0; --i) {            for (int j = 0; j < bucket[i].size(); ++j){                res.push_back(bucket[i][j]);                if (res.size() == k) return res;            }        }        return res;    }


0 0
原创粉丝点击