web-dev-qa-db-fra.com

Heap Sort: comment trier?

J'essaie d'implémenter Heap Sort en Python, mais je n'arrive pas à comprendre. J'ai essayé d'implémenter ce pseudo-code , mais mon code n'est pas trié! Cela tamise un effet ridicule. Je suis enclin à penser que le problème est dans cette ligne:

permuter la racine (valeur maximale) du segment avec le dernier élément du segment

Comment puis-je obtenir la valeur maximale?

C'est ce que j'ai

def my_heap_sort(sqc):                    
    def heapify(count):                
        start = (count-2)/2            
        while start >= 0:              
            sift_down(start, count-1)  
            start -= 1                 

    def swap(i, j):                    
        sqc[i], sqc[j] = sqc[j], sqc[i]

    def sift_down(start, end):         
        root = start                   

        while (root * 2 + 1) <= end:   
            child = root * 2 + 1       
            temp = root                
            if sqc[temp] < sqc[child]: 
                temp = child+1         
            if temp != root:           
                swap(root, temp)       
                root = temp            
            else:                      
                return                 

    count = len(sqc)                   
    heapify(count)                     

    end = count-1                      

    while end > 0:                     
        swap(end, 0)                   
        end -= 1                       
        sift_down(0, end)              

Et j'ai trouvé un exemple avec presque le même problème:

def heap_sort_example(a):                                 
    def heapify(a):                                       
        start = (len(a) - 2) / 2                          
        start -= 1                                        

    def sift_down(a, start, end):                         
        root = start                                      
        while root * 2 + 1 <= end:                        
            child = root * 2 + 1                          
            if child + 1 <= end and a[child] < a[child+1]:
                child += 1                                
            if child <= end and a[root] < a[child]:       
                a[root], a[child] = a[child], a[root]     
                root = child                              
            else:                                         
                return                                    

    heapify(a)                                            
    end = len(a) - 1                                      
    while end > 0:                                        
        a[end], a[0] = a[0], a[end]                       
        sift_down(a, 0, end-1)                            
        end -= 1                                          

Les résultats sont différents, mais les deux sont ridicules:

>>> my_heap_sort(sqc)
[2, 7, 1, -2, 56, 5, 3]

>>> heap_sort_example(sqc)
[-2, 1, 7, 2, 56, 5, 3]
13
I159

Je l'ai trouvé et j'ai presque compris comment cela fonctionne:

def heapsort(sqc):                                 
    def down_heap(sqc, k, n):                            
        parent = sqc[k]                                  

        while 2*k+1 < n:                                 
            child = 2*k+1                                
            if child+1 < n and sqc[child] < sqc[child+1]:
                child += 1                               
            if parent >= sqc[child]:                     
                break                                    
            sqc[k] = sqc[child]                          
            k = child                                    
        sqc[k] = parent                                  

    size = len(sqc)                                      

    for i in range(size/2-1, -1, -1):                    
        down_heap(sqc, i, size)                          

    for i in range(size-1, 0, -1):                       
        sqc[0], sqc[i] = sqc[i], sqc[0]                  
        down_heap(sqc, 0, i)                             

modifier:

Cette implémentation est écrite en fonction de ma propre compréhension de l'algorithme. C'est plus long, mais pour moi cet algorithme est beaucoup plus clair dans cette implémentation. Nommer longtemps est une aide lorsque vous avez besoin de comprendre l'algorithme, alors j'ai laissé intacts tous les noms longs.

def heapsort(sequence):                                                      
    sequence_length = len(sequence)                                          

    def swap_if_greater(parent_index, child_index):                          
        if sequence[parent_index] < sequence[child_index]:                   
            sequence[parent_index], sequence[child_index] =\                 
                    sequence[child_index], sequence[parent_index]            

    def sift(parent_index, unsorted_length):                                 
        index_of_greater = lambda a, b: a if sequence[a] > sequence[b] else b
        while parent_index*2+2 < unsorted_length:                            
            left_child_index = parent_index*2+1                              
            right_child_index = parent_index*2+2                             

            greater_child_index = index_of_greater(left_child_index,         
                    right_child_index)                                       

            swap_if_greater(parent_index, greater_child_index)               

            parent_index = greater_child_index                               

    def heapify():                                                           
        for i in range((sequence_length/2)-1, -1, -1):                       
            sift(i, sequence_length)                                         

    def sort():                                                              
        count = sequence_length                                              
        while count > 0:                                                     
            count -= 1                                                       
        swap_if_greater(count, 0)                                        
        sift(0, count)                                                   

    heapify()                                                                
    sort()                                                        

modifier:

Et version optimisée:

def opt_heapsort(s):                               
    sl = len(s)                                    

    def swap(pi, ci):                              
        if s[pi] < s[ci]:                          
            s[pi], s[ci] = s[ci], s[pi]            

    def sift(pi, unsorted):                        
        i_gt = lambda a, b: a if s[a] > s[b] else b
        while pi*2+2 < unsorted:                   
            gtci = i_gt(pi*2+1, pi*2+2)            
            swap(pi, gtci)                         
            pi = gtci                              
    # heapify                                      
    for i in range((sl/2)-1, -1, -1):              
        sift(i, sl)                                
    # sort                                         
    for i in range(sl-1, 0, -1):                   
        swap(i, 0)                                 
        sift(0, i)                                 
1
I159

Comment puis-je obtenir la valeur maximale? Vous n'avez pas besoin de "l'obtenir". La racine est exactement le maximum, c'est une propriété définie d'un tas.

Si vous avez du mal à comprendre le tri en tas, ce chapitre sera extrêmement utile.


J'ai réécrit ton code:

def swap(i, j):                    
    sqc[i], sqc[j] = sqc[j], sqc[i] 

def heapify(end,i):   
    l=2 * i + 1  
    r=2 * (i + 1)   
    max=i   
    if l < end and sqc[i] < sqc[l]:   
        max = l   
    if r < end and sqc[max] < sqc[r]:   
        max = r   
    if max != i:   
        swap(i, max)   
        heapify(end, max)   

def heap_sort():     
    end = len(sqc)   
    start = end // 2 - 1 # use // instead of /
    for i in range(start, -1, -1):   
        heapify(end, i)   
    for i in range(end-1, 0, -1):   
        swap(i, 0)   
        heapify(i, 0)   

sqc = [2, 7, 1, -2, 56, 5, 3]
heap_sort()
print(sqc)

Il donne:

[-2, 1, 2, 3, 5, 7, 56]  
17
Skyler

Si vous utilisez Push and Pop ou utilisez la librairie heapq intégrée, essayez la solution documentée:

def heapsort(iterable):
    h = []
    for value in iterable:
        heappush(h, value)
    return [heappop(h) for i in range(len(h))]

heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4
Jason

le tri par sélection est un algorithme de tri relativement simple: parcourir un tableau, extraire le minimum des n premiers éléments, puis extraire min des n-1 éléments suivants ...... Maintenant ce serait un algorithme O (n ^ 2). 

Puisque vous extrayez toujours une minute, vous devriez penser à utiliser un tas minimum. il extrait une minute en temps O (log n). l'extraction des min n fois conduit à O (n * log n) fois.

ainsi, pour le tri de tas, il suffit de construire un tas (heapify O(n)) et de parcourir le tableau et d'extraire le min n fois.

vous pouvez utiliser python heap pour créer un tas ou créer le vôtre.

def heapsort(l):
    hp = make_heap(l)
    for i in range(len(l)):
       yield hp.extract_min() 
1
locojay

Je trouve que les différentes implémentations de heapify, le "cœur" de la sorte de tas, ne sont pas claires sur Internetz. Voici mon humble tentative d’aider la communauté en ajoutant un exemple simple mais clair de "heapify". J'utilise des vecteurs pour éviter la confusion supplémentaire liée à la manipulation de tableaux.

Cette méthode compile 1 cellule du tableau. Pour heapifier le tableau entier dont vous avez besoin d'une boucle, L'exécutez à partir du milieu du tableau, en allant au début. Le vecteur retourné DOIT être le même que nous envoyons dans la prochaine itération, sinon c'est un gâchis. par exemple:

for (int i = myvector.size()/2; i >= 0; i--) { in = Heapify(in, i);}

vector_of_int Sort::Heapify(vector_of_int in_vector, int in_index)
{

int min_index = in_index; // Track index of smallest out of parent and two children.
int left_child_index = 0; 
int right_child_index = 0;
int vector_size = in_vector.size(); 

left_child_index = LeftChildIndex(in_index);// index of left child, at position 2*in_index
right_child_index = left_child_index + 1;// index of right child, at position 2*in_index + 1

// If left_child_index is not overflowing, suggest swap...
if ((left_child_index) < vector_size) 
{
    // If parent larger than left child, min_index remembers left child position
    if (in_vector[min_index] > in_vector[left_child_index]) 
    { min_index = left_child_index; }
}

// If right_child_index is is not overflowing, suggest swap...
if (right_child_index < vector_size) 
{
    // If parent larger than right child, min_index remembers right child position
    if (in_vector[min_index] > in_vector[right_child_index]) 
    { min_index = right_child_index; }
}

// Now min_index has the index of the smallest out of parent and it's two children.
// If the smallest is not the parent, swap parent and smallest.
if (min_index != in_index) 
{
    in_vector = swap(in_vector, in_index ,min_index);
    in_vector = Heapify(in_vector, min_index); // RECURSION IS HERE
}

return in_vector;
}
// End heapify
1
BeMeCollective

Exemple de tri de tas avec l'exemple de la création initiale d'un tas

def findMin(heapArr,i,firstChildLoc,secondChildLoc):
        a = heapArr[i]
        b = heapArr[firstChildLoc]
        c = heapArr[secondChildLoc]
        return i if ((a < b) and (a < c)) else firstChildLoc if (b < c) else secondChildLoc


def prelocateUp(heapArr):
    l = len(heapArr)
    i = l-1
    while True:
        parentLoc = (i+1)/2 - 1 
        if parentLoc >= 0:
            if heapArr[parentLoc] > heapArr[i]:
                temp = heapArr[parentLoc]
                heapArr[parentLoc] = heapArr[i]
                heapArr[i] = temp  
        else :
            break
        i = parentLoc
    return heapArr

def prelocateDown(heapArr):

    l = len(heapArr)
    i = 0

    while True:
        firstChildLoc = 2*(i+1) - 1
        secondChildLoc = 2*(i+1)
        if (firstChildLoc > l-1):
            break

        Elif (secondChildLoc > l-1):
            if heapArr[i] > heapArr[firstChildLoc]:
                temp = heapArr[i]
                heapArr[i] = heapArr[firstChildLoc]
                heapArr[firstChildLoc] = temp
            break

        else :
            minLoc = findMin(heapArr,i,firstChildLoc,secondChildLoc)
            if minLoc !=i:
                temp = heapArr[i]
                heapArr[i] = heapArr[minLoc]
                heapArr[minLoc] = temp
                i = minLoc
            else :
                break
    return heapArr



def heapify(heapArr,op):
    if op==1:
        heapArr = prelocateUp(heapArr)
    else :
        heapArr = prelocateDown(heapArr)
    return heapArr

def insertHeap(heapArr,num):
    heapArr.append(num)
    heapArr = heapify(heapArr,1)
    return heapArr

def getMin(heapArr):
    ele = heapArr[0]
    heapArr[0] = heapArr[-1]
    heapArr.pop(-1)
    heapArr = heapify(heapArr,2)
    return ele,heapArr

a=[5,4,8,2,6]
heapArr = []
for i in xrange(0,len(a)):
    heapArr = insertHeap(heapArr,a[i])

#No 
sortedArr = []
for i in xrange(0,len(a)):
    [ele,heapArr] = getMin(heapArr)
    sortedArr.append(ele)
print sortedArr
0
user2490585