347-前 K 个高频元素

347-前 K 个高频元素

topk (前k大)用小根堆,维护堆大小不超过 k 即可。每次压入堆前和堆顶元素比较,如果比堆顶元素还小,直接扔掉,否则压入堆。检查堆大小是否超过 k,如果超过,弹出堆顶。复杂度是 nlogk
避免使用大根堆,因为你得把所有元素压入堆,复杂度是 nlogn,而且还浪费内存。如果是海量元素,那就挂了。

[注意]

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

  • topk 复杂度不是 klogk,是 nlogk.

  • 建堆,假设数组大小是 n, 执行 n 次 sink 操作,每次操作复杂度是 logn,因此建堆复杂度是 nlogn.

  • 插入,logn,上浮操作。

  • 删除(堆顶),一次 sink 操作,logn.

// 利用i 这个trick,记录了最大出现的频率值,然后逐渐减少i, 直到找到第二小的频率值, 但是这个方法适用于频率值差异比较小的情况,如果频率值差异很大, 那么会无用的遍历map很多遍,而找不到对应的频率值
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> m;
        vector<int> res;
        int i=0;
        for(auto num:nums)
            i = max(i,++m[num]);
        
        while(res.size()<k){
            for(auto p:m){
                if(p.second == i)
                    res.push_back(p.first);
            }
            --i;
        }
      return res;   
    }
};

优先队列

**

  • 有限队列说到底还是队列,只不过优先队列的top指向优先级比较高的元素,而队列的top就是指向队首;

  • 如果要取前k个最大的元素,则需要优先队列最小堆排序,既top永远指向最小的元素,如果新添加的元素大于当前队列最小的元素,则出队列,并且push新元素,这样遍历一遍后,保证队列中的元素为前k个最大的元素

  • 如果要取前k个最小的元素,则需要优先队列最大堆排序,既top指向最大的元素,如果新添加的元素小于最大元素,则出队列,push新元素,这样保证队列中为最小的元素

// 利用优先队列的特性,首先弹出优先级比较高的元素
vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> m;
        vector<int> res;
        int i=0;
        for(auto num:nums)
            m[num]++;
         priority_queue< pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>> > pq;
         
         for(auto p:m){
             if(pq.size() == k){
                 
                 if(p.second > pq.top().first){
                     pq.pop();
                     pq.push( make_pair(p.second,p.first));
                 }
                 
             }else{
                 pq.push( make_pair(p.second,p.first));
             }   
         }
         
         while(!pq.empty()){
             res.push_back(pq.top().second);
             pq.pop();
         }
         
      return res;   
    }
   

优先队列的使用

优先队列低层默认为最大堆,既队首对排序最大的元素 在优先队列中,优先级高的元素先出队列。

它的模板声明带有三个参数,priority_queue<Type, Container, Functional>

  • Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。
  • Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list.
  • STL里面默认用的是 vector. 比较方式默认用 operator< , 所以如果你把后面俩个参数缺省的话,优先队列就是大顶堆,队头元素最大。
//priority_queue <> 元素,
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
struct Node{
	int x,y;
	Node(int a=0, int b=0):
		x(a), y(b) {}
};
 
struct cmp{
	bool operator()(Node a, Node b){
		if(a.x == b.x)	return a.y>b.y;
		return a.x>b.x;
	}
};
 
int main(){
	priority_queue<Node, vector<Node>, cmp>p;
	
	for(int i=0; i<10; ++i)
		p.push(Node(rand(), rand()));
		
	while(!p.empty()){
		cout<<p.top().x<<' '<<p.top().y<<endl;
		p.pop();
	}//while
	//getchar();
	return 0;

根据字符出现频率排序

输入: "tree"
输出: "eert"

解释:
'e'出现两次,'r''t'都只出现一次。
因此'e'必须出现在'r''t'之前。此外,"eetr"也是一个有效的答案。
class Solution {
public:
    string frequencySort(string s) {
        int i=0;
        unordered_map<char,int> m;
        for(auto c:s)
             i = max(i,++m[c]);
        string res = "";
        while(res.size()<s.size()){
            
            for(auto p:m){
                if(p.second == i){
                    for(int j=0;j<i;j++)
                        res+=p.first;
                }
            }
            --i;
        }
        
        return res;
    }
};

692. 前K个高频单词

给一非空的单词列表,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

示例 1:

输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i""love" 为出现次数最多的两个单词,均为2次。
    注意,按字母顺序 "i""love" 之前。
class Solution {
public:
    
    struct cmp
    { 
        bool operator()(pair<string,int>a,pair<string,int>b){
            if(a.second<b.second)
                return true;
            else if(a.second>b.second)
                return false;
            return a.first>b.first;
        }    
    };
    
    vector<string> topKFrequent(vector<string>& words, int k) {
        vector<string> res;
        map<string,int> m;
        for(auto s:words)
            m[s]++;
        priority_queue< pair<string,int>, vector<pair<string,int>>,cmp > pq;
        
        for(auto p:m)
            pq.push(p);
        
        int index=0;
        while(!pq.empty()&&index++!=k){
            res.push_back(pq.top().first);
            pq.pop();
        }
        return res;
	}
};

973 最接近0的点

解法1

// 直接修改sort排序函数的 comp比较函数,然后取前k个最小的元素
class Solution {
public:
    
    static bool cmp(vector<int>&a, vector<int>&b) {        //排序方法
		 return sqrt(pow(a[0], 2) + pow(a[1], 2)) < sqrt(pow(b[0], 2) + pow(b[1], 2));
	 }
    
    vector<vector<int>> kClosest(vector<vector<int>>& points, int K) {
        vector<vector<int>> res;
        sort(points.begin(), points.end(), cmp);
        return vector<vector<int>>(points.begin(), points.begin() + K);
    }
};

class Solution {
public:
    
    struct cmp{
         bool operator()(vector<int> a,vector<int> b){
             return sqrt(pow(a[0], 2) + pow(a[1], 2)) < sqrt(pow(b[0], 2) + pow(b[1], 2));
         }  
     };
    
    vector<vector<int>> kClosest(vector<vector<int>>& points, int K) {
        vector<vector<int>> res;
         priority_queue< vector<int>, vector<vector<int>>, cmp > pq;
        
         for(int i=0;i<points.size();i++){
            if(pq.size() < K)           
                pq.push(make_pair(distance2(points[i]), i));
            else if(distance2(points[i]) < pq.top().first)
            {
                pq.pop();
                pq.push(make_pair(distance2(points[i]), i));
            }
        }
        
        int index = 0;
        while(!pq.empty()&&index++!=K){
            res.push_back(points[pq.top().second]);
            pq.pop();
        }
        return res;
    }
};

1046. 最后一块石头的重量

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
使用优先队列 动态保持数组有序

不使用优先队列的话,每次更新都需要重新sort

class Solution {
public:
    int lastStoneWeight(vector<int>& stones) {
        priority_queue<int> mp;
        for(int i:stones)
            mp.push(i);
        
        while(mp.size() > 1){
            int x = mp.top();
            mp.pop();
            int y = mp.top();
            mp.pop();
            if(x==y) continue;
            if(x!=y) mp.push(x-y);
        }
        if(!mp.empty()) return mp.top();
        else return 0;
    }
};


215. 数组中的第K个最大元素

c++ 快排 每个案列都随机化一下 避免极端案例

记得快排前,对数组随机化一下,避免极端案例,退化成 O(N^2)冒泡算法

非递归

class Solution {
public:
    int partition(vector<int>& nums,int left,int right){
        int randIndex = rand()%(right-left+1)+left;
        swap(nums[left], nums[randIndex]);
        int key = nums[left];

        while(left<right){
            while(left<right && nums[right] >= key) right--;
            nums[left] = nums[right];
            while(left<right && nums[left] <= key) left++;
            nums[right] = nums[left];
        }
        nums[left] = key;
        return left;
    }

    int findKthLargest(vector<int>& nums, int k) {
        
        int len = nums.size();
        if(!len) return 0;

        srand(time(NULL));

        typedef pair<int,int> P;
        stack<P> ms;
        ms.push(P(0,len-1));
        while(!ms.empty()){
            int left = ms.top().first;
            int right = ms.top().second;
            ms.pop();
            if(left > right) continue;
            
            int mid = partition(nums,left,right);
            if(mid == len-k){
                return nums[mid];
                break;
            }
            if(mid < len-k) ms.push(P(mid+1,right));
            else ms.push(P(left,mid-1));
        }

        return -1;
    }
};

递归

class Solution {
public:
    int partition(vector<int>& nums,int left,int right){
        int randIndex = rand()%(right-left+1)+left;
        swap(nums[left], nums[randIndex]);
        int key = nums[left];

        while(left<right){
            while(left<right && nums[right] >= key) right--;
            nums[left] = nums[right];
            while(left<right && nums[left] <= key) left++;
            nums[right] = nums[left];
        }
        nums[left] = key;
        return left;
    }

    int dfs(vector<int>& nums, int left,int right,int k){
        if(left > right) return -1;
        int mid = partition(nums,left,right);
        if(mid == nums.size()-k) return nums[mid];
        else if(mid < nums.size()-k) return dfs(nums,mid+1,right,k);
        else return dfs(nums,left,mid-1,k);
    }

    int findKthLargest(vector<int>& nums, int k) {
        
        int len = nums.size();
        if(!len) return 0;

        srand(time(NULL));
        return dfs(nums,0,len-1,k);
    }
};

小顶堆

  • 手动维护一个大小为k小顶堆,最后弹出堆顶元素 即为小顶堆中最小的元素 也是整个数组中第k大的元素
class Solution {
public:
    void adjust_heap(vector<int>& nums,int index){
        int left = 2*index + 1;
        int right = 2*index + 2;
        int len = nums.size();
        int maxindex =index;

        if(left<len && nums[left] < nums[maxindex]) maxindex = left;
        if(right<len && nums[right] < nums[maxindex]) maxindex = right;
        if(index!=maxindex){
            swap(nums[index],nums[maxindex]);
            adjust_heap(nums,maxindex);
        }
    }

    int findKthLargest(vector<int>& nums, int k) {
        int len = nums.size();
        int res = 0;
        if(len<0) return res;
        
        vector<int> tmp(nums.begin(),nums.begin()+k);

        for(int i=tmp.size()/2;i>=0;i--)
            adjust_heap(tmp,i);
        
        for(int i=k;i<len;i++){
            if(nums[i] > tmp[0]){
                tmp[0] = nums[i];
                adjust_heap(tmp,0);
            }
        }
        return tmp[0];
    }
};


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值