Самая подробная версия графического приоритета очереди (кучи)

структура данных

Во-первых, разница между очередью и приоритетной очередью

  1. очередьэтоFIFO(Первое в первую очередь) в первую очередь, в первую очередь структура данных, соответствующая сцене очередей в жизни, человек впереди всегда проходит первым,последовательно.
  2. приоритетная очередьэто специальная очередь, от слова "приоритет" видно чтоСуществует «феномен очередей». Например, стоя в очереди на вокзале, некоторые спешащие люди вскочат в очередь и первыми пройдут проверку билетов. приоритетная очередьСостоит не менее чем из двух действующихСтруктура данных:вставлять, т. е. вставка элемента в приоритетную очередь (enqueue); иdeleteMin (удалить наименьший), его роль заключается в поиске и удалении наименьшего элемента в приоритетной очереди (dequeue).
优先队列
приоритетная очередь

Во-вторых, характеристики приоритетной очереди (кучи)

  • Реализация приоритетных очередей часто используетсядвоичная куча,В структуре данных приоритетная очередь также обычно относится к куче..

  • Два свойства кучи:

  1. структурный:Куча представляет собой бинарное дерево, которое полностью заполнено, за исключением нижнего слоя, узлы нижнего слоя заполняются слева направо.Такое дерево называетсяПолное бинарное дерево.

  2. Порядок кучи:Поскольку мы хотим быстро найти наименьший элемент, наименьший элемент должен быть в корне,любой узел меньше, чем его потомки,ЭтоМаленькая стопка (MIN-HEAP); если вы ищете максимальный элемент, максимальный элемент должен быть в корне,Любой узел должен быть больше, чем его потомки,ЭтоБольшая верхняя куча (Max-heap).

    Структурный:

    完成二叉树
    полное бинарное дерево

Путем наблюдения было установлено, чтоПолное бинарное дерево может быть непосредственно представлено массивомбез использования других структур данных. Таким образом, нам нужно только передать размер, чтобы построить структуру приоритетной очереди (элементы сравниваются с помощью метода compareTo).

public class PriorityQueue<T extends Comparable<? super T>> { 
    public PriorityQueue(int capacity) {
        currentSize = 0;
        array = (T[]) new Comparable[capacity + 1];
    }
}
完全二叉树的数组实现
Массивная реализация полного двоичного дерева

Для элемента в любой позиции i в массиве еголевый сынв месте2iНаправильный сынсуществует2i+1начальство,родительский узелвi/2(округлено вниз). Обычно он сохраняется из индекса массива 1. Преимущество этого в том, что легко найти левый и правый и родительские узлы. Если начать с 0, левый дочерний узел находится в 2i+1, правый дочерний узел — в 2i+2, а родительский узел — в (i-1)/2 (округлено вниз).

Порядок кучи:

мы строимМинимальная куча, то есть для каждого элемента X ключ в родительском элементе X меньше (или равен) ключу в X, за исключением корневого узла (у которого нет родителя).

堆
куча

Как показано на рисунке, только левая сторона является кучей, а красный узел справа нарушает порядок кучи. Согласно упорядочению кучи, для нахождения наименьшего элемента требуется только константа O (1).

3. Основные операции с кучей

  1. вставлять
  • верхний фильтр:Чтобы вставить элемент X, мы создали отверстие в следующем доступном месте.(Иначе это сломало бы структуру, а не полное бинарное дерево).Если этот элемент помещается в дыру без нарушения порядка кучи, вставка завершена, в противном случае родительский узел перемещается вниз к дыре, то есть дыра делает шаг в направлении корня.Продолжайте этот процесс, пока X не будет вставлен в полость. Такой процесс называется верхней фильтрацией.
建立空穴
Построить отверстие
完成插入
полная вставка

На рисунке показан процесс вставки 18,Установив отверстие в следующей доступной позиции (структурное удовлетворение), обнаруженное как вставленное непосредственно, родительский узел переместится вниз, приняв отверстие. Продолжайте этот процесс, пока не встретите порядок в куче.Таким образом элементы вставляются в приоритетную очередь (кучу).

  • Java реализует фильтрацию вверх
     /**
     * 插入到优先队列,维护堆序性
     *
     * @param x :插入的元素
     */
    public void insert(T x) {
        if (null == x) {
            return;
        }
        //扩容
        if (currentSize == array.length - 1) {
            enlargeArray(array.length * 2 + 1);
        }
        //上滤
        int hole = ++currentSize;
        for (array[0] = x; x.compareTo(array[hole / 2]) < 0; hole /= 2) {
            array[hole] = array[hole / 2];
        }
        array[hole] = x;
    }

    /**
     * 扩容方法
     *
     * @param newSize :扩容后的容量,为原来的2倍+1
     */
    private void enlargeArray(int newSize) {
        T[] old = array;
        array = (T[]) new Comparable[newSize];
        System.arraycopy(old, 0, array, 0, old.length);
    }

Операцию обмена можно использовать повторно для выполнения процесса верхней фильтрации, но если вставлен верхний слой фильтрации X, требуются 3d-назначения; таким образом, нам нужно только d+1 присвоений.

Если вставленный элемент является новым наименьшим элементом и, таким образом, фильтруется до корня, то время вставки равно O(logN). Но в среднем верхний фильтр отключается раньше.Было показано, что выполнение последовательных вставок требует в среднем 2,607 сравнений, поэтому в среднем операция вставки перемещает элементы вверх на 1,607 уровня.Количество загрузок всего на единицу меньше, чем количество вставок.

  1. deleteMin (удалить наименьший элемент)
  • отфильтровать: Аналогично работе верхнего фильтра. Поскольку мы строим минимальную кучу, удаление минимального элемента означает удаление корневого узла, что разрушает структуру. такМы создаем дыру в корневом узле. Чтобы удовлетворить структуру, последний элемент X в куче должен быть перемещен в подходящую позицию. Если он может быть непосредственно помещен в дыру, удаление завершено (вообще невозможно); в противном случае меньший из левого и правого сыновей дыры перемещается в дыру, то есть дыра смещается на один слой вниз. Продолжайте делать это до тех пор, пока X не сможет быть помещен в полость.Таким образом, структура и порядок кучи могут быть удовлетворены. Этот процесс называется фильтрацией вниз.
删除最小元
удалить самый маленький элемент
完成删除最小元
Завершите удаление наименьшего элемента

как показано на рисунке:В корне устанавливается отверстие, и последний элемент помещается в отверстие, которое удовлетворило структуру; для того, чтобы удовлетворить порядок укладки, отверстие необходимо переместить вниз в подходящее положение.

Примечание: кучаПри реализации часто встречающаяся ошибкатолько четное количество элементов,То есть есть узел только с одним ребенком.такНеобходимо проверить существование правильного сына.

/**
     * 删除最小元
     * 若优先队列为空,抛出UnderflowException
     *
     * @return :返回最小元
     */
    public T deleteMin() {
        if (isEmpty()) {
            throw new UnderflowException();
        }

        T minItem = findMin();
        array[1] = array[currentSize--];
        percolateDown(1);

        return minItem;
    }

     /**
     * 下滤方法
     *
     * @param hole :从数组下标hole1开始下滤
     */
    private void percolateDown(int hole) {
        int child;
        T tmp = array[hole];

        for (; hole * 2 <= currentSize; hole = child) {
            //左儿子
            child = hole * 2;
            //判断右儿子是否存在
            if (child != currentSize &&
                    array[child + 1].compareTo(array[child]) < 0) {
                child++;
            }
            if (array[child].compareTo(tmp) < 0) {
                array[hole] = array[child];
            } else {
                break;
            }
        }
        array[hole] = tmp;
    }

Наихудшая временная сложность этой операции — O(logN). В среднем элементы, помещенные в корень, фильтруются почти до нижнего слоя (то есть слоя, из которого они пришли), поэтому средняя временная сложность составляет O(logN).

4. Резюме

Очереди с приоритетами часто реализуются с использованием двоичных куч.Эта статья иллюстрирует две самые основные операции с двоичными кучами: вставка и удаление минимальных элементов. insert выполняется за O(1) постоянное время, а deleteMin выполняется за O(logN).Я считаю, что после прочтения вы можете посмотреть исходный код PriorityQueue в java. Сегодня я рассказал только о самых основных операциях с бинарной кучей, а некоторые дополнительные операции и анализ будут рассмотрены в следующий раз. Например, как доказать, что buildHeap является линейным? И применение приоритетной очереди и т.д.

Отказ от ответственности: Все изображения и тексты являются оригинальными, если они воспроизведены, пожалуйста, укажите источник. Если есть какие-либо ошибки, пожалуйста, помогите указать, добро пожаловать на обсуждение;