前K个高频元素

数据结构--堆

  • Heap是一种数据结构具有以下的特点:

    1)完全二叉树;

    2)heap中存储的值是偏序;

  • Min-heap: 父节点的值小于或等于子节点的值;

  • Max-heap: 父节点的值大于或等于子节点的值;

1.堆的存储:

一般都用数组来表示堆,i结点的父结点下标就为(i–1)/2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2

2.堆的操作:insert

插入一个元素:新元素被加入到heap的末尾,然后更新树以恢复堆的次序。
每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,现在的任务是将这个新数据插入到这个有序数据中——这就类似于直接插入排序中将一个数据并入到有序区间中。

3.堆的操作:Removemax

按定义,堆中每次都删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最大的,如果父结点比这个最小的子结点还大说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。

4.堆的操作:buildHeap 堆化数组

对于叶子节点,不用调整次序,根据满二叉树的性质,叶子节点比内部节点的个数多1.所以i=n/2 -1 ,不用从n开始

5.堆排序

堆建好之后堆中第0个数据是堆中最大的数据。取出这个数据再执行下堆的删除操作。这样堆中第0个数据又是堆中最大的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。

priority_queue

  1. priority_queue允许用户为队列中元素设置优先级,放置元素的时候不是直接放到队尾,而是放置到比它优先级低的元素前面,标准库默认使用<操作符来确定优先级关系。

  2. priority_queue模板类有三个模板参数:元素类型,容器类型,比较算子。其中后两个可以省略,默认容器为vector,默认算子为less,即如果x<y判断为真,则y排到x前面,y先出队。

  3. priority_queue<int,vector,greater> q3;//定义优先级小的先出队

  4. priority_queue与queue的基本操作均相同,在使用q.top()时返回最高优先级的元素,该操作只适用于优先级队列。

pair

  1. pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。

  2. make_pair函数:

     template pair make_pair(T1 a, T2 b) { return pair(a, b); }
    

很明显,我们可以使用pair的构造函数也可以使用make_pair来生成我们需要的pair。 一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象很方便,代码也很清晰。 另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。

类模板:template <class T1, class T2> struct pair
参数:T1是第一个值的数据类型,T2是第二个值的数据类型。
功能:pair将一对值组合成一个值,这一对值可以具有不同的数据类型(T1和T2),两个值可以分别用pair的两个公有函数first和second访问。

  1. 生成新的pair对象

可以使用make_pair对已存在的两个数据构造一个新的pair类型:

int a = 8; 
string m = "James"; 
pair<int, string> newone; 
newone = make_pair(a, m);

using 别名指定

在C++11中提出了通过using指定别名

using value_type = _Ty

以后使用value_type value; 就代表_Ty value;

#include <bits/stdc++.h>

开始今天的题目

给定一个非空的整数数组,返回其中出现频率前 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) , n 是数组的大小。

解析:

topk (前k大)用小根堆,维护堆大小不超过 k 可。每次压入堆前和堆顶元素比较,如果比堆顶元素还小,直接扔掉,否则压入堆。检查堆大小是否超过 k,如果超过,弹出堆顶。复杂度是 nlogk

避免使用大根堆,因为你得把所有元素压入堆,复杂度是 nlogn,而且还浪费内存。如果是海量元素,那就挂了。

[注意]
求前 k 大,用小根堆,求前 k 小,用大根堆。面试的时候如果说反了会挂!

代码

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {

      using  E =  pair<int,int>;
       priority_queue<E,vector<E>,greater<E>> minHeap;//优先级小的在栈顶,即值小的先出栈
        map<int,int> counter;
        for(auto e :nums){
            ++counter[e];//记录出现的频率
        }
        for(auto & x:counter){//加个引用如果需要改变X的值就必须得有
            auto pair  = make_pair(x.second,x.first);
            if(minHeap.size() == k){//保证前K
                if(pair < minHeap.top()) continue;
                minHeap.pop();
            }
            minHeap.push(pair);
            
        }
        vector<int> res (minHeap.size());
        k = res.size();
        while(!minHeap.empty()){
            res[--k] = minHeap.top().second;
            minHeap.pop();
        }
        return res;
        
    }
};
原文地址:https://www.cnblogs.com/gzyc/p/10724310.html