Você realmente entendeu a essência da "fila de prioridade"?

Preâmbulo

Se atribuirmos um número a cada elemento para marcar sua prioridade, vamos definir um número menor para ter uma prioridade mais alta, para que possamos acessar o elemento de maior prioridade em uma coleção e localizá-lo e excluí-lo Operado. Desta forma, introduzimos a estrutura de dados da fila de prioridade .

Primeiro, a introdução de priority_queue

1. Uma fila de prioridade é um adaptador de contêiner cujo primeiro elemento é o maior entre todos os elementos de acordo com critérios de classificação fracos estritos.
2. Essa estrutura é semelhante a um heap.Elementos podem ser inseridos no heap a qualquer momento e somente o maior elemento do heap (o elemento no topo da fila de prioridade) pode ser recuperado.
3. A fila de prioridade é implementada como um adaptador de contêiner , que encapsula uma classe de contêiner específica como sua classe de contêiner subjacente , e priority_queue fornece um conjunto de funções de membro específicas para acessar seus elementos . Os elementos são extraídos da "cauda" de um contêiner específico, que é chamado de topo da fila de prioridade .
4. O contêiner subjacente pode ser qualquer modelo de classe de contêiner padrão ou qualquer outra classe de contêiner especialmente projetada. Os contêineres devem ser acessíveis por meio de iteradores de acesso aleatório e oferecer suporte às seguintes operações:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5. As classes de contêiner padrão vector e deque atendem a esses requisitos. Por padrão, vector é usado se nenhuma classe de contêiner for especificada para uma instanciação de classe priority_queue específica.
6. Necessidade de suportar iteradores de acesso aleatório para que a estrutura do heap seja sempre mantida internamente. O adaptador de contêiner faz isso automaticamente chamando automaticamente as funções algorítmicas make_heap, push_heap e pop_heap quando necessário.

Em segundo lugar, a implementação de simulação de priority_queue

As funções a serem implementadas por priority_queue são as seguintes:

void adjust_up(int child)//push的时候要用
void adjust_down(int parent)//pop的时候要用
void push(const T& x)//在优先级队列中插入元素x
void pop()//删除优先级队列中最大(最小)元素,即堆顶元素
const T& top()//返回优先级队列中最大(最小元素),即堆顶元素
size_t size()//返回队列中的有效数据个数
bool empty()//判空
Como a estrutura subjacente é a estrutura sequencial do heap, a simulação realmente usa o contêiner subjacente para implementar um heap .

2.1 Variáveis ​​de membro

template<class T,class Container=vector<T>,class Compare> 
    class priority_queue
    {
    private:
        Container _con;
    };
A implementação do contêiner subjacente aqui usa vector por padrão . Uma coisa a observar aqui é que não precisamos escrever um construtor aqui. O construtor padrão chamará automaticamente o construtor do contêiner subjacente .

2.2 ajuste_para cima()/ajuste_para baixo()

Mencionamos na introdução que a estrutura do heap usado pela fila de prioridade é um heap grande por padrão, e se o heap for usado, o ajuste para cima e o ajuste para baixo são indispensáveis ​​no processo de push e pop, então primeiro usamos isso As duas funções estão concluídas e o resto é muito simples.

2.2.1 ajuste_up()

Adjust_up ajusta para cima, geralmente usado para empurrar. Depois de empurrar um valor no final, ajuste-o para a posição que ele deve ir ajustando para cima. Aqui adotamos o padrão de explicação gráfica. A figura a seguir é a seguinte
void adjust_up(int child)
        {
            int parent = (child - 1) / 2;
            while (child>0)
            {
                if (_con[parent] < _con[child])
                {
                    swap(_con[parent], _con[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

2.2.2 ajuste_para baixo ()

ajust_down ajusta para baixo, usado para pop, será explicado quando pop
Adjust_down precisa considerar o problema transfronteiriço dos filhos esquerdo e direito, aqui você precisa prestar atenção
//向下调整
        void adjust_down(int parent)
        {
            int child = parent * 2 + 1;
            while (child < _con.size())
            {
                //看右孩子是否越界,然后取左右孩子大的值
                if (child + 1 < _con.size() && _con[child + 1] > _con[child])
                {
                    child = child + 1;
                }

                if (_con[parent] < _con[child])
                {
                    swap(_con[parent], _con[child]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }

2.3 push()/pop()

2.3.1 empurre ()

push:在优先级队列中插入元素x
实现逻辑:尾插插入元素x,然后通过adjust_up调整位置,堆的结构不被破坏。
        void push(const T& x)
        {
            _con.push_back(x);
            adjust_up(_con.size()-1);
        }

2.3.2 pop()

pop:删除优先级队列中最大(最小)元素,即堆顶元素
实现逻辑:队列中的 top最后一个数据进行 交换,然后进行 尾删,在对换到top位置的数据进行adjust_down,保证堆的 结构不被破坏
        void pop()
        {
            swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            adjust_down(0);
        }

2.4 top()/size()/empty()

这三个接口的实现比较简单,直接用底层容器的接口实现
        const T& top()
        {
            return _con[0];
        }
        size_t size()
        {
            return _con.size();
        }
        bool empty()
        {
            return _con.empty();
        }

三,priority_queue的精髓

priority_queue的大体框架大致如上,但这并不是它的精髓,那么他的精髓是什么呢?
不知道有没有老铁发现priority_queue的模板参数有三个,而上面的内容只用了前两个,最后一个还没有用,而最后一个就是精髓。
那么它有什么作用呢,众所周知堆分大堆和小堆,大堆top是最大值,小堆top是最小值,而priority_queue也是支持这种转变的,如何实现的呢,就是靠第三个模板参数。
如上图所示就是库中priority_queue第三个参数转为小堆的使用方法,如上可知greater也是一个类,因为他需要传类模板参数,那么greater是什么呢?
这里还需要涉及一个知识点,那就是仿函数。

3.1 仿函数

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个 operator (),这个类就有了类似函数的行为,就是一个仿函数类了。
需要注意的是,仿函数也需要实例化.

3.2 构建greater()/less()

greater()/less()的构建和上面类似,不过多了一个模板参数
    template <class T>
    class less//less小于是我们按照库里的来的
    {
    public:
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };

    template <class T>
    class greater
    {
    public:
        bool operator()(const T& x, const T& y)
        {
            return x > y;
        }
    };
而运用less、greater需要注意,less在库中表示的是大堆,而我们想要使用就需要调整位置,调整成<。
然后就可以套用类函数。
    void adjust_up(int child)
        {
            int parent = (child - 1) / 2;
            while (child>0)
            {
                if (_com(_con[parent],_con[child]))//类函数
                {
                    swap(_con[parent], _con[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

        //向下调整
        void adjust_down(int parent)
        {
            int child = parent * 2 + 1;
            while (child < _con.size())
            {
                //看右孩子是否越界,然后取左右孩子大的值
                if (child + 1 < _con.size() && _con[child + 1]>_con[child])
                {
                    child = child + 1;
                }

                if (_com(_con[parent], _con[child]))//类函数
                {
                    swap(_con[parent], _con[child]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }
成功运行

Acho que você gosta

Origin blog.csdn.net/zcxmjw/article/details/129755383
Recomendado
Clasificación