C++基础(三)——STL优先级队列

文章目录

  • 一、定义
    • 大顶堆
    • 小顶堆
  • 二、接口
  • 三、例子
  • 四、实现一个优先级队列


一、定义

大顶堆

priority_queue name;
priority_queue默认为大顶堆
故定义也可以写作:
priority_queue, less > ay;

小顶堆

如果需要构建一个小顶堆,可以改变定义方式为:
priority_queue, greater > ay;
使用std::greater模板无需自定义比较函数。

自定义比较函数:

#include 

struct Compare {
    bool operator()(int a, int b) {
        return a > b; 
    }
};

int main() {
    std::priority_queue<int, std::vector<int>, Compare> pq; // 使用自定义的比较函数
    pq.push(3);
    pq.push(1);
    pq.push(2);

    while (!pq.empty()) {
        std::cout << pq.top() << " ";
        pq.pop();
    }

    return 0;
}

二、接口

优先级队列只能通过top()访问队首元素(优先级最高的那个元素)
pop():弹出优先级最高的元素
push():塞元素进队列
empty() size():是否空和元素个数

#include 
#include 
using namespace std;
int main() {
    priority_queue<int> ay;
    ay.push(3);
    ay.push(6);
    ay.push(2);
    cout << ay.top() << endl;   // result: 6
    ay.pop();   // 弹出6,剩3、2
    return 0;
}

三、例子

题目:找出topK的元素

建立 [元素, 元素出现个数] 的键值对
优先级队列中的数据结构为pair
将优先级队列的优先级设置为值的小顶堆

定义:
priority_queue, vector>, mycomparison> pri_que;
仿函数mycomparison如下:

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

总体:

class Solution {
public:
    // 小顶堆
    class mycomparison {
    public:
        bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) {
            return lhs.second > rhs.second;
        }
    };
    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 统计元素出现频率
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            map[nums[i]]++;
        }

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

        // 用固定大小为k的小顶堆,扫面所有频率的数值
        for (unordered_map<int, int>::iterator it = map.begin(); it != map.end(); it++) {
            pri_que.push(*it);
            if (pri_que.size() > k) { // 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
                pri_que.pop();
            }
        }

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

    }
};

方法二:
自定义sort函数排序规则

class Solution {
public:
    static bool compareByValue(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) {
        return lhs.second > rhs.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        vector<int> res;
        // 要统计元素出现频率
        unordered_map<int, int> mymap; // map
        for (int i = 0; i < nums.size(); i++) {
            mymap[nums[i]]++;
        }

        // 将键值对存储到 vector 中
        vector<pair<int, int>> vec(mymap.begin(), mymap.end());

        // 使用自定义的比较器函数进行排序
        std::sort(vec.begin(), vec.end(), Solution::compareByValue);
        // 按照无序容器 unordered_map 中的值来排序

        // 遍历输出排序后的键值对
        for (int i = 0; i < k; ++i)  res.push_back(vec[i].first);
        
        return res;
    }
};

四、实现一个优先级队列

#include
#include
#include//memecpy
#include    // time
using namespace std;


//优先级队列实现

class PriorityQueue {
public:
    using Comp = function<bool(int, int)>;
    PriorityQueue(int cap = 20, Comp comp = greater<int>())
        : size_(0)
        , cap_(cap)
        , comp_(comp) {
        que_ = new int[cap_];
    }

    PriorityQueue(Comp comp) 
        : size_(0), cap_(20), comp_(comp) {
        que_ = new int [cap_];
    }

    ~PriorityQueue() {
        delete[] que_;
        que_ = nullptr;
    }

public:
    //入堆操作
    void push(int val) {
        //判断扩容
        if (size_ == cap_) {
            int* p = new int[2 * cap_];
            memcpy(p, que_, cap_ * sizeof(int));
            delete[]que_;
            que_ = p;
            cap_ *= 2;
        }

        if (size_ == 0) {
            //只有一个元素不用进行堆的上浮调整
            que_[size_] = val;
        } else {
            // 堆里面有多个元素,需要进行上浮调整
            siftUp(size_, val);
        }
        size_++;
    }

    void pop() {
        if (size_ == 0) {
            throw "container is empty";
        }
        size_--;
        if (size_ > 0) {
            //删除堆顶元素, 还有剩余的元素, 要进行堆的下沉调整
            siftDown(0, que_[size_]);
        }
    }

    bool empty() const {
        return size_ == 0;
    }

    int top() const {
        if (size_ == 0) {
            throw "container is empty!"; 
        }
        return que_[0];
    }

    int size() const {
        return size_;
    }

private:
    //入堆上浮调整 O(log n)
    void siftUp(int i, int val) {
        while (i > 0) {   //最多计算到根节点 0号位
            int father = (i - 1) / 2;
            if (comp_(val, que_[father])) {  // 默认大于 当前元素比父节点的值大
                que_[i] = que_[father];
                i = father;
            } else {
                break;
            }
        }

        // 把val放到i的位置
        que_[i] = val;
    }

    //出堆下沉调整  当前节点与孩子节点比较
    void siftDown(int i, int val) {
        //i下沉不能超过最后一个有孩子的节点 (size_ - 1 - 1) / 2
        while (i <= (size_ - 1 - 1) / 2) {
            int child = 2 * i + 1; // 第i个节点的左孩子
            if (child + 1 < size_) {  // 有右孩子
                if (child + 1 < size_ && comp_(que_[child + 1], que_[child])) {
                    // 如果i节点右孩子大于左孩子, child 记录 右孩子的值
                    child = child + 1;
                }
            }

            if (comp_(que_[child], val)) {
                que_[i] = que_[child];
                i = child;
            } else {
                break;  //已经满足堆的性质 提前结束
            }
        }
        que_[i] = val;
    }
private:
    int* que_;  //指向动态扩容的数组
    int size_;  // 数组元素的个数
    int cap_; //数组的总空间大小
    Comp comp_; //比较器对象
};
int main() {
//    PriorityQueue que;  //基于大根堆实现的优先级队列
    PriorityQueue que([](int a, int b) {return a < b;}); // 小根堆
    srand(time(NULL));

    for (int i = 0; i < 10; i++) {
        que.push(rand() % 100);
    }
  
    while (!que.empty()) {
        cout << que.top() << " ";
        que.pop();
    }

    cout << endl;

    return 0;
}

你可能感兴趣的:(c++,开发语言,算法)