代码随想录:前K个高频元素

代码随想录:前K个高频元素

代码随想录链接:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html#%E6%80%9D%E8%B7%AF

力扣链接:https://leetcode.cn/problems/top-k-frequent-elements/

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

示例 1:

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

示例 2:

  • 输入: nums = [1], k = 1
  • 输出: [1]

提示:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O ( n log ⁡ n ) O(n \log n) O(nlogn) , n 是数组的大小。
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
  • 你可以按任意顺序返回答案。

1、优先队列priority_queue的相关知识

    priority_queue是不允许随机访问,只能访问队列首部的元素,也只能对首部元素进行出队。在优先队列中,会按照一定的方式对队列中的元素进行排列。

    这是priority_queue类的模板参数列表,里面有三个参数:class T,class Container = vector,class Compare = less

template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> >

class T:T是优先队列中存储的元素的类型,可以是int、double,也或者自定义的数据类型struct、class等;
class Container = vector<T>:Container是优先队列底层使用的存储结构,默认采用vector;
class Compare = less<typename Container::value_type>:Compar是定义优先队列中元素的比较方式的类,即如何对元素进行排列。

    对元素排列的方式,可以是大顶堆(从大到小排列),或者小顶堆(从小到大排列),如不指明,则默认为大顶堆。

less<储存的数据类型> 即使用大顶堆
greater<储存的数据类型> 即是用小顶堆

    priority_queue的定义和使用

priority_queue<Typename, Container<Typename>, Compare> queue_name;

priority_queue<int> q;//储存int型数据,容器默认为vector,比较方式默认为大顶堆
priority_queue<string> q;//储存string型数据,容器默认为vector,比较方式默认为大顶堆
priority_queue<int,vector<int>,less> q;//使用大顶堆储存int型数据 
priority_queue<int,vector<int>,greater> q;//使用小顶堆储存int型数据

2、class Compare的实现

    class Compare就是定义了一个类,在这个类中定义了一个成员函数,这个成员函数重载了’()'运算符,该成员函数的函数名为operator(),这里的()代表了被重载的运算符。这种类的定义,也叫做仿函数。仿函数就是假函数,它是把对象当作函数使用,所以也称为函数对象。因为普通函数在某些特殊场景下使用比较麻烦,所以就诞生了仿函数。

例如,大顶堆的less类的定义如下

template <class T> 
    struct less : binary_function <T,T,bool> {
      bool operator() (const T& x, const T& y) const {return x<y;}
};

当数据类型为int型时,可以转化为如下代码:

class Less {
public:
	bool operator()(const int a,const int b) const{
		return a < b;
	}
};

此时创建一个class Less的对象,并调用函数名为‘operator()’的成员函数,

Less lessCompare;	//创建对象

lessCompare.operator()(2,8);	//原本的调用格式,但是在C++中会省略'.operator()',使用简化的方式进行调用,
lessCompare(2,8);

    这情况下,就实现了把对象当作函数使用,它其实就是因为C++重载运算符时,对象调用成员函数省略引用符号’.'和成员函数名‘operator()’造成的,实际上的代码逻辑仍然是对象调用成员函数。

priority_queue的class Compare比较类是使用仿函数来实现的,当我们自定义比较类时,也需要按照这种格式。因此,小顶堆类的定义如下:

// 小顶堆
class mycomparison {
public:
    bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
        return lhs.second > rhs.second;
    }
};

在本题中,需要找到出现频率最高的数字,而数字和频率在哈希表unordered_map中成对存放,比较项是pair<int,int>中的第二项,且是小顶堆,因此左孩子的second值应为更小值,此时返回true。

此时小顶堆的定义如下:

priority_queue<pair<int, int>, vector<pair<int, int>>, mycomparison> pri_que;

参数的类型为pair<int,int>,容器底层使用vector,比较类为自定义的小顶堆mycomparison。

3、代码逻辑

使用unordered_map来统计数组中数字出现的频率,其中key为数值,value是出现的频率:

unordered_map<int, int> map;
for(int i : nums){
    map[i]++;
}

用优先队列定义一个小顶堆pri_que,并使用小顶堆pri_que来存储map中的pair对,并且判断小顶堆的size是否超过了k,因为题目中要求需要计算的是前k的频率,如果超出k,就弹出前面的pair对,保证pri_que中只有k个元素。

priority_queue<pair<int,int>,vector<pair<int,int>>,mycomparison> pri_que;

//用固定大小为k的小顶堆去扫描map,将频率前k的元素存储到pri_que中
for(auto it = map.begin(); it != map.end();it++){
    pri_que.push(*it);
        
    //判断pri_que的个数是否超出k
    if(pri_que.size() > k){
        pri_que.pop();  //弹出front,因为是从小到大排列,所以弹出了较小的频率
    }
}

题目要求的返回前K个高频元素,类型为vector,并且因为小顶堆先弹出的是最小的,所以倒序来输出到固定大小为k的数组

vector<int> result(k);
for(int i = k-1; i >= 0; i--){
    result[i] = pri_que.top().first;
    pri_que.pop();
}

最终返回result即可。

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值