¿Realmente has captado la esencia de la "cola de prioridad"?

Preámbulo

Si asignamos un número a cada elemento para marcar su prioridad, establezcamos un número más bajo para tener una prioridad más alta, de modo que podamos acceder al elemento de mayor prioridad en una colección y encontrarlo y eliminarlo Operado. De esta manera, introdujimos la estructura de datos de la cola de prioridad .

Uno, la introducción de priority_queue

1. Una cola de prioridad es un adaptador de contenedor cuyo primer elemento es el más grande entre todos los elementos de acuerdo con criterios estrictos de clasificación débil.
2. Esta estructura es similar a un montón. Los elementos se pueden insertar en el montón en cualquier momento y solo se puede recuperar el elemento más grande del montón (el elemento en la parte superior de la cola de prioridad).
3. La cola de prioridad se implementa como un adaptador de contenedor , que encapsula una clase de contenedor específica como su clase de contenedor subyacente , y la cola de prioridad proporciona un conjunto de funciones miembro específicas para acceder a sus elementos . Los elementos se extraen de la "cola" de un contenedor en particular, que se denomina la parte superior de la cola de prioridad .
4. El contenedor subyacente puede ser cualquier plantilla de clase de contenedor estándar o cualquier otra clase de contenedor especialmente diseñada. Los contenedores deben ser accesibles a través de iteradores de acceso aleatorio y admitir las siguientes operaciones:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
5. Las clases de contenedor estándar vector y deque cumplen estos requisitos. De forma predeterminada, se utiliza vector si no se especifica ninguna clase de contenedor para una instancia de clase de prioridad_cola en particular.
6. Necesidad de admitir iteradores de acceso aleatorio para que la estructura del montón siempre se mantenga internamente. El adaptador de contenedor hace esto automáticamente llamando automáticamente a las funciones algorítmicas make_heap, push_heap y pop_heap cuando es necesario.

En segundo lugar, la implementación de la simulación de la prioridad_cola

Las funciones a implementar por priority_queue son las siguientes:

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()//判空
Dado que la estructura subyacente es la estructura secuencial del montón, la simulación en realidad usa el contenedor subyacente para implementar un montón .

2.1 Variables miembro

template<class T,class Container=vector<T>,class Compare> 
    class priority_queue
    {
    private:
        Container _con;
    };
La implementación del contenedor subyacente aquí usa vector por defecto . Una cosa a tener en cuenta aquí es que no necesitamos escribir un constructor aquí. El constructor predeterminado llamará automáticamente al constructor del contenedor subyacente .

2.2 ajustar_arriba()/ajustar_abajo()

Mencionamos en la introducción que la estructura del montón utilizada por la cola de prioridad es un montón grande por defecto, y si se usa el montón, el ajuste hacia arriba y hacia abajo son indispensables en el proceso de inserción y extracción, por lo que primero usamos este Las dos funciones están completas, y el resto es muy sencillo.

2.2.1 ajustar_arriba()

adjust_up ajusta hacia arriba, generalmente se usa para empujar. Después de empujar un valor al final, ajústelo a la posición en la que debería ir ajustando hacia arriba. Aquí adoptamos el patrón de explicación gráfica. La siguiente es la imagen de arriba
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 ajustar_abajo()

ajust_down se ajusta hacia abajo, se usa para pop, se explicará cuando pop
ajuste_abajo necesita considerar el problema transfronterizo de los niños izquierdo y derecho, aquí debe prestar atención
//向下调整
        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 empujar()/pop()

2.3.1 empujar()

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;
                }
            }
        }
成功运行

Supongo que te gusta

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