Estructura de datos: montón (lenguaje C)

Este artículo resolverá algunas preguntas:
1. ¿Qué es un montón?
2. ¿Cómo formar un montón?
3. Escenarios de aplicaciones del montón
 

¿Qué es un montón?

  • El montón es siempre un árbol binario completo.
  • Un nodo en el montón no siempre es mayor ni menor que su nodo padre.

Como se muestra en la figura, en un montón pequeño, el nodo principal siempre es más pequeño que el nodo secundario.

 

Como se muestra en la figura, en un montón grande, el nodo principal siempre es más grande que el nodo secundario.

Hay una gran diferencia entre un montón y un árbol binario. El montón se implementa mediante una matriz. Aunque la estructura lógica es un árbol binario, es mejor que un árbol binario en términos de memoria. Para los árboles binarios ordinarios, debe use listas vinculadas para almacenar sus hijos izquierdo y derecho, pero también les asigne espacio, pero el montón solo está representado por una matriz.

¿Cómo formar un montón?

Hay dos formas de crear un montón: ajuste hacia arriba y ajuste hacia abajo.

Ajustar hacia arriba: comience desde el primer nodo que no sea hoja y ajuste hacia arriba hasta el nodo raíz.

Utilice int a[] ={1,5,3,8,7,6}; como ejemplo,

como muestra la imagen,

Ajustar hacia abajo: comenzando desde el nodo raíz, compare e intercambie con los nodos más pequeños o más grandes entre los hijos izquierdo y derecho hasta que sea más pequeño que el elemento de la matriz.

inserción de montón

eliminación del montón

Eliminar el montón significa eliminar el elemento en la parte superior del montón, cambiar el elemento en la parte superior del montón de acuerdo con los últimos datos, luego eliminar el último elemento de la matriz y luego ajustar el algoritmo hacia abajo.

Piensa por qué es así. ? ?

1. Debido a que el montón se crea con una matriz, si elimina directamente los datos en la parte superior del montón, la primera desventaja es que causará movimiento y sobrescritura de atrás hacia adelante, lo que causará un problema. Los nodos hermanos se convierten en nodos padre-hijo, y esto no aprovecha las ventajas de la matriz.

2. Si intercambias el primer y el último elemento, esto tiene dos ventajas:

  • El primero es no destruir la estructura de los montones izquierdo y derecho, excepto la parte superior del montón.
  • El segundo es aprovechar la matriz, que se lee muy rápidamente, de modo que el último elemento o el más pequeño se coloca al final cada vez.

Complejidad del tiempo de pila

Ajuste la complejidad del tiempo hacia abajo:

 Entonces el número total de pasos para mover el nodo es:

Ajuste la complejidad del tiempo hacia arriba:

Entonces el número total de nodos a ajustar es:

Escenarios de aplicaciones de montón

  1. La clasificación del montón se puede implementar creando y eliminando montones. La clasificación del montón es muy estable (las posiciones relativas de los mismos elementos no se intercambiarán) y la complejidad del tiempo es O (N * logN).
  2. Pregunta TOP-K, pensemos en cómo lo consiguieron los 100 mejores jugadores de Honor de Reyes, o los 10 mejores profesionales… pregunta

1) Primero responda la pregunta TOP-K: es decir, encuentre los primeros K elementos más grandes o más pequeños en la combinación de datos. En algunos casos, los datos son muy grandes.

2) Para este escenario, lo primero que me viene a la mente es ordenar, pero: los datos son muy grandes, por lo que no es aconsejable ordenar. Debido al tamaño de la memoria, no todos se cargarán en la memoria. En este momento, el montón tiene una gran ventaja.

Idea: use elementos K para construir un montón. Si busca los elementos K más grandes, construya un montón pequeño. Si busca el elemento de karaoke más pequeño, construya un montón grande. Luego compare los elementos NK con el elemento superior del montón e intercámbielos si se cumplen las condiciones.

Aquí está el código fuente:

void HeapInit(Heap* php)
{
  assert(php);

  php->a = NULL;
  php->size = php->capacity =0;
}

void HeapDestroy(Heap* php)
{
  assert(php);

  free(php->a);
  php->a = NULL;
  php->capacity = php->size =0;
}

void Swap(HeapDateType* child, HeapDateType* parent){
  HeapDateType tmp = *child;
  *child=  *parent;
  *parent = tmp;
}

void AdjustUp(HeapDateType* a,int child){
  int parent = (child-1)/2;
  while(child > 0){
    if(a[child] < a[parent]){
      Swap(&a[child],&a[parent]);
      child = parent;
      parent = (child-1)/2;
    }else{
      break;
    }
}

}


void HeapPush(Heap* php,HeapDateType x)
{
  assert(php);
  
  if(php->size == php->capacity){
  int newCapacity = php->capacity == 0?4:php->capacity*2;
  HeapDateType* tmp = (HeapDateType*)realloc(php->a,sizeof(HeapDateType)*newCapacity);

  if(tmp == NULL){
    perror("realloc fail\n");
  }

  php->a = tmp;
  php->capacity = newCapacity;
  }

  php->a[php->size] = x;
  php->size++;

  AdjustUp(php->a,php->size-1);
}

void HeapPrint(Heap* php)
{
  assert(php);

  for(size_t i =0; i<php->size; i++){
    std::cout << php->a[i] << " ";
  }
  std::cout << std::endl;
}

void AdjustDown(HeapDateType* a,int n, int parent)
{
  int child = parent*2+1;
  while(child < n){
    if(child+1 < n && a[child+1] < a[child]){
      child++;
    }

    if(a[child] < a[parent]){
      Swap(&a[child],&a[parent]);
      parent = child;
      child = parent*2+1;
    }else{
      break;
    }
  }
}

HeapDateType HeapTop(Heap* php)
{
  assert(php);
  assert(php->size > 0);

  return php->a[0];
}

void HeapPop(Heap* php)
{
  assert(php);
  assert(php->size > 0);

  Swap(&php->a[0],&php->a[php->size-1]);
  --php->size;

  AdjustDown(php->a,php->size,0);

  
}

bool HeapEmpty(Heap* php)
{
 assert(php);

 return php->size == 0;
}

 

void HeapSort(int* a, int n)
{
  //向上调整 O(n*logn)
//  for(size_t i =1; i<n; i++){
//    AdjustUp(a,i);
//  }
//
  //向下调整 O(n)
  for(int i = (n-2)/2; i>=0; i--){
    AdjustDown(a,n,i);
  }

  //时间复杂度O(N*logN)
  int end = n-1;
  while(end > 0){
    Swap(&a[0],&a[end]);
    AdjustDown(a,end,0);
    --end;
  }
}

void PrintTopK(const char* filename,int k)
{
  FILE* fout = fopen(filename,"r");
  if(fout == NULL){
    perror("fopen fail");
    exit(-1);
  }

  int* minHeap = (int*)malloc(sizeof(int)*k);
  if(minHeap == NULL){
    perror("malloc fail");
    exit(-1);
  }

  for(int i =0; i<k; i++){
    fscanf(fout,"%d",&minHeap[i]);
  }

  for(int i = (k-2)/2; i>=0; i++){
    AdjustDown(minHeap,k,0);
  }

  int x =0;
  while(fscanf(fout,"%d",&x)!= EOF){
    if(x > minHeap[0]){
      minHeap[0] = x;
      AdjustDown(minHeap,k,0);
    }
  }

  for(int i =0; i<k; i++){
    std::cout << minHeap[i] << " ";
  }
  std::cout << std::endl;
}

                        

 

Supongo que te gusta

Origin blog.csdn.net/m0_72165281/article/details/133440797
Recomendado
Clasificación