Buggy Heap Сортиране

По-долу е моят опит за Heap Sort, който се предполага, че прилича на показаното в CLRS от страница 152 и нататък.

Ако подам A = [9, 0, 5, 7, 4, 6, 3, 8, 1, 2] като вход. Резултатът от BuildMaxHeap е [9, 8, 6, 7, 4, 5, 3, 0, 1, 2]. Което изглежда правилно. Въпреки това, предаването на същия вход към HeapSort ми дава [9, 8, 4, 7, 2, 3, 5, 6, 1, 0], което е предизвикателно погрешно.

Може ли някой да хвърли малко светлина върху това, което правя погрешно?

def left(i):
    return 2 * i

def right(i):
    return 2 * i + 1

def HeapSize(A):
    return len(A) - 1 

def MaxHeapify(A, i): 
    l = left(i)
    r = right(i)
    if l <= HeapSize(A) and A[l] > A[i]:
            largest = l 
    else:
            largest = i 
    if r <= HeapSize(A) and A[r] > A[largest]:
            largest = r 
    if largest != i:
            A[i], A[largest] = A[largest], A[i]
            MaxHeapify(A, largest)

def BuildMaxHeap(A):
    for i in range(HeapSize(A) // 2, 0, -1):
            MaxHeapify(A, i)

def HeapSort(A):
    BuildMaxHeap(A)
    for i in range(HeapSize(A), 1, -1):
            A[i], A[1] = A[1], A[i]
            MaxHeapify(A, 1)

person user672009    schedule 14.05.2014    source източник
comment
Мисля, че един от проблемите може да е, че имате грешка "изключване по едно". Индексите на масиви в Python са базирани на 0, докато ако си спомням правилно, те са базирани на 1 в CLRS. И така, A[i], A[1] = A[1], A[i] трябва да бъде A[i], A[0] = A[0], A[i] и вашето range трябва да слезе до 0. Все още не съм пуснал кода, това е просто мисъл.   -  person s16h    schedule 15.05.2014
comment
Вие сте прав и докато актуализирахте коментара си, аз актуализирах своя код. Мислех, че вече съм го отчел в моите диапазони... но brb.   -  person user672009    schedule 15.05.2014
comment
Вашето MaxHeapify изглежда неправилно. Ако A = [0, 5, 7, 4, 6, 3, 8, 1, 2, 9] и извикате MaxHeapify(A, 0), това ще го промени на [5, 7, 6, 4, 9, 3, 8, 1, 2, 0], което е грешно, нали?   -  person s16h    schedule 15.05.2014
comment
Да, така мисля. Но моите леви и десни функции бяха грешни и сега току-що ги актуализирах... но все още грешен краен резултат.   -  person user672009    schedule 15.05.2014
comment
Да, сега са добре. Стъпка по стъпка :-) Не забравяйте, че range(HeapSize(A), 1, -1) трябва да бъде range(HeapSize(A), 0, -1).   -  person s16h    schedule 15.05.2014
comment
Също така, не трябва да продължавате да коригирате кода си, като го редактирате тук. Това ще направи въпроса безполезен за следващия човек, който има същия проблем и иска да види какво не е наред с вашия код.   -  person s16h    schedule 15.05.2014
comment
Актуализирах обхвата локално. Ще променя кода тук... Имам го в git. Все пак грешен резултат.   -  person user672009    schedule 15.05.2014
comment
нека да продължим тази дискусия в чата   -  person s16h    schedule 15.05.2014


Отговори (1)


Както видях, някои корекции на вашите оригинални кодове (проверете коментарите):

def left(i):
    return 2 * i      # this is 1-based tree. Since your first element is stored in 0th cell, here is an error

def right(i):
    return 2 * i + 1    # same, this's 1-based

def HeapSize(A):
    return len(A) - 1 

def MaxHeapify(A, i): 
    l = left(i)
    r = right(i)
    if l <= HeapSize(A) and A[l] > A[i]:
            largest = l       # always keep 4 white space (or tab) as indent in Python, don't mix up
    else:
            largest = i 
    if r <= HeapSize(A) and A[r] > A[largest]:
            largest = r 
    if largest != i:
            A[i], A[largest] = A[largest], A[i]
            MaxHeapify(A, largest)

def BuildMaxHeap(A):
    for i in range(HeapSize(A) // 2, 0, -1):     # the last non-leaf element is (HeapSize(A)-1) // 2
            MaxHeapify(A, i)

def HeapSort(A):
    BuildMaxHeap(A)
    for i in range(HeapSize(A), 1, -1):   # e.g. range(3,1,-1) gives you [3,2], you miss the 1st element
            A[i], A[1] = A[1], A[i]     # swap with A[0], unless you don't use the 0th element of array
            MaxHeapify(A, 1)      # here is essential error for the algorithm. Since you're doing in-place sorting, you'd not swap the largest element to end of heap then MaxHeapify the WHOLE ARRAY again. I.e. you have to MaxHeapify the heap EXCLUDING the last element

Тогава по-долу е работеща версия, базирана на вашите кодове:

def left(i):
    return 2 * i + 1

def right(i):
    return 2 * i + 2

def parent(i):     # your 'left' and 'right' function is good practice, keep it
    return (i - 1) // 2

def MaxHeapify(A, heap_size, i): 
    l = left(i)
    r = right(i)
    if l <= heap_size and A[l] > A[i]:
        largest = l 
    else:
        largest = i 
    if r <= heap_size and A[r] > A[largest]:
        largest = r 
    if largest != i:
        A[i], A[largest] = A[largest], A[i]
        MaxHeapify(A, heap_size, largest)      # important: don't always sort the whole array (size of heap keeps decreasing in 'HeapSort')

def BuildMaxHeap(A):
    heap_size = len(A) - 1    # index of last element of the heap
    for i in range(parent(heap_size), -1, -1):    # don't miss i=0 iteration
        MaxHeapify(A, heap_size, i)

def HeapSort(A):
    BuildMaxHeap(A)
    heap_size = len(A) - 1
    for i in range(heap_size, 0, -1):    # ends at i=1. You don't swap A[0] with A[0], right?
        A[i], A[0] = A[0], A[i]
        MaxHeapify(A, i-1, 0)     # careful here: MaxHeapify which part of the array

Резултат от теста:

>>> a = [9, 0, 5, 7, 4, 6, 3, 8, 1, 2]
>>> BuildMaxHeap(a)
>>> a
[9, 8, 6, 7, 4, 5, 3, 0, 1, 2]
>>> HeapSort(a)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
person ZZY    schedule 15.05.2014
comment
Чрез предаване на heap_size на MaxHeapify избягваме сортирането на целия масив, нали? - person user672009; 16.05.2014
comment
Правилно, това е целта - person ZZY; 17.05.2014