当前位置: 首页 > article >正文

优先级队列算法

1046. 最后一块石头的重量

题目链接:1046. 最后一块石头的重量

题目解析

题目的意思大致就是给一堆石头(数组),每次从里面选出两块最大的石头(最大的数)xy

  • 如果x == yxy粉碎(删除xy
  • 如果x!=y,较小的粉碎,较大的减去小石头的重量

返回最终的碰撞结果1块(返回重量)或者0块石头

算法原理

每次选2个最大的数进行比较,这正好符合大根堆的数据结构,即用堆来模拟

  1. 创建大根堆
  2. 数据丢入大根堆
  3. 拿出2次大根堆堆顶元素,碰完之后,如果还有一个没碎,丢入大根堆

代码实现

class Solution {
public:
    int lastStoneWeight(vector<int>& stones)
    {
        //默认大根堆
        priority_queue<int> heap(stones.begin(), stones.end());

        while(heap.size() > 1)
        {
            int x = heap.top();
            heap.pop();
            int y = heap.top();
            heap.pop();
            if(x > y)
            {
                x -= y;
                heap.push(x);
            }
        }
        return heap.empty() ? 0 : heap.top();
    }
};

703. 数据流中的第 K 大元素

题目链接:703. 数据流中的第 K 大元素

题目解析

目的是要找出排序之后的第k大元素(包含相同元素)

要实现一个类KthLargest,它的构造函数来构造数据流,add插入数据流然后返回第k大的元素

image-20240910215841819

算法原理

这考察就是TopK问题,两种主流方式:

  1. 堆(O(N*LogK))
  2. 快速选择算法(O(N))

这题数据是一个一个过来,而用堆解决topK,也是一个一个处理,所以采用堆更优一点

  1. 创建大小为K的堆
    第K大:小根堆
    第K小:大根堆
  2. 循环:
    元素依次进堆
    判定堆的大小是否超过K

关于TopK问题,可以查看此篇文章:数据结构——二叉树

这篇更棒求解TopK问题的三种境界(漫画版)

代码实现

class KthLargest {
public:
    int k;
    //第k大, 小根堆
    priority_queue<int ,vector<int>, greater<int> > heap;
    KthLargest(int _k, vector<int>& nums)
    {
        k = _k;
        for(auto e : nums)
        {
            heap.push(e);
            if(heap.size() > k)
            {
                heap.pop();
            }
        }
    }
    
    int add(int val)
    {
        heap.push(val);
        if(heap.size() > k)
        {
            heap.pop();
        }
        return heap.top();
    }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

692. 前K个高频单词

题目链接:692. 前K个高频单词

题目解析

这题也是TopK问题,给一个单词列表,返回前k个出现次数最多的。

这里除了统计次数,还是统计单词的字典序,如果次数相同,按字典序排序。

算法原理

  • 这里可以用排序,统计前k个次数最多的单词,需要重载一下比较函数,如果次数相同,就按照字典序排序。

  • 也可以采用堆解决:

    1. 这里需要知道单词的次数,所以要先预处理原始字符串数组每个单词出现的个数(哈希表);
    2. 然后创建大小为K的堆:
      出现次数键小根堆,字典序大根堆
    3. 循环,元素依次进堆,然后判端
    4. 提取结果

代码实现

排序:

class Solution {
public:
    struct Greater
    {
        bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2)
        {
            return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first <kv2.first);
        }
    };


    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countMap;
        for(const auto& e:words)
        {
            countMap[e]++;
        }

        vector<pair<string,int>> kvVec(countMap.begin(),countMap.end());
        sort(kvVec.begin(),kvVec.end(),Greater());
        vector<string> ret;
        for(int i=0;i<k;i++)
        {
            ret.push_back(kvVec[i].first);
        }
        return ret;
    }
};

堆:

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

        unordered_map<string, int> hash;
        for(const auto& e : words)
        {
            hash[e]++;
        }
        auto cmp = [](const pair<string, int> &p1, const pair<string, int> &p2)
        {
            if(p1.second == p2.second)
            {
                //大根堆
                return p1.first < p2.first;
            }
            //小根堆
            return p1.second > p2.second;
        };
        priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> heap(cmp);
        for(const auto& e : hash)
        {
            heap.push(e);
            if(heap.size() > k)
            {
                heap.pop();
            }
        }
        vector<string> ret(k);
        for(int i = k-1; i >= 0; i--)
        {
            ret[i] = heap.top().first;
            heap.pop();
        }
        return ret;
    }
};

295. 数据流的中位数

题目链接:295. 数据流的中位数

题目解析

题目给了一个有序整数列表,找出中间值,如果列表大小为偶数,中位数是中间两个数的平均值。

让我们实现一个MedianFinder类:

  • MedianFinder() 初始化 MedianFinder 对象
  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中
    数据一个一个添加,确保序列为有序序列
  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5以内的答案将被接受
    目前序列的中位数

算法原理

解法1——直接sort:

插入一个数,排一下序,然后通过元素个数,访问中间下标的元素。

这个add时间复杂度是O(N* logN),find的时间复杂度为O(1),题目数据量很大,会超时

解法2——插入排序思想

采用插入排序,add的时间复杂度为O(N),find时间复杂度为O(1),如果插入数据很大,时间复杂度也很大

解法3——大小堆维护

借助大小根堆来维护这个序列:

image-20240911210037737

此时先规定:

  • 假设左侧为m,右侧为n
  • 要么m == n
  • 要么 m == n+1

此时要求中位数,如果这个序列元素为偶数个,直接(大根堆堆顶元素 + 小根堆堆顶元素)/2即可得到中位数;

如果为奇数个,则是大根堆堆顶元素为中位数。

查找的时间复杂度为O(1),插入的时间复杂度为O(logN)

这里需要主要的时插入数据的时候,如何维护m == nm == n+1

这里分类讨论:

image-20240911211156959

  • m == n
    num <= x || m == 0,插入left
    num > x,插入right,然后让y进入left
  • m == n+1
    num <= x,进入left,此时m比n大2个,需要调整,让x进入right
    num > x,进入right

代码实现

class MedianFinder
{
public:
    //大根堆
    priority_queue<int> left;
    //小根堆
    priority_queue<int, vector<int>, greater<int>> right;
    MedianFinder()
    {}
    
    void addNum(int num)
    {
        if(left.size() == right.size())
        {
            if(left.empty() || left.top() >= num)
            {
                left.push(num);
            }
            else
            {
                right.push(num);
                int tmp = right.top();
                right.pop();
                left.push(tmp);
            }
        }
        else if(left.size() == right.size() + 1)
        {
            if(num <= left.top())
            {
                left.push(num);
                int tmp = left.top();
                left.pop();
                right.push(tmp);
            }
            else
            {
                right.push(num);
            }
        }
    }
    
    double findMedian()
    {
        return left.size() == right.size() ? (left.top() + right.top()) / 2.0 : left.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

http://www.kler.cn/a/306861.html

相关文章:

  • influxDB 时序数据库安装 flux语法 restful接口 nodjsAPI
  • GxtWaitCursor:Qt下基于RAII的鼠标等待光标类
  • 探索Pillow库:Python图像处理的瑞士军刀
  • 智能电视/盒子的应用管理——通过ADB工具优化体验
  • power bi中的related函数解析
  • 开源 2 + 1 链动模式、AI 智能名片、S2B2C 商城小程序在用户留存与品牌发展中的应用研究
  • QUIC的丢包处理
  • 2_foc闭环调试_ADC电流采样与滤波及pid数据结构
  • python 检索与该查询最相似的句子 使用库hflayers和sentence_transformers来实现遇到的问题
  • libwebsockets之日志系统
  • 【C#】vs2022 .net8
  • Leetcode 165. 比较版本号(Medium)
  • 日用放大器
  • 监控binlog日志监控表变化进行消息通知
  • Gartner发布报告揭秘微软数据安全功能和许可
  • (CVPR-2022)感知优先的扩散模型训练
  • 基于51单片机的倒计时音乐播放系统设计
  • Android libui新加接口,编译报错:error: Please update ABI references
  • 鸿蒙开发之ArkTS 基础八 联合类型
  • 搭建Eureka高可用集群 - day03
  • 智能工厂的设计软件 “程序”的完整框架
  • JAVA学习笔记02-integer
  • 二叉树算法
  • 掌握单片机,其实并不难
  • LeetCode70:爬楼梯
  • 【算法】 滑动窗口—最长无重复子串