Як побудова купи може бути O (n) часовою складністю?


494

Чи може хтось допомогти пояснити, як може скласти купу складати O (n) складність?

Вставлення елемента в купу є O(log n), і вставка повторюється n / 2 рази (решта - це листя, і вони не можуть порушити властивість купи). Отже, це означає, що складність повинна бути O(n log n), я думаю.

Іншими словами, для кожного елемента, який ми «накопичуємо», він має можливість відфільтрувати один раз для кожного рівня нагромадження досі (що це n n рівнів).

Що я пропускаю?


що саме ви маєте на увазі під "побудовою" купи?
mfrankli

Як і в групі, візьміть несортований масив і відфільтровуйте кожен елемент верхньої половини, поки він не відповідає правилам купи
GBa

2
Єдине, що я міг знайти, це посилання: Схоже, складність Buildheap становить Θ (n lg n) - n дзвінків до Heapify ціною Θ (lg n) за виклик, але цей результат можна покращити до Θ (n) cs.txstate.edu/~ch04/webtest/teaching/courses/5329/lectures/…
GBa

2
@Gba дивіться це відео з MIT: Він добре пояснює, як ми отримуємо O (n), з lil трохи математики youtube.com/watch?v=B7hVxCmfPtM
CodeShadow

2
Пряме посилання на пояснення @CodeShadow згадане: youtu.be/B7hVxCmfPtM?t=41m21s
sha1

Відповіді:


435

Я думаю, що в цій темі є кілька питань:

  • Як ви реалізуєте, buildHeapщоб він працював у O (n) час?
  • Як ви показуєте, що buildHeapпрацює за O (n) час при правильній реалізації?
  • Чому ця сама логіка не працює для того, щоб змусити сортувати групу в O (n) час, а не O (n log n) ?

Як ви реалізуєте, buildHeapщоб він працював у O (n) час?

Часто відповіді на ці запитання зосереджуються на різниці між siftUpта siftDown. Роблячи правильний вибір між siftUpі siftDownмає вирішальне значення для отримання (п) O продуктивність buildHeap, але нічого не робить , щоб допомогти людині не зрозуміти різницю між buildHeapі heapSortв цілому. Дійсно, належні реалізації обох buildHeapі heapSortбудуть використовуватись тількиsiftDown . siftUpОперація необхідна тільки для виконання вставки в існуючій купу, так що він буде використовуватися для реалізації черги з пріоритетами з допомогою бінарної купи, наприклад.

Я написав це, щоб описати, як працює максимальна купа. Це тип купи, який зазвичай використовується для сортування купи або для черги пріоритетів, де більш високі значення вказують на більший пріоритет. Мінна купа також корисна; наприклад, для отримання елементів з цілими ключами у порядку зростання або рядків в алфавітному порядку. Принципи точно однакові; просто переключіть порядок сортування.

У купі властивість визначає , що кожен вузол в двійковій купі має бути , по крайней мере , як великий , як і його дітей. Зокрема, це означає, що найбільший предмет у купі знаходиться в корені. Відсівання вниз і просіювання - це по суті та сама операція в протилежних напрямках: переміщуйте порушуючий вузол, поки він не задовольнить властивість купи:

  • siftDown міняє занадто малий вузол зі своїм найбільшим дочірнім пристроєм (тим самим переміщуючи його вниз), поки він не буде настільки ж великим, як обидва вузли під ним.
  • siftUp поміняє занадто великий вузол зі своїм батьком (тим самим переміщуючи його вгору), поки він не стане більшим, ніж вузол над ним.

Кількість операцій, необхідних для siftDownта siftUpпропорційна відстані, яку, можливо, доведеться перемістити. Бо siftDownце відстань до дна дерева, тому siftDownдороге для вузлів на верхівці дерева. З siftUp, робота пропорційна відстані до верхівки дерева, тому siftUpдорога для вузлів у нижній частині дерева. Хоча обидві операції є O (log n) у гіршому випадку, у купі, лише один вузол знаходиться вгорі, тоді як половина вузлів лежить у нижньому шарі. Тож не повинно бути занадто дивним, що якщо нам доведеться застосувати операцію до кожного вузла, ми віддамо перевагу siftDownзакінченню siftUp.

buildHeapФункція приймає масив невпорядкованих елементів і переміщує їх , поки вони все не задовольняють кучного власності, в результаті чого отримують дійсну купу. Можна використовувати два підходи для buildHeapвикористання siftUpта siftDownописаних нами операцій.

  1. Почніть з верхньої частини купи (початок масиву) і зателефонуйте siftUpдо кожного елемента. На кожному кроці попередньо просіяні елементи (елементи перед поточним елементом у масиві) утворюють дійсну купу, а просіювання наступного елемента вгору ставить його у дійсне місце у купі. Після просіювання кожного вузла всі елементи задовольняють властивості купи.

  2. Або йдіть у зворотному напрямку: почніть з кінця масиву і рухайтеся назад вперед. При кожній ітерації ви просіюєте елемент донизу, поки він не знаходиться в потрібному місці.

Яка реалізація buildHeapє більш ефективною?

Обидва ці рішення дадуть дійсну купу. Не дивно, що більш ефективною є друга операція, яка використовується siftDown.

Нехай h = log n позначає висоту купи. Робота, необхідна для siftDownпідходу, задається сумою

(0 * n/2) + (1 * n/4) + (2 * n/8) + ... + (h * 1).

Кожен доданок у сумі має максимальну відстань, яку повинен буде перемістити вузол на заданій висоті (нуль для нижнього шару, h для кореня), помножений на кількість вузлів на цій висоті. На відміну від цього, сума для виклику siftUpна кожному вузлі є

(h * n/2) + ((h-1) * n/4) + ((h-2)*n/8) + ... + (0 * 1).

Повинно бути зрозуміло, що друга сума більша. Перший додаток лише hn / 2 = 1/2 n log n , тому такий підхід має складність у кращому випадку O (n log n) .

Як ми доводимо, що сума за siftDownпідхід справді є O (n) ?

Один з методів (є й інші аналізи, які також працюють) - перетворити кінцеву суму в нескінченний ряд, а потім використовувати ряд Тейлора. Ми можемо ігнорувати перший термін, який дорівнює нулю:

Серія Тейлора для складності buildHeap

Якщо ви не впевнені, чому працює кожен з цих кроків, ось виправдання процесу словами:

  • Усі умови позитивні, тому кінцева сума повинна бути меншою, ніж нескінченна сума.
  • Серія дорівнює ряду потужностей, оцінених за x = 1/2 .
  • Цей силовий ряд дорівнює (постійному часу) похідній ряду Тейлора для f (x) = 1 / (1-x) .
  • x = 1/2 знаходиться в інтервалі зближення цього ряду Тейлора.
  • Тому ми можемо замінити ряд Тейлора на 1 / (1-x) , диференціювати та оцінити, щоб знайти значення нескінченного ряду.

Оскільки нескінченна сума рівно n , ми робимо висновок, що кінцева сума не більша, і тому O (n) .

Чому для сортування купи потрібен O (n log n) час?

Якщо можливо запустити buildHeapв лінійний час, чому сортування купи вимагає часу O (n log n) ? Ну, купа купи складається з двох етапів. По-перше, ми закликаємо buildHeapмасив, який вимагає часу O (n), якщо його оптимально реалізувати. Наступним етапом є багаторазове видалення найбільшого елемента з купи та розміщення його в кінці масиву. Оскільки ми видаляємо предмет з купи, завжди є відкрите місце після закінчення купи, де ми можемо зберігати предмет. Таким чином, купа купівлі досягає відсортованого порядку, послідовно видаляючи наступний найбільший елемент і поміщаючи його в масив, починаючи з останньої позиції та рухаючись у напрямку спереду. Саме складність цієї останньої частини домінує в купі сортів. Петля виглядає так:

for (i = n - 1; i > 0; i--) {
    arr[i] = deleteMax();
}

Зрозуміло, що цикл працює O (n) разів ( n - 1, якщо бути точним, останній елемент вже на місці). Складність deleteMaxдля купи становить O (log n) . Зазвичай він реалізується, видаляючи корінь (найбільший елемент, що залишився в купі) і замінюючи його останнім елементом у купі, який є листом, а отже, одним із найменших елементів. Цей новий корінь майже напевно порушить властивість купи, тому вам доведеться дзвонити, siftDownпоки ви не перемістите його назад у прийнятну позицію. Це також призводить до переміщення наступного найбільшого елемента до кореня. Зауважте, що, на відміну від того, buildHeapде для більшості вузлів ми дзвонимо siftDownз низу дерева, ми зараз телефонуємо siftDownзверху дерева на кожній ітерації!Хоча дерево скорочується, воно не скорочується досить швидко : висота дерева залишається незмінною, поки ви не видалите першу половину вузлів (коли повністю очистите нижній шар). Тоді для наступної чверті висота h - 1 . Тож загальна робота для цього другого етапу

h*n/2 + (h-1)*n/4 + ... + 0 * 1.

Помітьте перемикач: тепер нульовий робочий випадок відповідає одному вузлу, а h робочий випадок відповідає половині вузлів. Ця сума є O (n log n) так само, як неефективна версія buildHeap, реалізована за допомогою siftUp. Але в цьому випадку у нас немає вибору, оскільки ми намагаємося сортувати, і нам потрібно видалити наступний найбільший елемент.

Підсумовуючи, робота для сортування купи - це сума двох етапів: O (n) час для buildHeap та O (n log n) для видалення кожного вузла в порядку , тому складність становить O (n log n) . Ви можете довести (використовуючи деякі ідеї з інформаційної теорії), що для сортування на основі порівняння O (n log n) - найкраще, на що ви могли сподіватися, так що немає жодних причин для цього розчаровуватись чи сподіватися на нагромадження купи, щоб досягти досягнення O (n) обмежений час, що buildHeapробить.


2
Я відредагував свою відповідь на використання максимальної купи, оскільки, здається, більшість інших людей посилаються на це, і це найкращий вибір для сортування купи.
Джеремі Вест

28
Ось що мені стало зрозуміло інтуїтивно: "лише один вузол знаходиться вгорі, тоді як половина вузлів лежить у нижньому шарі. Тож не повинно бути дивно, що якщо нам доведеться застосувати операцію до кожного вузла, ми б віддайте перевагу siftDown над siftUp. "
Vicky Chijwani

3
@JeremyWest "Потрібно починати у верхній частині купи (початок масиву) і викликати siftUp для кожного елемента." - Ти мав на увазі почати внизу купи?
aste123

4
@ aste123 Ні, це правильно, як написано. Ідея полягає у підтримці бар'єру між частиною масиву, яка задовольняє властивість купи та несортованою частиною масиву. Ви або починаєте спочатку рухатись вперед і телефонувати siftUpпо кожному предмету, або починаєте в кінці рухатися назад і дзвонити siftDown. Незалежно від того, який підхід ви обрали, ви вибираєте наступний елемент у несортованій частині масиву та виконуєте відповідну операцію, щоб перемістити його у дійсне положення в упорядкованій частині масиву. Єдина відмінність - продуктивність.
Джеремі Вест

2
Це найкраща відповідь, яку я коли-небудь бачив на будь-яке питання у світі. Це було так добре пояснено, мені подобалося, чи це дійсно можливо ... велике спасибі.
HARSHIL JAIN

314

Ваш аналіз правильний. Однак це не тісно.

Пояснити, чому побудувати купу - це лінійна операція, непросто, вам краще її прочитати.

Чудовий аналіз алгоритму можна побачити тут .


Основна ідея полягає в тому, що в build_heapалгоритмі фактична heapifyвартість не O(log n)на всі елементи.

Коли heapifyвикликається, час запуску залежить від того, наскільки елемент може рухатися вниз по дереву до завершення процесу. Іншими словами, це залежить від висоти елемента в купі. У гіршому випадку елемент може знизитися аж до рівня листя.

Давайте порахуємо виконану роботу рівень за рівнем.

На самому нижньому рівні є 2^(h)вузли, але ми не закликаємо heapifyжоден із них, тому робота дорівнює 0. На наступному рівні є 2^(h − 1)вузли, і кожен може рухатися вниз на 1 рівень. На 3-му рівні знизу є 2^(h − 2)вузли, і кожен може рухатися вниз на 2 рівні.

Як ви можете бачити, що не всі операції з надмірною швидкістю є O(log n), ось чому ви отримуєте O(n).


17
Це чудове пояснення ... але чому тоді сортування купи працює в O (n log n). Чому одне міркування не застосовується до нагромадження купи?
hba

49
@hba Я думаю, що відповідь на ваше запитання полягає в розумінні цього образу з цієї статті . Heapifyце O(n)коли робиться з, siftDownале O(n log n)коли робиться з siftUp. Отже, власне сортування (витягування предметів з купи один за одним) повинно здійснюватися siftUpсаме тому O(n log n).
The111

3
Мені дуже подобається інтуїтивне пояснення вашого зовнішнього документа внизу.
Лукас Греблікас

1
@hba Відповідь, поданий нижче від Джеремі Веста, вирішує ваше запитання більш тонкою, зрозумілою деталізацією, далі пояснюючи відповідь коментаря The111 тут.
cellepo

Питання. Мені здається, що # порівняння, зроблені для вузла на висоті iвід низу дерева висотою h, повинні також проводити 2* log(h-i)порівняння, і їх слід враховувати також @ The111. Як ти гадаєш?
Сид

94

Інтуїтивно:

"Складність повинна бути O (nLog n) ... для кожного елемента, який ми" нагріваємо ", він має потенціал, що доведеться один раз відфільтрувати для кожного рівня нагромадження до цих пір (що є n n рівнями)."

Не зовсім. Ваша логіка не призводить до жорсткої межі - вона оцінює складність кожної групи. Якщо побудовано знизу вгору, вставка (наддув) може бути значно меншою, ніж O(log(n)). Процес такий:

(Крок 1) Перші n/2елементи переходять на нижній ряд купи. h=0, тому нагрівати не потрібно.

(Крок 2) Наступні елементи переходять на рядок 1 вгору знизу. , нагрійте фільтри на 1 рівень вниз.n/22h=1

(Крок i ) Наступні елементи йдуть в рядку вгору знизу. , підвищити рівень фільтрів вниз.n/2iih=ii

( Журнал кроків (n) ) Останній елемент йде в рядку вгору знизу. , підвищити рівень фільтрів вниз.n/2log2(n) = 1log(n)h=log(n)log(n)

ПОВІДОМЛЕННЯ: після першого кроку 1/2елементи (n/2)вже знаходяться в купі, і нам навіть не потрібно було один раз викликати hepify. Також зауважте, що лише один елемент, корінь, насправді несе всю log(n)складність.


Теоретично:

Загальні кроки Nдля створення купи розмірів n, можна виписати математично.

На висоті iми показали (вище), що знайдуться елементи, які потрібно викликати «hepify», і ми знаємо, що «heapify» на висоті є . Це дає:n/2i+1iO(i)

введіть тут опис зображення

Рішення останнього підсумовування можна знайти, взявши похідну обох сторін добре відомого рівняння геометричного ряду:

введіть тут опис зображення

Нарешті, підключення x = 1/2до вищевказаного рівняння дає вихід 2. Підключення цього до першого рівняння дає:

введіть тут опис зображення

Таким чином, загальна кількість кроків має розмір O(n)


35

Було б O (n log n), якби ви будували купу, повторно вставляючи елементи. Однак ви можете створити нову купу ефективніше, вставивши елементи у довільному порядку, а потім застосувавши алгоритм, щоб «нагромадити» їх у належному порядку (залежно від типу купи, звичайно).

Див. Http://en.wikipedia.org/wiki/Binary_heap , "Створення купи" для прикладу. У цьому випадку ви по суті працюєте з нижнього рівня дерева, замінюючи батьківські та дочірні вузли, поки не будуть виконані умови купи.


12

Вже є кілька чудових відповідей, але я хотів би додати трохи наочного пояснення

введіть тут опис зображення

Тепер погляньте на зображенні, є
n/2^1 зелені вершини з висотою 0 (тут 23/2 = 12)
n/2^2 червоні вузли з висотою 1 (тут 23/4 = 6)
n/2^3 синім вузлом з висотою 2 (тут 23/8 = 3)
n/2^4 фіолетові вузли з висоти 3 (тут 23/16 = 2) ,
так є n/2^(h+1)вузли для висоти ч
Щоб знайти тимчасову складність дозволяє підрахувати кількість виконаної роботи або максимальна к - у ітерацій , виконуваної кожним вузлом
тепер можна помітити , що кожен вузол може виконати (щонайменше) ітерації == висота вузла

Green  = n/2^1 * 0 (no iterations since no children)  
red    = n/2^2 * 1 (heapify will perform atmost one swap for each red node)  
blue   = n/2^3 * 2 (heapify will perform atmost two swaps for each blue node)  
purple = n/2^4 * 3 (heapify will perform atmost three swaps for each purple node)   

тому для будь-яких вузлів з висотою h максимальна виконана робота - n / 2 ^ (h + 1) * h

Зараз загальна робота виконана

->(n/2^1 * 0) + (n/2^2 * 1)+ (n/2^3 * 2) + (n/2^4 * 3) +...+ (n/2^(h+1) * h)  
-> n * ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

тепер для будь-якого значення h , послідовності

-> ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

ніколи не перевищуватиме 1
Таким чином, трудомісткість ніколи не перевищить O (n) для побудови купи


7

Як ми знаємо, висота купи - це log (n) , де n - загальна кількість елементів.    Показуємо це як h.
Коли ми виконуємо операцію overpify, елементи на останньому рівні ( h ) не переміщуватимуться навіть жодного крок.
   Кількість елементів на другому останньому рівні ( h-1 ) дорівнює 2 h-1, і вони можуть переміщатися на рівні максимум 1 (під час "overpify").
   Аналогічним чином , для я - го , рівня ми маємо 2 я елементи , які можуть переміщатися привіт позиції.

Тому загальна кількість ходів = S = 2 год * 0 + 2 год-1 * 1 + 2 год-2 * 2 + ... 2 0 * год

                                               S = 2 год {1/2 + 2/2 2 + 3/2 3 + ... год / 2 год } ----------------------- -------------------------- 1
це серія AGP , для розв’язання цього поділу обидві сторони на 2
                                               S / 2 = 2 h {1/2 2 + 2/2 3 + ... год / 2 год + 1 } --------------------------------- ---------------- 2
рівняння віднімання 2 з 1 дає
                                               S / 2 = 2 год { 1/2 + 1/2 2 + 1/2 3 + ... + 1 / 2 год + год / 2 год + 1 }
                                               S = 2 ч + 1 {1/2 + 1/2 2 + 1/2 3 + ... 1/2 ч + ч / 2 ч + 1 }
Тепер 1/2 + 1/2 2 + 1/2 3 + ... + 1/2 год зменшується GP , сума якого менше 1 (коли h прагне до нескінченності, сума прагне до 1). У подальшому аналізі візьмемо верхню межу суми, яка дорівнює 1.
Це дає S = 2 h + 1 {1 + h / 2 h + 1 }
                    = 2 h + 1 + h
                    ~ 2 h + h
як h = log (n) , 2 год = n

Тому S = n + log (n)
T (C) = O (n)


6

Під час створення купи, скажімо, ви підходите знизу вгору.

  1. Ви берете кожен елемент і порівнюєте його з його дітьми, щоб перевірити, чи відповідає пара правим купі. Тож листя потрапляє в купу безкоштовно. Це тому, що у них немає дітей.
  2. Просуваючись вгору, найгірший сценарій для вузла праворуч над листям буде 1 порівнянням (при максимумі їх порівнятимуть лише з одним поколінням дітей)
  3. Просуваючись далі вгору, їх безпосередніх батьків можна на максимум порівняти з двома поколіннями дітей.
  4. Продовжуючи в тому ж напрямку, ви матимете порівняння журналу (n) для кореня в гіршому випадку. і log (n) -1 для своїх найближчих дітей, log (n) -2 для їх безпосередніх дітей тощо.
  5. Отже, підсумовуючи все це, ви переходите до чогось типу log (n) + {log (n) -1} * 2 + {log (n) -2} * 4 + ..... + 1 * 2 ^ {( logn) -1}, що є не що інше, як O (n).

2

У випадку побудови купи, ми починаємо з висоти, logn -1 (де logn - висота дерева з n елементів). Для кожного елемента, присутнього на висоті 'h', ми переходимо на максимальну висоту (logn -h) висотою вниз.

    So total number of traversal would be:-
    T(n) = sigma((2^(logn-h))*h) where h varies from 1 to logn
    T(n) = n((1/2)+(2/4)+(3/8)+.....+(logn/(2^logn)))
    T(n) = n*(sigma(x/(2^x))) where x varies from 1 to logn
     and according to the [sources][1]
    function in the bracket approaches to 2 at infinity.
    Hence T(n) ~ O(n)

1

Послідовні вставки можна описати:

T = O(log(1) + log(2) + .. + log(n)) = O(log(n!))

За Старлінг наближення, n! =~ O(n^(n + O(1)))томуT =~ O(nlog(n))

Сподіваюся, це допомагає, оптимальним способом O(n)є використання алгоритму збирання купи для заданого набору (замовлення не має значення).


1

В основному, робота проводиться лише на нелистових вузлах під час складання купи ... і виконана робота полягає в кількості заміщення, щоб задовольнити стан купи ... іншими словами (в гіршому випадку) сума пропорційна висоті вузла ... у цілому складність задачі пропорційна сумі висот усіх нелистових вузлів .. що (2 ^ h + 1 - 1) -h-1 = nh-1 = O (n)


1

@bcorso вже продемонстрував доказ аналізу складності. Але заради тих, хто ще вивчає складність аналізу, я маю це додати:

Основа вашої первісної помилки пов’язана з неправильним тлумаченням значення висловлювання, "вставлення в купу займає О (log n) час". Вставка в купу дійсно є O (log n), але ви повинні визнати, що n - розмір купи під час вставки .

У контексті вставки n об’єктів у купу, складність i-го вставлення становить O (log n_i), де n_i - розмір купи, як при вставці i. Тільки остання вставка має складність O (log n).


1

Припустимо, у вас в купі N елементів. Тоді його висота складе Log (N)

Тепер ви хочете вставити ще один елемент, тоді складність полягала б у: Log (N) , ми повинні порівняти весь шлях UP до кореня.

Тепер у вас є N + 1 елементів і висота = Журнал (N + 1)

Використовуючи індукційну техніку, можна довести, що складність вставки буде ∑logi .

Зараз використовуємо

log a + log b = log ab

Це спрощує: ∑logi = log (n!)

що насправді є O (NlogN)

Але

ми робимо тут щось не так, як і в усьому випадку ми не досягаємо на вершині. Отже, виконуючи більшість випадків, коли ми можемо виявити, що ми не йдемо навіть на півдорозі вгору по дереву. Отже, цю межу можна оптимізувати, щоб мати ще одну більш жорстку зв'язану, використовуючи математику, подану у відповідях вище.

Ця реалізація прийшла до мене після детальної роботи, хоча й експериментів над Heaps.


0

Мені дуже подобається пояснення Джеремі Вест .... інший підхід, який дуже легко зрозуміти, подано тут http://courses.washington.edu/css343/zander/NotesProbs/heapcomplexity

Оскільки, нарощування купівлі залежить від використання, залежить від підходу, який застосовується, і застосовується підхід, який залежить від суми висот усіх вузлів. Отже, щоб знайти суму висоти вузлів, яка задана S = підсумком від i = 0 до i = h (2 ^ i * (привіт)), де h = logn - висота вирішення дерева s, отримуємо s = 2 ^ (h + 1) - 1 - (h + 1), оскільки n = 2 ^ (h + 1) - 1 s = n - h - 1 = n- logn - 1 s = O (n), і тому складність накопичувальної маси становить O (n).


0

"Лінійний час, пов'язаний з побудовою Heap, можна показати, обчисливши суму висот усіх вузлів у купі, яка є максимальною кількістю пунктирних ліній. Для ідеального бінарного дерева висотою h, що містить N = 2 ^ ( h + 1) - 1 вузол, сума висот вузлів дорівнює N - H - 1. Отже, це O (N) ".


0

Доказ O (n)

Доказ не фантастичний, і досить прямо, я лише довів справу з повним бінарним деревом, результат можна узагальнити для повного бінарного дерева.


0

Ми отримуємо час виконання збірки купи, з'ясовуючи максимальний хід, який може зробити кожен вузол. Тому нам потрібно знати, скільки вузлів у кожному ряду і наскільки далеко від кожного може дійти кожен вузол.

Починаючи з кореневого вузла, кожен наступний рядок має вдвічі більше вузлів, ніж попередній ряд, тому, відповідаючи на те, як часто ми можемо подвоїти кількість вузлів, поки у нас не залишиться жодних вузлів, ми отримаємо висоту дерева. Або в математичному відношенні висота дерева - log2 (n), n - довжина масиву.

Для обчислення вузлів в одному ряду ми починаємо з задньої частини, ми знаємо, що n / 2 вузли знаходяться внизу, тому діленням на 2 ми отримуємо попередній ряд тощо.

Виходячи з цього, ми отримуємо цю формулу для підходу Siftdown: (0 * n / 2) + (1 * n / 4) + (2 * n / 8) + ... + (log2 (n) * 1)

Термін в останньому парантезі - це висота дерева, помножена на той вузол, який знаходиться в корені, термін у першому парантезі - це всі вузли в нижньому ряду, помножені на довжину, яку вони можуть пройти, 0. Та ж формула в смарт введіть тут опис зображення

Математика

Якщо повернути n назад, ми маємо 2 * n, 2 можна відкинути, оскільки його константа та тада у нас є найгіршим випадком виконання підходу Siftdown: n.


-6

думаю, що ти робиш помилку. Погляньте на це: http://golang.org/pkg/container/heap/ Побудова купи не O (n). Однак, вставляючи O (lg (n). Я припускаю, що ініціалізація O (n), якщо ви встановите розмір купи b / c, купі потрібно виділити простір і налаштувати структуру даних. Якщо у вас є n елементів, які потрібно поставити у купу тоді так, кожна вставка - lg (n) і є n елементів, тож ви отримуєте n * lg (n), як зазначено u


2
ні це не тісно. більш жорсткий аналіз видобутку купи дає вихід O (n)
emre nevayeshirazi

схоже, це оцінка. Цитата в статті, на яку він посилається, «Інтуїція полягає в тому, що більшість закликів до пересипки є дуже короткими». Однак це робить деякі припущення. Імовірно, для великої купи найгіршим сценарієм все-таки буде O (n * lg (n)), навіть якщо зазвичай ви могли б наблизитися до O (n). Але я можу помилятися
Майк Шахтер

Так, це і моя інтуїтивна відповідь, але такі посилання, як вікіпедія, "Купи з п елементами можуть бути побудовані знизу вгору в O (n)."
GBa

1
Я думав про повністю відсортовану структуру даних. Я забув конкретні властивості купи.
Майк Шахтер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.