TopK问题

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_queue
vector<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;
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值