Пытаюсь понять max heapify

Я пытался смотреть http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-4-куча-и-куча-сортировка/, чтобы понять кучи и heapsort, но не нашел это ясным.

Я не понимаю функцию max-heapify. Это похоже на рекурсивную функцию, но почему-то говорят, что она работает за логарифмическое время из-за высоты дерева.

Для меня это не имеет смысла. В худшем случае не придется ли реверсировать каждый отдельный узел? Я не понимаю, как это можно сделать, не затрагивая каждый отдельный узел неоднократно.


person DoubleBass    schedule 28.01.2016    source источник
comment
Я использую книгу CLRS, и MAX_HEAPIFY четко указано как время O (N), что является простым доказательством. Не могли бы вы сказать, в какой части видео говорится, что это O (log N)?   -  person Aravind    schedule 28.01.2016
comment
@Aravind Начните с 26:06 и смотрите оттуда   -  person DoubleBass    schedule 28.01.2016
comment
@beaker: я перепутал max_heapify и build_max_heap, мой плохой.   -  person Aravind    schedule 28.01.2016
comment
@Aravind Не беспокойтесь, я перепутал max_heapify и вставил в комментарии ниже. ;)   -  person beaker    schedule 28.01.2016


Ответы (3)


Вот что делает MAX-HEAPIFY:

Учитывая узел с индексом i, чьи левое и правое поддеревья являются максимальными кучами, MAX-HEAPIFY перемещает узел с индексом i вниз по максимальной куче до тех пор, пока он не перестанет нарушать максимальное значение. -heap свойство (то есть узел не меньше своих дочерних элементов).

Самый длинный путь, который может пройти узел, прежде чем он окажется в правильном положении, равен начальной высоте узла. Каждый раз, когда узлу нужно спуститься еще на один уровень в дереве, алгоритм выберет ровно одну ветвь и никогда не вернется назад. Если узел, к которому добавляется куча, является корнем максимальной кучи, то самый длинный путь, который он может пройти, равен высоте дерева или O(log n).

MAX-HEAPIFY перемещает только один узел. Если вы хотите преобразовать массив в максимальную кучу, вы должны убедиться, что все поддеревья имеют максимальную кучу, прежде чем переходить к корню. Вы делаете это, вызывая MAX-HEAPIFY на n/2 узлах (листья всегда удовлетворяют свойству max-heap).

Из КЛРС:

for i = floor(length(A)/2) downto 1
   do MAX-HEAPIFY(A,i)

Поскольку вы вызываете MAX-HEAPIFY O(n) раза, построение всей кучи занимает O(n log n).*

* Как упоминалось в комментариях, можно показать более точную верхнюю границу O(n). Анализ см. в разделе 6.3 2-го и 3-го изданий CLRS. . (Мое первое издание упаковано, поэтому я не смог проверить номер раздела.)

person beaker    schedule 28.01.2016
comment
На самом деле создание всей кучи - это O (n), если вы более тщательно вычисляете временную сложность. - person Zeno Rogue; 11.05.2020
comment
@ZenoRogue Да, более жесткую верхнюю границу можно найти, принимая во внимание глубину, на которую вставлен каждый узел. Вы можете найти анализ во втором издании CLRS в разделе 6.3, который воспроизводится здесь в разделе 1.3.2. - person beaker; 11.05.2020
comment
@ZenoRogue Я добавил примечание к своему ответу. Пожалуйста, дайте мне знать, если я что-то пропустил. - person beaker; 11.05.2020
comment
Выглядит отлично! (У меня тоже нет доступа к номерам секций.) - person Zeno Rogue; 12.05.2020

В худшем случае не придется ли реверсировать каждый отдельный узел?

Вам не нужно проходить через каждый узел. Стандартный алгоритм max-heapify: (взято из Википедии)

Max-Heapify (A, i):
    left ← 2*i  // ← means "assignment"
    right ← 2*i + 1
    largest ← i

    if left ≤ heap_length[A] and A[left] > A[largest] then:
        largest ← left
    if right ≤ heap_length[A] and A[right] > A[largest] then:
        largest ← right

    if largest ≠ i then:
        swap A[i] and A[largest]
    Max-Heapify(A, largest)

Вы можете видеть, что при каждом рекурсивном вызове вы либо останавливаетесь, либо продолжаете работу с поддеревом left или right. В последнем случае вы уменьшаете высоту дерева с помощью 1. Поскольку дерево кучи сбалансировано по определению, вы должны сделать не более log(N) шагов.

person sve    schedule 28.01.2016
comment
Теперь я еще больше запутался, потому что @Aravind сказал, что в CLRS это отображается как время O (N) - person DoubleBass; 28.01.2016
comment
Я даю вам случайный список [7, 9, 8, 15, 13, 11, 10, 5, 6, 1, 14, 3, 2, 4, 12] (узел первого уровня 1, узлы второго уровня 2, узлы третьего уровня 4, узлы четвертого уровня 8), вы говорите, что можете преобразовать его в максимальную кучу, не касаясь всех элементов? - person DoubleBass; 28.01.2016
comment
@DoubleBass Вам не нужно трогать все элементы, а только элементы на одном пути между листом, где элемент впервые добавляется к корню. Как сказано в CLRS, это можно представить по-другому: O(h), где h — высота дерева. - person beaker; 28.01.2016
comment
Когда я вызываю max_heapify в своем списке, результирующий список не кажется максимальной кучей - person DoubleBass; 28.01.2016
comment
Была ли это максимальная куча раньше? Потому что, если остальная часть дерева не является кучей, вы не можете ожидать, что куча одного узла исправит все это. (Обратите внимание, что я неправильно указал направление пути выше. Значение, которое увеличивается, перемещается от корня к листу.) - person beaker; 28.01.2016
comment
Тогда я не понимаю, что должен делать max heapify. Как будто у меня есть случайный список, и я хочу превратить его в максимальную кучу. - person DoubleBass; 28.01.2016
comment
@DoubleBass Я добавил ответ, так как он стал слишком длинным, чтобы поместиться в комментарий. - person beaker; 28.01.2016

Вот аргумент, почему это O (N).

Предположим, что это полная куча, поэтому у каждого нелистового узла есть два потомка. (Это все еще работает, даже если это не так, но это больше раздражает.)

Положите монету на каждый узел дерева. Каждый раз, когда мы делаем обмен, мы собираемся потратить одну из этих монет. (Обратите внимание, что когда элементы меняются местами в куче, монеты не меняются местами с ними.) Если мы запустим MAX-HEAPIFY и останутся монеты, это означает, что мы сделали меньше свопов, чем узлов в дереве. и, таким образом, MAX-HEAPIFY выполняет O(N) свопов.

Утверждение: после завершения работы MAX-HEAPIFY в куче всегда будет хотя бы один путь от корня к листу с монетами на каждом узле пути.

Доказательство по индукции: для кучи с одним узлом нам не нужно делать никаких обменов, поэтому нам не нужно тратить монеты. Таким образом, один узел сохраняет свою монету, и у нас есть полный путь от корня до листа (длиной 1) с целой монетой.

Теперь предположим, что у нас есть куча с левой и правой подкучами, и MAX-HEAPIFY уже выполняется на обеих. Согласно индуктивной гипотезе, у каждого есть хотя бы один путь от корня к листу с монетами на нем, поэтому у нас есть как минимум два пути от корня к листу с монетами, по одному для каждого потомка. Самое большее, что нужно будет пройти корню, чтобы установить свойство MAX-HEAP, — это переместиться до конца дерева. Допустим, он переключается вниз в левое поддерево и переключается полностью вниз. Для каждого обмена нам нужно тратить монету, поэтому мы тратим ее с узла, на который перешел корень.

При этом мы потратили все монеты на один путь от корня к листу, но помните, что изначально у нас было как минимум два! Таким образом, у нас все еще есть путь от корня к листу с монетами после того, как MAX-HEAPIFY запустит всю кучу. Следовательно, MAX-HEAPIFY потратил меньше монет, чем узлов в дереве. Следовательно, количество свопов равно O(N). КЭД.

person hjfreyer    schedule 11.02.2021