Простой и удобный анализ временной и пространственной сложности для Heapsort

Мне не удалось найти простой и понятный анализ временной или пространственной сложности для Heapsort. Это не повторяющийся вопрос в stackoveflow.

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

Сравним с алгоритмом сортировки слиянием.

Сортировка слиянием

void mergeSort(int arr[], int l, int r)    // T(n), n being the number of elements in arr
{ 
    if (l < r) 
    { 
        ........ // constant operations

        mergeSort(arr, l, m);     // T(n/2) since we apply it on aprox half
        mergeSort(arr, m+1, r);   // T(n/2) since we apply it on aprox half

        merge(arr, l, m, r);    // c*n , out of scope to analyse merge() 
    } 
} 

Цель состоит в том, чтобы проанализировать сложность, а не сам алгоритм: https://www.geeksforgeeks.org/merge-sort/

Следовательно, если n — количество элементов массива, а c — константа, мы имеем:

T(n) = c, if n=1
T(n) = 2*T(n/2) + c*n, if n>1
so we can rewrite
T(n) = 2*( 2*T(n/4) + c*n/2 ) +c*n
T(n) = 4*T(n/4) + 2cn
T(n) = 4*( 2T(n/8) + c*n/4 ) + 2cn
T(n) = 8*T(n/8) + 3c
...
T(n) = 2^k * T(n/2^k) + kcn
if we use T(1) that we know => 1 = n/2^k => k = log(n) =>
T(n) = 2^(log(n)) * T(1) + nlog(n)
T(n) = n + nlog(n)
=> nlog(n)

Полное объяснение: https://www.youtube.com/watch?v=0nlPxaC2lTw&list=PL2_aWCzGMAwKedT2KfDMB9YA5DgASZb3U&index=6

Теперь давайте рассмотрим пример пирамидальной сортировки.

Я упоминаю, что уже проверил stackoverflow, но четкого ответа нет: Анализ скорости и память для heapsort или heapsort - сложность реализации

Heapsort

Цель состоит в том, чтобы проанализировать сложность, а не сам алгоритм: https://www.geeksforgeeks.org/heap-sort/

// main function to do heap sort 
void heapSort(int arr[], int n) 
{ 
    // Build heap (rearrange array) 
    for (int i = n / 2 - 1; i >= 0; i--) 
        heapify(arr, n, i);                       // n/2 * TH(n)

    // One by one extract an element from heap 
    for (int i=n-1; i>0; i--) 
    { 
        // Move current root to end 
        swap(arr[0], arr[i]); 

        // call max heapify on the reduced heap 
        heapify(arr, i, 0);              // (n-1) * TH(i)
    } 
} 

T(n) = c, if n=1
TH is the time complexity for heapify()
T(n) = n/2 * TH(n) + (n-1) * TH(i) , where i is decreasing from n-1 to 1

Проблема в том, как рассчитать TH(n) = log(n). Потому что на его основе T(n) = n*log(n)

Поэтому код для heapify, для которого нам нужно вычислить TH(n):

// To heapify a subtree rooted with node i which is 
// an index in arr[]. n is size of heap 
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; // Initialize largest as root 
    int l = 2*i + 1; // left = 2*i + 1 
    int r = 2*i + 2; // right = 2*i + 2 

    // If left child is larger than root 
    if (l < n && arr[l] > arr[largest]) 
        largest = l; 

    // If right child is larger than largest so far 
    if (r < n && arr[r] > arr[largest]) 
        largest = r; 

    // If largest is not root 
    if (largest != i) 
    { 
        swap(arr[i], arr[largest]); 

        // Recursively heapify the affected sub-tree 
        heapify(arr, n, largest);       //  how to express TH in terms of n ?
    } 
}

TH(n) = c, if n=1
worst case when the largest if never root:
TH(n) = c + ? * TH( ? )

как написать рекурсивное выражение?

Существует также логическая дедукция, но давайте избежим этой стратегии:

There are log(n) levels. 
Half of the elements will be on the last layer, not touched by heapify().
So log(n) elements are touched. 
This might be a good strategy, but not simple or friendly enough.
  1. Пространственная сложность

Как вычислить O(1) как пространственную сложность, если мы используем кучу из n элементов? Или, возможно, значение O(1) неверно: ссылка https://www.bigocheatsheet.com/.


person user3742309    schedule 06.06.2020    source источник
comment
Высота полного бинарного дерева, содержащего n элементов, равна log(n). Чтобы полностью наполнить элемент, чьи поддеревья уже являются максимальными кучами, нам нужно продолжать сравнивать элемент с его левым и правым дочерними элементами и толкать его вниз, пока он не достигнет точки где оба его потомка меньше его. В худшем случае нам нужно будет переместить элемент из корня в конечный узел, выполнив кратное log(n) сравнение и обмен. На этапе build_max_heap мы делаем это для n/2 элементов, поэтому наихудшая сложность этапа build_heap составляет n/2*log(n) ~ nlogn.   -  person kaileena    schedule 06.06.2020