Classificação inferior da pilha



Este é o artigo final da série de classificação de heap. Nas palestras anteriores, vimos uma grande variedade de estruturas de pilha, mostrando excelentes resultados de velocidade. Isso levanta a questão: qual pilha é a mais eficiente quando se trata de classificação? A resposta é: a que olharemos hoje.
EDISON Software - desenvolvimento web
EDISON .




« » — -, CRM-, , iOS Android.



;-)
Os montes sofisticados que examinamos anteriormente são bons, mas o heap mais eficiente é o heap padrão com limpeza aprimorada.



O que é uma limpeza, por que é necessário em uma pilha e como funciona é descrito na primeira parte de uma série de artigos.



A peneiração padrão na classificação clássica de um grupo funciona aproximadamente como "testa" - um elemento da raiz da subárvore é enviado para a área de transferência, enquanto os elementos do ramo, de acordo com os resultados da comparação, aumentam. É bastante simples, mas há muitas comparações.







Na faixa ascendente, as comparações são salvas devido ao fato de os pais dificilmente serem comparados aos seus descendentes; basicamente, apenas os descendentes são comparados entre si. Em um amontoado comum, os pais são comparados com os filhos e os filhos são comparados entre si - portanto, há quase uma vez e meia mais comparações com o mesmo número de trocas.



Então, como funciona, vamos ver um exemplo específico. Digamos que temos uma matriz na qual um monte está quase formado - tudo o que resta é peneirar a raiz. Para todos os outros nós, a condição é atendida - qualquer descendente não é mais que seu pai.



Primeiro, no nó para o qual a peneiração é realizada, é necessário descer ao longo dos descendentes grandes. A pilha é binária - ou seja, temos um filho esquerdo e um filho certo. Descemos para o ramo onde o descendente é maior. Nesta fase, a maioria das comparações ocorre - as crianças esquerda / direita são comparadas entre si.







Tendo alcançado a folha no último nível, decidimos, assim, o ramo, cujos valores precisam ser alterados. Mas você não precisa mover o ramo inteiro, mas apenas a parte que é maior que a raiz da qual você iniciou.



Portanto, subimos a ramificação até o nó mais próximo, que é maior que a raiz.







O último passo - usando a variável de buffer, alteramos os valores dos nós no ramo.







É isso aí. A peneira ascendente formou uma árvore de classificação a partir da matriz, na qual qualquer pai ou mãe é maior que seus descendentes.



Animação final:







Implementação Python 3.7



O algoritmo básico de classificação não é diferente do heapsort regular:



#    
def HeapSortBottomUp(data):

    #    
    #   -   
    # (   )       
    for start in range((len(data) - 2) // 2, -1, -1):
        HeapSortBottomUp_Sift(data, start, len(data) - 1) 

    #        
    #        .
    for end in range(len(data) - 1, 0, -1): 
        #       
        #    
        data[end], data[0] = data[0], data[end]
        #        
        #   
        #     
        HeapSortBottomUp_Sift(data, 0, end - 1)
    return data


A descida para a folha inferior é convenientemente / visualmente colocada em uma função separada:



#      
#   
def HeapSortBottomUp_LeafSearch(data, start, end):
    
    current = start
    
    #  ,  
    #  (  ) 
    while True:
        child = current * 2 + 1 #  
        #  ,    
        if child + 1 > end: 
            break 
        #  ,   
        if data[child + 1] > data[child]:
            current = child + 1
        else:
            current = child
    
    #  ,    
    child = current * 2 + 1 #  
    if child <= end:
        current = child
        
    return current


E o mais importante - uma clareira, primeiro descendo e depois emergindo para cima:



#  
def HeapSortBottomUp_Sift(data, start, end):
    
    #        
    current = HeapSortBottomUp_LeafSearch(data, start, end)
    
    #  ,    
    #     
    while data[start] > data[current]:
        current = (current - 1) // 2
    
    #   ,
    #      
    temp = data[current]
    data[current] = data[start]
    
    #        
    # -     
    while current > start:
        current = (current - 1) // 2
        temp, data[current] = data[current], temp  


O código C também foi encontrado na Internet
/*----------------------------------------------------------------------*/
/*                         BOTTOM-UP HEAPSORT                           */
/* Written by J. Teuhola <teuhola@cs.utu.fi>; the original idea is      */
/* probably due to R.W. Floyd. Thereafter it has been used by many      */
/* authors, among others S. Carlsson and I. Wegener. Building the heap  */
/* bottom-up is also due to R. W. Floyd: Treesort 3 (Algorithm 245),    */
/* Communications of the ACM 7, p. 701, 1964.                           */
/*----------------------------------------------------------------------*/

#define element float

/*-----------------------------------------------------------------------*/
/* The sift-up procedure sinks a hole from v[i] to leaf and then sifts   */
/* the original v[i] element from the leaf level up. This is the main    */
/* idea of bottom-up heapsort.                                           */
/*-----------------------------------------------------------------------*/

static void siftup(v, i, n) element v[]; int i, n; {
	
  int j, start;
  element x;

  start = i;
  x = v[i];
  j = i << 1;
	
  /* Leaf Search */
  while(j <= n) {
    if(j < n) if v[j] < v[j + 1]) j++;
    v[i] = v[j];
    i = j;
    j = i << 1;
  }
	
  /* Siftup */
  j = i >> 1;
  while(j >= start) {
    if(v[j] < x) {
      v[i] = v[j];
      i = j;
      j = i >> 1;
    } else break;
  }
  v[i] = x;
	
} /* End of siftup */

/*----------------------------------------------------------------------*/
/* The heapsort procedure; the original array is r[0..n-1], but here    */
/* it is shifted to vector v[1..n], for convenience.                    */
/*----------------------------------------------------------------------*/

void bottom_up_heapsort(r, n) element r[]; int n; {
  int k; 
  element x;
  element *v;

  v = r - 1; /* The address shift */

  /* Build the heap bottom-up, using siftup. */
  for (k = n >> 1; k > 1; k--) siftup(v, k, n);

  /* The main loop of sorting follows. The root is swapped with the last  */
  /* leaf after each sift-up. */
  for(k = n; k > 1; k--) {
    siftup(v, 1, k);
    x = v[k];
    v[k] = v[1];
    v[1] = x;
  }
} /* End of bottom_up_heapsort */


Puramente empiricamente - de acordo com minhas medidas, a classificação ascendente por um monte funciona 1,5 vezes mais rápido que a classificação regular por um monte.



De acordo com algumas informações (na página do algoritmo da Wikipedia, no PDF citado na seção "Links"), o BottomUp HeapSort está em média à frente de uma classificação ainda mais rápida - para matrizes bastante grandes de 16 mil elementos ou mais.



Ligações







Heapsort de baixo para cima Uma variante do Heapsort com número quase ideal de comparações



Criando montões rapidamente



Uma nova variante do heapsort superando, em média, o quicksort (se n não for muito pequeno)



Artigos da série:





A classificação de hoje foi adicionada ao aplicativo AlgoLab, que a utiliza - atualize o arquivo do Excel com macros.



All Articles