C++ быстро реализует приоритетную очередь в STL

Мы превратили курс по структуре данных в курс по изготовлению колес. DijkstraКстати, для алгоритма оптимизации кучи я реализовал приоритетную очередь. Не много ерунды, просто перейдите к коду.

Я написал небольшую верхнюю кучу, если вам нужно использовать большую верхнюю кучу, есть два способа. <1. Измените несколько в программе на >2. Все сохраненные данные принимают отрицательные числа и принимают отрицательные числа, когда их нужно вынуть

Так как STL priority_queueреализована путем vectorпередачи, то и переносчик данных приоритетной очереди в коде здесь vector.

Сначала заголовочный файл и объявление класса:

#include "iostream"
#include "vector"
using namespace std;

template<class DataType>
class priority
{
    
    
    public:
        vector<DataType> a;

        priority();		// 构造函数
        ~priority();	// 析构函数

        int size();		// 获取优先队列中数据的个数
        bool empty();	// 判断优先队列是否为空
        DataType top();	// 获取队首元素(堆顶元素)
        DataType pop();	// 弹出队首元素
        void push(const DataType &value);	// 元素入队
        void adjust(int low, int high);		// 调整堆(这是辅助函数,不会作为功能函数呗用户调用)
};

Реализуется через базовые операции с кучей.

Логика операции вставки ( push) состоит в том, чтобы сначала поместить вставляемый элемент в конец очереди, то есть последний родительский узел бинарного дерева, соответствующий куче, а затем начать с этого узла и настроить двоичный дерево снизу вверх. Логика настройки проста: сравните узел с его родительским узлом, если он меньше своего родительского узла, то это означает, что узел должен действовать как корневой узел текущего двоичного поддерева, чтобы соответствовать определению малой верхней кучи, поэтому мы поменяем позицию текущего узла с его родительским узлом, а затем сделаем это рекурсивно, пока он не станет корневым узлом ( j==0); если узел больше, чем его родительский узел, то это означает, что наше неаккуратное размещение в конце будет не влияет на текущее бинарное дерево или маленькую корневую кучу на этом можно закончить.

Логика операции всплывающего окна очень проста, она обратна методу вставки, так как наша очередь перед выталкиванием уже является приоритетной очередью, а минимальное значение всей очереди находится уже в первом элементе, то что мы нужно вернуть и уничтожить первый элемент очереди. Так как vectorпринцип реализации - линейная таблица, а непосредственное уничтожение первого адресного элемента линейной таблицы реализуется перемещением элемента, то временная сложность popкаждого очереди равна O(n)1, что не является затратным. эффективный. Поэтому мы рассматриваем сначала обмен элементов в начале очереди с элементами в конце очереди, затем возвращаем элементы в конце очереди в качестве возвращаемого значения, затем уничтожаем элементы в конце очереди pop(), и, наконец, вызовите adjust()функцию, чтобы повторно сохранить оставшиеся n-1элементы в небольшой верхней куче.

Конечно, поскольку returnоперация завершит текущую функцию напрямую, фактическая последовательность записи должна быть такой:

保存队首元素
交换队首与队尾元素
销毁队尾元素
调整小根堆
返回保存的值

код показывает, как показано ниже:

template<class DataType>
priority<DataType>::priority()  {
    
    }

template<class DataType>
priority<DataType>::~priority() {
    
    this->a.clear();}

template<class DataType>
int priority<DataType>::size()
{
    
    
    return this->a.size();
}

template<class DataType>
bool priority<DataType>::empty()
{
    
    
    if (this->size())
        return false;
    else    
        return true;
}

template<class DataType>
DataType priority<DataType>::top()
{
    
    
    return this->a.front();
}

template<class DataType>
DataType priority<DataType>::pop()
{
    
    
    DataType temp = this->a.front();
    swap(this->a.front(), this->a.back());
    this->a.pop_back();
    this->adjust(0, this->size() - 1);
    return temp;
}

template<class DataType>
void priority<DataType>::push(const DataType &value)
{
    
    
    this->a.push_back(value);
    int i = this->a.size() - 1, j = (i - 1) / 2;
    while (j >= 0)
    {
    
    
        if (this->a[i] < this->a[j])
        {
    
    
            swap(this->a[i], this->a[j]);
            i = j, j = (i - 1) / 2;
        }
        else
            break;
    }
}

template<class DataType>
void priority<DataType>::adjust(int low, int high)
{
    
    
    int temp = a[low];
    int i = low, j = 2 * i + 1;

    while (j <= high)
    {
    
    
        if (j + 1 <= high && a[j + 1] < a[j])
            j ++;
        
        if (temp <= a[j])
            break;
        
        a[i] = a[j];
        i = j, j = 2 * i + 1;
    }
    a[i] = temp;
}

template<class DataType>
int arraylen(DataType &a)
{
    
    
    return sizeof(a) / sizeof(a[0]);
}

Следует отметить, что мы храним данные, начиная с 0. Если индекс родительского узла равен int i, а индекс левого дочернего узла равен int j, то связь iсji == 2 * j + 1; j == (i - 1) / 2;

есть тест:

template<class DataType>
int arraylen(DataType &a)	// 返回任意一维数组元素个数的函数
{
    
    
    return sizeof(a) / sizeof(a[0]);
}


int main(int argc, char const *argv[])
{
    
    
    int a[] = {
    
    2,1,3,4,6};
    priority<int> q;
    for (int i = 0; i < arraylen(a); ++ i)
        q.push(a[i]);
    
    while (!q.empty())
    {
    
    
        cout << q.pop() << " ";
    }
    return 0;
}

вне:

1 2 3 4 6 

Guess you like

Origin blog.csdn.net/weixin_45576923/article/details/111933220