从零开始手写STL库:Priority_Queue

从零开始手写STL库–Priority_Queue的实现

Gihub链接:miniSTL



一、priority_queue是什么?

优先队列在力扣刷题中可能会出现,实际上就是一个带有权重优先级的队列

在这个队列中,所有元素都会有一个权重,队列会根据权重来对元素排序

这也就是所谓的“优先”,而这个优先顺序是可以自己定义的

对int型优先队列,STL库默认是把数据从大到小排序的

所以难点在于如何排序,答案是基于堆来构建优先队列

那么问题也就明确了,所谓优先队列的考察,实际上是对数据结构–的考察

二、堆是什么?

堆(Heap)是一种特殊的完全二叉树,它满足下面的性质:

结构性质: 堆是一个完全二叉树,这意味着除了最后一层外,每一层都是完全填满的,而最后一层的节点则尽可能地集中在左边。

堆性质: 在一个最大堆(Max Heap)中,每个节点的值都大于或等于其子节点的值,根节点的值是堆中的最大值。相反,在一个最小堆(Min Heap)中,每个节点的值都小于或等于其子节点的值,根节点的值是堆中的最小值。

实际上堆可以用队列实现,不需要真的构建一个二叉树,只需要构建两个维护函数即可

三、priority_queue要包含什么函数

首先明确一下两个维护函数,即向上维护和向下维护

    void heapUp()
    {
        int index = data.size() - 1;
        while (index)
        {
            int parentIndex = (index - 1) / 2;
            if(data[index] > data[parentIndex])
            {
                std::swap(data[index], data[parentIndex]);
                index = parentIndex;
            }
            else break;
        }
    }

    void heapDown() {
        int index = 0;
        int size = data.size();
        while (1) 
        {
            int leftChild = 2 * index + 1;
            int rightChild = 2 * index + 2;
            int largest = index;

            if (leftChild < size && data[leftChild] > data[largest]) largest = leftChild;
            if (rightChild < size && data[rightChild] > data[largest]) largest = rightChild;
            
            if (largest != index) 
            {
                std::swap(data[index], data[largest]);
                index = largest;
            } else break;
        }
    }

本处实现就不考虑string或者其他类型的队列了,只考虑int和double型队列

这里向上向下维护实际上就是堆排序的上浮和下沉

上浮:如果父节点比k结点小,就交换他们的位置
下沉:如果k结点比它的两个子结点小,则与较大的那个交换

之后就是正常的一层双向队列封装,只需要每次插入和删除的时候维护一下堆即可

template <typename T, typename Container = myDeque<T> >
class myPriQue
{
private:
    Container data;

    void heapUp()
    {
        int index = data.size() - 1;
        while (index)
        {
            int parentIndex = (index - 1) / 2;
            if(data[index] > data[parentIndex])
            {
                std::swap(data[index], data[parentIndex]);
                index = parentIndex;
            }
            else break;
        }
    }

    void heapDown() {
        int index = 0;
        int size = data.size();
        while (1) 
        {
            int leftChild = 2 * index + 1;
            int rightChild = 2 * index + 2;
            int largest = index;

            if (leftChild < size && data[leftChild] > data[largest]) largest = leftChild;
            if (rightChild < size && data[rightChild] > data[largest]) largest = rightChild;
            
            if (largest != index) 
            {
                std::swap(data[index], data[largest]);
                index = largest;
            } else break;
        }
    }  

public:
    myPriQue() {};
    myPriQue(const Container & c) : data(c) 
    {
        int size = data.size();
        for(int i = (size / 2) - 1; i >= 0; i --) heapDown();
    }

    void push(const T & value)
    {
        data.push_back(value);
        heapUp();
    }

    void pop()
    {
        if (!data.empty())
        {
            std::swap(data[0], data[data.size() - 1]);
            data.pop_back();
            heapDown();
        }
        else throw std::runtime_error("Stack is empty!");
        
    }

    T& top() 
    {
        if (!data.empty()) return data[0];
        else throw std::runtime_error("Priority queue is empty.");
        
    }

    bool empty()  
    {
        return data.empty();
    }

    size_t size() const 
    {
        return data.size();
    }
};

总结

优先队列一般也不会作为考察重点,在力扣中更多地是考虑如何利用vector或者queue自己构建一个专属的优先队列来解决问题,比如单调栈,实际上也是一种优先队列

只需要知道优先队列是以堆为基础构建的即可

至此,简单STL库实现教程完成。由于本人初学,博客中或许会有不对之处,也请读者指出,完整代码异步GitHub链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值