zoukankan      html  css  js  c++  java
  • C++中的堆及常见题目汇总

    目录

    一、 数据结构之堆的基本知识

    1. 完全二叉树
      1. 完全二叉树的概念
      2. 完全二叉树的重要性质
      3. 完全二叉树的存储  
    2. 堆的特性
      1. 最大堆
        1. 最大堆的特性
        2. 最大堆的实现逻辑(包括插入、删除等)  
          1. 插入
          2. 删除    
    3. 基于堆的优先队列
    4. C++中优先队列(priority_queue)详解

    二、数据结构之堆-常见题目汇总

    1. 最小堆
      1. 前k个高频元素  
      2. 前k个高频单词
      3. 剑指offer 40: 最小的K个数
      4. 丑数
      5. 超级丑数
      6. 第k个数
    2. 最大堆
      1. 数组的第k个最大元素  
      2. 有序矩阵中第k小的元素
      3. 最后一块石头的重量
      4. 最小k个数
      5. 重构字符串
      6. 最接近原点的K个点
      7. 查找和最小的K对数字

    一、 数据结构之堆的基本知识

    1. 完全二叉树

     1.1完全 二叉树的概念

     如果一棵树深度为k,有n个结点的二叉树中各节点能与深度为k的顺序编号的满二叉树从1到n标号的节点相对应的二叉树成为完全二叉树,如下图所示

            

       1.2 二叉树的重要性质

      

       

       

       

       1.3 二叉树的存储

    • 线性存储

      线性存储一般仅用于满二叉树和完全二叉树

      • 满二叉树的线性存储

      

      • 一般二叉树的线性存储  

      

     2. 堆的特性

      堆是数据结构中的一类,其可以看做是一颗完全二叉树的数组对象。因此,堆必须是完全二叉树,且用数组实现,任一节点是其子树所有节点的最大值或最小值。

      

      最小堆:完全二叉树,每个节点的元素值不大于其他节点的元素值。

      2.1 最大堆

    • 最大堆的概念  

      最大堆:完全二叉树,每个结点的元素值不小于其子节点的元素值

    • 最大堆的实现逻辑(包括插入、删除等)  
      • 插入

          最大堆的插入操作可以简单看成是“结点上浮”。当我们向最大堆中插入一个结点,我们必须满足完全二叉树的标准,那么被插入节点的位置是固定的。而且要满足父节点关键字值不小于子节点关键字值,我们需要移动父节点和子节点的相互位置关系。

        

      •  删除

        最大堆的删除操作,总是从堆的根节点删除元素。同样根元素被删除之后为了能够保证树还是一颗完全二叉树,我们需要移动完全二叉树的最后一个节点,使其继续符合完全二叉树的定义,从这里可以看做是最大堆最后一个节点的下沉。

     

       此时是满足完全二叉树的要求,但是仍然不满足根节点是最大值的要求,继续调整如下:

      

       在这里已经确定的最大堆中做删除操作,被删除的元素是固定的,需要被移动的节点也是固定的,这里我说的被移动元素是指最初的移动,即最大堆的最后一个元素,移动方式为从最大的节点开始比较。

     3. 基于堆的优先队列

      我们知道,普通的队列的特点是先进先出。先入队列的元素自然会先出队列。

      优先队列将不再遵循先入先出的原则,而是分为两种情况

      最大优先队列,无论入队顺序,都是当前最大的元素优先出队列--基于最大堆

      最小优先队列,无论入队顺序,都是当前最小的元素优先出队列--基于最小堆

      比如有一个最大优先队列,其最大的元素是8,那么虽然元素8不是队首元素,但是出队时仍然让元素8优先出队

      

       要满足上述需求,利用线性数据结构并非不能实现,但是时间复杂度较高,最坏时间复杂度是O(n)并不是最理想的方式,因此我们使用二叉堆来实现优先队列。

      二叉堆的特性

      最大堆:堆顶元素是整个堆中最大元素

      最小堆:堆顶元素是整个堆中最小元素

      入队和出队操作刚刚已经介绍过。

    4. C++中优先队列priority_queue介绍

      优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出(first in, largest out)的行为特征

      priority_queue<Type, Container, Functional>

    • type就是数据类型
    • Container就是容器类型(Container必须是数组实现的容器,如vector,deque等,但不能用list,STL中默认的是vector)
    • Functional就是比较的方式  

      当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大堆顶

      一般是

    1 //升序队列,小堆顶
    2 priority_queue<int, vector<int>,greater<int> > q;
    3 //降序队列,大堆顶
    4 priority_queue<int, vector<int>, less<int> > q;
    • 基本类型优先队列的例子
     1 #include<iostream>
     2 #include <queue>
     3 using namespace std;
     4 int main()
     5 {
     6     //对于基础类型 默认是大顶堆
     7     priority_queue<int> a;
     8     //等同于 priority_queue<int, vector<int>, less<int> > a;
     9 
    10     //      这里一定要有空格,不然成了右移运算符↓↓
    11     priority_queue<int, vector<int>, greater<int> > c;  //这样就是小顶堆
    12     priority_queue<string> b;
    13 
    14     for (int i = 0; i < 5; i++)
    15     {
    16         a.push(i);
    17         c.push(i);
    18     }
    19     while (!a.empty())
    20     {
    21         cout << a.top() << ' ';
    22         a.pop();
    23     }
    24     cout << endl;
    25 
    26     while (!c.empty())
    27     {
    28         cout << c.top() << ' ';
    29         c.pop();
    30     }
    31     cout << endl;
    32 
    33     b.push("abc");
    34     b.push("abcd");
    35     b.push("cbd");
    36     while (!b.empty())
    37     {
    38         cout << b.top() << ' ';
    39         b.pop();
    40     }
    41     cout << endl;
    42     return 0;
    43 }

      运行结果如下:

       

    •  用pair做优先队列元素的例子

      规则:pair的比较,先比较第一个元素,若第一个相等比较第二个

     1 #include <iostream>
     2 #include <queue>
     3 #include <vector>
     4 using namespace std;
     5 int main()
     6 {
     7     priority_queue<pair<int, int> > a;
     8     pair<int, int> b(1, 2);
     9     pair<int, int> c(1, 3);
    10     pair<int, int> d(2, 5);
    11     a.push(d);
    12     a.push(c);
    13     a.push(b);
    14     while (!a.empty())
    15     {
    16         cout << a.top().first << ' ' << a.top().second << '
    ';
    17         a.pop();
    18     }
    19 }

      运行结果

      

     二、数据结构之堆-常见题目汇总

    1. 最小堆

      1.1 前K个高频元素

      

    •  问题分析

      首先建立一个哈希表,统计非空数组中每个元素出现的次数

      建立一个最小堆,最小堆尺寸大于k时,则将堆顶元素出栈

      由于最小堆是从小到大,因此要一次输出出现频率前k高的元素,倒序打印

    • 代码分析

     

     1 class Solution {
     2 public:
     3     class mycomparison{
     4     public:
     5         bool operator()(const pair<int,int>& fsh,const pair<int,int> &ssh)
     6         {
     7             return fsh.second>ssh.second;
     8         }
     9     };
    10     vector<int> topKFrequent(vector<int>& nums, int k) {
    11         vector<int> B(k);
    12         if(nums.empty())
    13             return B;
    14         //首先建立一个哈希表,统计非空数组中每个元素出现的次数
    15         unordered_map<int,int> map;
    16         for(int i=0;i<nums.size();++i)
    17         {
    18             map[nums[i]]++;
    19         }
    20         //建立一个最小堆,最小堆尺寸大于k是则将堆顶元素出栈
    21         priority_queue<pair<int,int>,vector<pair<int,int>>,mycomparison> pri_que;
    22         for(unordered_map<int,int>::iterator it=map.begin();it!=map.end();++it)
    23         {
    24             pri_que.push(*it);
    25             if(pri_que.size()>k)
    26                 pri_que.pop();
    27         }
    28         //由于最小堆是从小到大,因此要一次输出出现频率前k高的元素,倒序打印
    29         for(int i=k-1;i>=0;--i)
    30         {
    31             B[i]=pri_que.top().first;
    32             pri_que.pop();
    33         }
    34         return B;
    35     }
    36 };

      1.2 前k个高频单词

      

    • 问题分析

      为了找到前k个高频单词,采用算法如下:

        首先统计字符串数组中每个字符串出现的次数。

        建立最小堆,维护一个大小为k的最小堆,优先级小的元素在堆顶,每当某个新来的元素的优先级高于小顶堆的优先级时,将这个元素进堆,然后pop掉优先级最小的元素,堆的大小还是k。

        优先级的判定需要重载()(注意一定要重载这个括号运算符而不是其他,因为末尾的greater<T>和less<T>里面也是重载的()),这里我们需要的是greater的功能,也就是说,需要优先级小的元素在堆顶。

    • 代码参考

      

     1 class Solution {
     2 public:
     3     struct compare{
     4     public:
     5         bool operator() (const pair<int,string> &a,const pair<int,string> &b)
     6         {
     7             return (a.first>b.first||a.first==b.first&&a.second<b.second);
     8         }
     9     };
    10     vector<string> topKFrequent(vector<string>& words, int k) {
    11         vector<string> B(k);
    12         if(words.empty()||k<=0)
    13             return B;
    14         unordered_map<string,int> map;
    15         compare ord;
    16         //首先统计每个单词出现的次数
    17         for(int i=0;i<words.size();++i)
    18         {
    19             map[words[i]]++;
    20         }
    21         priority_queue<pair<int,string>,vector<pair<int,string>>,compare > q;
    22         for(auto it:map)
    23         {
    24             pair<int,string> temp{it.second,it.first};
    25             q.push(temp);
    26             if(q.size()>k)
    27                 q.pop();
    28         }
    29         for(int i=k-1;i>=0;--i)
    30         {
    31             B[i]=q.top().second;
    32             q.pop();
    33         }
    34         return B;
    35     }
    36 };

      1.2 剑指offer 40: 最小的k个数

      

    •  问题分析

      要找到最小的K个数,可以使用最小堆来实现,依次弹出K个堆顶元素即可找到最小的k个元素

    • 代码参考
     1 class Solution {
     2 public:
     3     vector<int> getLeastNumbers(vector<int>& arr, int k) {
     4         vector<int> B(k);
     5         if(arr.empty())
     6             return B;
     7         priority_queue<int,vector<int>,greater<int> > pri_que;
     8         for(int i=0;i<arr.size();++i)
     9         {
    10             pri_que.push(arr[i]);
    11         }
    12         for(int i=0;i<k;++i)
    13         {
    14             B[i]=pri_que.top();
    15             pri_que.pop();
    16         }
    17         return B;
    18     }
    19 };

     1.3 丑数

      

    • Solution 1:使用常规方法
      • 问题分析1  

        丑数应该是另一个丑数乘以2,3,5的结果。对于乘以2而言,肯定存在一个丑数T2,排在他之前的每个丑数乘以2得到的结果都会小于已有最大的丑数,在他之后的丑数乘以2得到的结果都会太大,我们只需要记下这个丑数的位置,每次生成新的丑数的时候去更新这个T2即可

      • 代码参考1

      

     1 class Solution {
     2 public:
     3     //最小的丑数是2,3,5,后面的丑数一定是丑数乘以2,3,5,取最小的得到的
     4     int Min(int a,int b,int c)
     5     {
     6         int min=a<b?a:b;
     7         min=min<c?min:c;
     8         return min;
     9     }
    10     int nthUglyNumber(int n) {
    11         if(n<=0)
    12             return 0;
    13         int *uglynumbers=new int [n+1];
    14         uglynumbers[0]=1;
    15         int *M2=uglynumbers;
    16         int *M3=uglynumbers;
    17         int *M5=uglynumbers;
    18         int nextuglynumbers=1;
    19         while(nextuglynumbers<n)
    20         {
    21             int min=Min(*M2*2,*M3*3,*M5*5);
    22             uglynumbers[nextuglynumbers]=min;
    23             while(*M2*2<=min)
    24                 ++M2;
    25             while(*M3*3<=min)
    26                 ++M3;
    27             while(*M5*5<=min)
    28                 ++M5;
    29             ++nextuglynumbers;
    30         }
    31         int ugly=uglynumbers[nextuglynumbers-1];
    32         delete[]uglynumbers;
    33         return ugly;
    34     }
    35 };
    • Solution2 利用最小堆实现
      • 问题分析2

          由于丑数都是另一个丑数乘以2,3,5得到的,可以利用优先队列实现自动排序功能。

          每次去除队头元素,存入队头元素*2,队头元素*3,队头元素*5

          但是需要去重,因为想12这个元素,可以由3*4得到,也可以由2*6得到

      • 代码参考2
     1 class Solution {
     2 public:
     3     int nthUglyNumber(int n) {
     4         if(n<=0)
     5             return 0;
     6         priority_queue<double,vector<double>,greater<double> >q;
     7         double answer=1;
     8         for(int i=1;i<n;++i)
     9         {
    10             q.push(answer*2);
    11             q.push(answer*3);
    12             q.push(answer*5);
    13             answer=q.top();
    14             q.pop();
    15             while(!q.empty()&&answer==q.top())
    16                 q.pop();
    17         }
    18         return answer;
    19     }
    20 };

      1.5 超级丑数

      

    •  Solution 1--利用数组特性来做
      • 问题分析1

        分析超级丑数和丑数的区别是:超级丑数的因子为质数列表primes中的元素,数组长度是未定的

        在循环的过程中,可以先设置一个变量mini保存指针在uglynumbers中的位置对应的值x自身prime值的最小值,接着再循环一次将所有的指针位置进行更新

      • 代码参考1
     1 class Solution {
     2 public:
     3     int nthSuperUglyNumber(int n, vector<int>& primes) {
     4         if(n<=0)
     5             return 0;
     6         int k=primes.size();
     7         vector<int> uglynumbers(n);
     8         vector<int> kv(k,0);//用来记录这k个数的指针位置,初始化为0
     9         int nextuglynumber=1;
    10         uglynumbers[0]=1;
    11         while(nextuglynumber<n)
    12         {
    13             int mini=INT_MAX;
    14             for(int j=0;j<k;++j)
    15             {
    16                 mini=min(mini,uglynumbers[kv[j]]*primes[j]);
    17             }
    18             uglynumbers[nextuglynumber]=mini;
    19             for(int j=0;j<k;++j)
    20             {
    21                 if(mini==uglynumbers[kv[j]]*primes[j])
    22                     kv[j]++;
    23             }
    24             ++nextuglynumber;
    25         }
    26         return uglynumbers[nextuglynumber-1];
    27     }
    28 };
    • solution 2--利用堆
      • 问题分析2  

          由于丑数都是另一个丑数乘以primes[i]得到的,可以利用优先队列实现自动排序功能。

          每次去除队头元素,存入队头元素*primes[i];

          但是需要去重

      • 代码参考2
     1 class Solution {
     2 public:
     3     int nthSuperUglyNumber(int n, vector<int>& primes) {
     4         if(n<=0)
     5             return 0;
     6         priority_queue<long long,vector<long long>,greater<long long> >pq;
     7         long long answer=1;
     8         int k=primes.size();
     9         for(int i=1;i<n;++i)
    10         {
    11             for(int i=0;i<k;++i)
    12             {
    13                 pq.push(answer*primes[i]);
    14                 
    15             }
    16             answer=pq.top();
    17             pq.pop();
    18             while(!pq.empty()&&answer==pq.top())
    19                 pq.pop();
    20         }
    21         return answer;
    22     }
    23 };

      1.6 第k个数

      

    •  问题分析

      这道题类似于丑数的算法,我们将要找到的数称为丑数要得到有序的丑数数组,我们可以用前面的丑数乘以3,5,7,取最小值放入丑数数组即可。

      

     1 class Solution {
     2 public:
     3     int Min(int a,int b,int c)
     4     {
     5         int min=a<b?a:b;
     6         min=min<c?min:c;
     7         return min;
     8     }
     9     int getKthMagicNumber(int k) {
    10         if(k<=0)
    11             return 0;
    12         int *uglynumbers=new int[k+1];
    13         uglynumbers[0]=1;
    14         int *M3=uglynumbers;
    15         int *M5=uglynumbers;
    16         int *M7=uglynumbers;
    17         int nextuglynumbers=1;
    18         while(nextuglynumbers<k)
    19         {
    20             int mini=Min(*M3*3,*M5*5,*M7*7);
    21             uglynumbers[nextuglynumbers]=mini;
    22             while(*M3*3<=mini)
    23                 ++M3;
    24             while(*M5*5<=mini)
    25                 ++M5;
    26             while(*M7*7<=mini)
    27                 ++M7;
    28             ++nextuglynumbers;
    29         }
    30         return uglynumbers[nextuglynumbers-1];
    31     }
    32 };
    • 问题分析2

      也可以用最小堆来实现,算法如下:

        由于丑数都是另一个丑数乘以3,5,7得到的,可以利用优先队列实现自动排序功能。

        每次去除队头元素,存入队头元素*3,队头元素*5,队头元素*7

        但是需要去重

    • 代码参考2

      

     1 class Solution {
     2 public:
     3     int getKthMagicNumber(int k) {
     4         if(k<=0)
     5             return 0;
     6         priority_queue<double,vector<double>,greater<double> > pq;
     7         double answer=1;
     8         for(int i=1;i<k;++i)
     9         {
    10             pq.push(answer*3);
    11             pq.push(answer*5);
    12             pq.push(answer*7);
    13             answer=pq.top();
    14             pq.pop();
    15             //去重
    16             while(!pq.empty()&&answer==pq.top())
    17                 pq.pop();
    18         }
    19         return answer;
    20     }
    21 };

      

      

    2. 最大堆

       2.1 数组中的第k个最大元素

      

    • solution 1
      • 问题分析(使用STL模板)

        要实现找到未排序数组中第k个最大的元素,我们可以使用对排序

          将数组中的元素建立一个最大堆,堆顶元素是最大的

          要找到第k个最大元素,只需要删除k-1次堆顶元素,删除后堆顶元素即为第k个最大值

      • 代码参考
     1 class Solution {
     2 public:
     3     int findKthLargest(vector<int>& nums, int k) {
     4         if(nums.empty())
     5             return 0;
     6         //建立一个最大堆,对于第k大元素,删除k-1次后,堆顶元素就是数组排序后第k个最大的元素
     7         priority_queue<int> pri_que;
     8         for(int i=0;i<nums.size();++i)
     9         {
    10             pri_que.push(nums[i]);
    11         }
    12         //删除k-1次堆顶元素
    13         for(int i=0;i<k-1;++i)
    14             pri_que.pop();
    15         return pri_que.top();
    16     }
    17 };
    • solution2 (自己实现最大堆,包括最大堆的建堆,调整以及删除)
      • 问题分析
      •  代码参考  
     1 class Solution {
     2 public:
     3     //自己实现,要实现三个部分
     4     //首先是堆化
     5     //完全二叉树中,对于节点i,父节点为i/2,左子节点为i*2+1,右子节点为i*2+2
     6     void maxHeapify(vector<int> &a,int i,int heapSize)
     7     {
     8         //左子节点
     9         int l=i*2+1;
    10         //右子节点
    11         int r=i*2+2;
    12         int largest=i;
    13         if(l<heapSize&&a[l]>a[largest])
    14             largest=l;
    15         if(r<heapSize&&a[r]>a[largest])
    16             largest=r;
    17         if(largest!=i)
    18         {
    19             swap(a[i],a[largest]);
    20             maxHeapify(a,largest,heapSize);
    21         }
    22     }
    23     //建立堆
    24     void buildMaxHeap(vector<int> &a,int heapSize)
    25     {
    26         //从第一个非叶子节点为根节点的子树开始,将其调整为最大堆
    27         for(int i=heapSize/2;i>=0;--i)
    28         {
    29             maxHeapify(a,i,heapSize);
    30         }
    31     }    
    32     int findKthLargest(vector<int>& nums, int k) {
    33         int heapSize=nums.size();
    34         buildMaxHeap(nums,heapSize);
    35         for(int i=nums.size()-1;i>=nums.size()-k+1;--i)
    36         {
    37             swap(nums[0],nums[i]);
    38             --heapSize;
    39             maxHeapify(nums,0,heapSize);
    40         }
    41         return nums[0];
    42 
    43     }
    44 };

     

     2.2 有序矩阵中第k小的元素

      

    •  solution1--最大堆
      • 问题分析1

        要找到第k小的元素,一种最常规的做法就是使用优先队列

          找第k小的元素,保留k个最小的元素,其中最大的那个就是答案,所以可以使用最大优先队列

          遍历矩阵中的元素,将元素添加到队列中,如果队列中元素数目>k,则将堆点最大的元素弹出

          遍历结束后弹出堆顶元素,就是最小的k个元素中最大的,即第k小的元素

      • 代码参考1 
     1 class Solution {
     2 public:
     3     int kthSmallest(vector<vector<int>>& matrix, int k) {
     4         if(matrix.empty())
     5             return 0;
     6         priority_queue<int> pri_que;
     7         for(int i=0;i<matrix.size();++i)
     8         {
     9             for(int j=0;j<matrix[0].size();++j)
    10             {
    11                 pri_que.push(matrix[i][j]);
    12                 if(pri_que.size()>k)
    13                     pri_que.pop();
    14             }
    15         }
    16         return pri_que.top();
    17     }
    18 };
    • solution2 --二分查找
      • 问题分析2

          方法1的方式没有用上矩阵的有序性,因此我们可以思考使用二分查找的方法

          对于有序矩阵中,左上角元素是最小元素,右下角元素是最大元素

          因此我们可以使用二分查找的方法,left=左上角元素,right=右下角元素

          mid=left+(right-left)/2;统计大于mid的个数

            如果小于mid的元素个数小于k,则比mid大的数可能是第k小元素

            如果小于mid的元素个数大于k,则比mid小的数可能是第k小元素

          在这里我们使用upper_bound来统计小于mid的元素个数

          upper_bound()

          upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

      • 代码参考2
     1 class Solution {
     2 public:
     3     int kthSmallest(vector<vector<int>>& matrix, int k) {
     4         //有序矩阵中,左上角元素是最小的,右下角元素是最大的
     5         /*
     6         left=matrix[0][0],right=matrix[n][n],mid=left+(left-right)/2
     7         统计大于mid的个数,
     8             如果小于mid的元素个数小于k,则比mid大的数可能是第k小元素
     9        如果小于mid的元素个数大于k,则比mid小的数可能是第k小元素
    10         */
    11         if(matrix.empty())
    12             return 0;
    13         int n=matrix.size()-1;
    14         int left=matrix[0][0];
    15         int right=matrix[n][n];
    16         int mid;
    17         
    18         while(left<right)
    19         {
    20             int ans=0;
    21             mid=left+(right-left)/2;
    22             for(int i=0;i<matrix.size();++i)
    23             {
    24                 ans+=upper_bound(matrix[i].begin(),matrix[i].end(),mid)-matrix[i].begin();
    25             }
    26             if(ans<k)
    27                 left=mid+1;
    28             else
    29                 right=mid;
    30         }
    31         return left;
    32     }
    33 };

      2.3 最后一块石头的重量

      

    •  问题分析

      有题意,最大堆即可解决问题,弹出最大的两个数据,若两个数据之差为0,则不用再次压入最大堆,否则将二者之差压入最大堆

    • 代码参考

      

     1 class Solution {
     2 public:
     3     int lastStoneWeight(vector<int>& stones) {
     4         if(stones.empty())
     5             return 0;
     6         priority_queue<int> pri_que(stones.begin(),stones.end());
     7         while(pri_que.size()>1)
     8         {
     9             int biggest=pri_que.top();
    10             pri_que.pop();
    11             int second=pri_que.top();
    12             pri_que.pop();
    13             int substraction=biggest-second;
    14             if(substraction>0)
    15                 pri_que.push(substraction);
    16         }
    17         if(pri_que.size()>0)
    18             return pri_que.top();
    19         else
    20             return 0;
    21     }
    22 };

      2.4 根据字符出现频率排序

      

    •  问题分析

      要给字符串中的字符按照出现频率升降排序,可以采用以下算法

        首先统计字符串中所有字符出现的次数,存储在哈希表中

        根据字符串中所有字符出现的次数构造最大堆

        依次输出

    • 代码参考
     1 class Solution {
     2 public:
     3     string frequencySort(string s) {
     4         if(s.empty())
     5             return "";
     6         unordered_map<char,int> map;
     7         //首先统计每个字符出现的次数
     8         for(int i=0;i<s.size();++i)
     9         {
    10             map[s[i]]++;
    11         }
    12         //根据每个字符出现的次数,建立一个最大堆
    13         priority_queue<pair<int,char>> pri_que;
    14         for(auto &m:map)
    15         {
    16            pri_que.push({m.second,m.first});
    17         }
    18         string res;
    19         while(!pri_que.empty())
    20         {
    21             auto t=pri_que.top();
    22             pri_que.pop();
    23             res.append(t.first,t.second);
    24         }
    25         return res;
    26     }
    27 };

      2.4 最小k个数

      

    •  问题分析

      要找到数组中最小的k个数,可以使用大堆顶来实现,将数组中的每个元素都压栈入大堆顶,如果最大堆的元素个数大于k,则将最大堆的堆顶元素删除

    • 代码参考

      

     1 class Solution {
     2 public:
     3     vector<int> smallestK(vector<int>& arr, int k) {
     4         vector<int> B;
     5         if(arr.empty()||k<=0)
     6             return B;
     7         priority_queue<int,vector<int>,less<int> >q;
     8         for(int i=0;i<arr.size();++i)
     9         {
    10             q.push(arr[i]);
    11             if(q.size()>k)
    12                 q.pop();
    13         }
    14         while(!q.empty())
    15         {
    16             B.push_back(q.top());
    17             q.pop();
    18         }
    19             
    20         return B;
    21     }
    22 };

      2.5 重构字符串

      

    •  问题分析

      要判断是否可以重构字符串,可以采用算法如下

        首先构建哈希表统计字符串中每个字符出现的次数,如果字符串中某个字符出现的次数超过字符串长度的一半,则不能重构字符串

        然后构建一个最大堆,将哈希表中的键值对根据出现次数构建成一个最大堆

        然后按照字母数量降序顺序,当队列不为空时,一次按照堆顶元素,隔着往原始字符串中插入当前字符,下标从0开始,每次插入下标+2,当超过数组大小时,变为1

    • 代码参考
     1 class Solution {
     2 public:
     3     string reorganizeString(string S) {
     4         //哈希表+最大堆
     5         /*
     6         构造一个哈希表,其首先统计字符串中每个字符出现的次数
     7         将哈希表字符及字符出现的次数压入大顶堆中
     8         依次插入一个长度与S等长的字符串中
     9         */
    10         if(S.empty())
    11             return "";
    12         //首先统计字符串中每个字符出现的次数,并将其存储在哈希表中
    13         unordered_map<char,int> map;
    14         for(int i=0;i<S.size();++i)
    15         {
    16             map[S[i]]++;
    17             if(map[S[i]]>(S.size()+1)/2)
    18                 return "";
    19         }
    20             
    21 
    22         //构造一个大顶堆存储哈希表
    23         priority_queue<pair<int,char>,vector<pair<int,char>>> pq;
    24         for(auto item:map)
    25         {
    26             pq.push({item.second,item.first});
    27         }
    28         int i=0;
    29         string res=S;
    30         while(!pq.empty())
    31         {
    32             char ch=pq.top().second;
    33             int cnt=pq.top().first;
    34             pq.pop();
    35             while(cnt--)
    36             {
    37                 i=i>=S.size()?1:i;
    38                 res[i]=ch;
    39                 i=i+2;
    40             }
    41         }
    42         return res;
    43     }
    44 };

      2.6 最接近原点的K个点

      

    •  问题分析

      要实现找到最接近原点的K个点,我们可以构造一个大小为K的最大堆,当最大堆的尺寸大于K时,将堆顶元素弹出,因此最后最大堆中剩下的就是距离原点最小的K个点

      构建的最大堆每次输入为一个长度为2的vector数组,此时不是基本类型,因此需要重载运算符()

    • 代码参考
     1 class Solution {
     2 public:
     3     //最大堆,因此需要重载()
     4     class compare{
     5     public:
     6         bool operator()(const vector<int>&a,const vector<int>&b)
     7         {
     8             return pow((pow(a[0],2)+pow(a[1],2)),0.5)<pow((pow(b[0],2)+pow(b[1],2)),0.5);
     9         }
    10     };
    11     vector<vector<int>> kClosest(vector<vector<int>>& points, int K) {
    12         vector<vector<int>> B;
    13         if(points.empty())
    14             return B;
    15         //构建最大堆
    16         priority_queue<vector<int>,vector<vector<int>>,compare> pq;
    17         for(int i=0;i<points.size();++i)
    18         {
    19             int distance=pow(pow(points[i][0],2)+pow(points[i][1],2),0.5);
    20             vector<int> temp={points[i][0],points[i][1]};
    21             pq.push(temp);
    22             if(pq.size()>K)
    23                 pq.pop();
    24         }
    25         for(int i=0;i<K;++i)
    26         {
    27             B.push_back(pq.top());
    28             pq.pop();
    29         }
    30         return B;
    31     }
    32 };

       2.7 查找和最小的k对数字

      

    •  问题分析

      要查找和最小的K对数字,我们可以将nums1的第i个元素和nums2的第j个元素相加,并将数据对存储在长度为K的最大堆上

    • 代码参考

      

     1 class Solution {
     2 public:
     3     class compare{
     4     public:
     5         bool operator()(const pair<int,int> &a,pair<int,int> &b)
     6         {
     7             return (a.first+a.second)<(b.first+b.second);
     8         }
     9     };
    10     vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
    11         vector<vector<int>> B;
    12         if(nums1.empty()||nums2.empty())
    13             return B;
    14         
    15         priority_queue<pair<int,int>,vector<pair<int,int>>,compare> pq;
    16         for(int i=0;i<nums1.size();++i)
    17         {
    18             for(int j=0;j<nums2.size();++j)
    19             {
    20                 pq.push({nums1[i],nums2[j]});
    21                 if(pq.size()>k)
    22                     pq.pop();
    23             }
    24         }
    25         while(!pq.empty())
    26         {
    27             pair<int,int> top=pq.top();
    28             pq.pop();
    29             B.push_back({top.first,top.second});
    30         }
    31         return B;
    32     }
    33 };

      

        

      

  • 相关阅读:
    vscode 快捷键
    Nest 中在当前模块使用其他模块 service 的方式
    Elasticsearch:应用 Nodejs 来访问 Elasticsearch【转载】
    开始使用 Elasticsearch (1)[转载]
    SVO详细解读
    深度滤波器详细解读
    Google Cardboard的九轴融合算法——基于李群的扩展卡尔曼滤波
    相机IMU融合四部曲(三):MSF详细解读与使用
    相机IMU融合四部曲(二):误差状态四元数详细解读
    相机IMU融合四部曲(一):D-LG-EKF详细解读
  • 原文地址:https://www.cnblogs.com/Cucucudeblog/p/13736940.html
Copyright © 2011-2022 走看看