C++之TopK求解

TopK问题,就是找到一堆数据中最大/最小、出现频率最高等问题。现在想一下其实解决TopK问题的话,有一下几个想法:
1、直接各种排序,前提是内存足够的话,如果数据量极大,那就不太抗得住了。
2、最大最小堆。保留K个值,然后其他值与之比较,对最大/最小堆进行增加删除操作。
3、分治思想。把所有数据分成无数的k段,然后再合并不同的段称为新的k段,直到最后剩下一个段,即为所求。
4、hash法

下面就用实例来上手一下:

Leetcode347. 前 K 个高频元素
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

class Solution {
public:
    //此时要定义为静态函数,否则在类中无法直接调用
    static bool cmp(pair<int,int> m1,pair<int,int> m2)
        {
            return m1.second>m2.second;
        }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        //用map来统计每个元素的个数
        map<int,int> m;
        for(int i=0;i<nums.size();i++)
        {
            m[nums[i]]++;
        } 
        //将每个map放入vector中(用迭代器),便于后续的按数量多少进行排序
        vector<pair<int,int>> v;
        map<int,int>::iterator it;
        for(it=m.begin();it!=m.end();it++)
        {
            v.push_back(*it);
        }
        //依据自定义的静态函数,进行排序,数量多的在前面
        sort(v.begin(),v.end(),cmp);
        //将数量topk元素放入结果向量中
        vector<int> res;
        for(int i=0;i<k;i++)
        {
            res.push_back(v[i].first);
        }
        return res;
    }
};

Leetcode692. 前K个高频单词
给一非空的单词列表,返回前 k 个出现次数最多的单词。
**注意:**返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

输入: [“i”, “love”, “leetcode”, “i”, “love”, “coding”], k = 2
输出: [“i”, “love”]
解析: “i” 和 “love” 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 “i” 在 “love” 之前。

class Solution {
public:
    static bool cmp(pair<string,int> m1,pair<string,int> m2)
    {
        if(m1.second==m2.second) return m1.first<m2.first;
        return m1.second>m2.second;
    }
    
    vector<string> topKFrequent(vector<string>& words, int k) {
        
        map<string,int> m;
        for(int i=0;i<words.size();i++)
        {
            m[words[i]]++;
        }

        vector<pair<string,int>> v;
        map<string,int>::iterator it;
        for(it=m.begin();it!=m.end();it++)
        {
            v.push_back(*it);
        }

        sort(v.begin(),v.end(),cmp);

        vector<string> v1;
        for(int i=0;i<k;i++)
        {
            v1.push_back(v[i].first);
        }

        return v1;
    }
};

可以看出,这和第一题的答案几乎相同,只是自定义的比较函数考虑的数量相同情况下的顺序问题。所以这里也不需要再注释了。

下面利用最大最小堆来解决TopK问题:

构造大顶堆:

//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;   
//另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;   

构造小顶堆:

//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;   

需要注意的是,如果使用less和greater,需要头文件:

#include <functional>

剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> v(k,0);
        if (k == 0) return v; // 排除 0 的情况
        //priority_queue<int> q;构造大顶堆的两种方式
        priority_queue<int,vector<int>,less<int>> q;
        for(int i=0;i<k;i++)
        {
            q.push(arr[i]);
        }
        for(int i=k;i<arr.size();i++)
        {
            if(arr[i]<q.top())
            {
                q.pop();
                q.push(arr[i]);
            }
        }
        for (int i = 0; i < k; ++i) {
            v[i] = q.top();
            q.pop();
        }
        return v;
    }
};

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是使用 Prim 算法求解带权连通图中最小生成树的 C++ 代码: ```c++ #include <iostream> #include <vector> #include <queue> #include <cstring> using namespace std; const int N = 1e5 + 10, INF = 0x3f3f3f3f; int head[N], ver[N << 1], edge[N << 1], nxt[N << 1], idx; bool st[N]; int dist[N], parent[N]; struct Node { int idx, dist; bool operator<(const Node &t) const { return dist > t.dist; } }; void add(int a, int b, int c) { ver[++idx] = b, edge[idx] = c, nxt[idx] = head[a], head[a] = idx; } void prim(int n) { memset(dist, INF, sizeof(dist)); dist[1] = 0; priority_queue<Node> q; q.push({1, 0}); while (q.size()) { auto t = q.top(); q.pop(); int ver = t.idx; if (st[ver]) continue; st[ver] = true; for (int i = head[ver]; i != -1; i = nxt[i]) { int j = ver[i], k = edge[i]; if (!st[j] && dist[j] > k) { dist[j] = k; parent[j] = ver; q.push({j, dist[j]}); } } } } int main() { int n, m; cin >> n >> m; memset(head, -1, sizeof(head)); for (int i = 0; i < m; i++) { int a, b, c; cin >> a >> b >> c; add(a, b, c); add(b, a, c); } prim(n); int sum = 0; for (int i = 2; i <= n; i++) { sum += dist[i]; } cout << sum << endl; return 0; } ``` 其中,`add()` 函数用于建图,`prim()` 函数实现 Prim 算法,`main()` 函数读入数据,并输出最小生成树的权值和。 在 `prim()` 函数中,我们使用了一个优先队列来维护当前距离集合最近的点。每次取出队首元素,然后遍历其所有相邻的边,将未被访问过的顶点加入队列中。如果该顶点到集合的距离比之前已经记录的更近,则更新该顶点到集合的距离,并记录下其父节点。最后,遍历完所有的顶点,就得到了最小生成树。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值