Índice
2. Estrutura de armazenamento heap
3.3 Complexidade de tempo de shiftDown e shiftUp
1. O conceito de pilha
Heaps são frequentemente usados para implementar aplicações como Filas Prioritárias, onde elementos com prioridade mais alta (ou mais baixa) podem ser rapidamente encontrados e excluídos. As operações de heap incluem a inserção de novos elementos, a remoção do elemento superior do heap (ou seja, o valor mais alto) e o ajuste dos heaps existentes para satisfazer a propriedade de ordem de heap. As operações comuns de ajuste de heap são "float" (filtro superior) e "sink" (filtro inferior) .
O heap tem as duas características principais a seguir:
- O heap é uma árvore binária completa
- O valor de cada nó no heap deve ser maior ou igual (ou menor ou igual) ao valor de seu nó filho.
2. Estrutura de armazenamento heap
A partir do conceito de heap, podemos saber que o heap é uma árvore binária completa em sua estrutura lógica, portanto pode ser armazenado de forma eficiente e sequencial de acordo com as regras de ordem hierárquica, ou seja, o heap é armazenado usando um tabela de sequência. Faça um desenho para entender:
Após armazenar os elementos em um array, a árvore pode ser restaurada de acordo com as propriedades de uma árvore binária completa. Supondo que i seja o subscrito do nó na matriz, então:
- Se i = 0, então o nó representado por i é o nó raiz, caso contrário, o nó pai do nó i é (i - 1)/2
- Se 2 * i + 1 for menor que o número de nós, o subscrito filho esquerdo do nó i será 2 * i + 1, caso contrário, não haverá filho esquerdo
- Se 2 * i + 2 for menor que o número de nós, o índice filho certo do nó i será 2 * i + 2, caso contrário, não haverá filho certo
3. Implementação de heap
O que é implementado aqui é um grande heap raiz:
public class Heap {
private int[] elem;
private int usedSize;
public Heap(int[] arr){
elem = new int[arr.length];
createHeap(arr);
}
//建堆
public void createHeap(int[] arr){
for (int i = 0; i < arr.length; i++) {
elem[i] = arr[i];
usedSize++;
}
for (int parent = (usedSize-2)/2; parent >= 0; parent--) {
shiftDown(parent,usedSize);
}
}
//向下调整
public void shiftDown(int parent, int len){}
public void swap(int i, int j){
int tmp = elem[i];
elem[i] = elem[j];
elem[j] = tmp;
}
//入堆
public void push(int val){}
public boolean isFull(){
return usedSize == elem.length;
}
//向上调整
public void shiftUp(int child){}
//出堆顶元素
public int poll(){
if(isEmpty()){
System.out.println("堆中没有元素");
return -1;
}
swap(0,usedSize-1);//将头尾交换
usedSize--;//去掉堆顶元素
shiftDown(0,usedSize);//重新排序
return elem[usedSize];//返回堆顶元素
}
public boolean isEmpty(){
return usedSize == 0;
}
//得到堆顶元素
public int peek(){
if(!isEmpty()){
return elem[0];
}
System.out.println("堆中没有元素");
return -1;
}
}
O código principal no heap é a implementação dos métodos shiftDown() e shiftUp() . Você pode entender os outros métodos observando-os. Vamos falar sobre a implementação desses dois métodos em detalhes.
3.1 shiftDown()
Por exemplo, queremos transformar o array {27, 15, 19, 18, 28, 34, 65, 49, 25, 37} em um heap. Como implementamos isso? A ideia: primeiro encontre a última subárvore e vire em uma pilha. Depois de formar uma pilha, percorra outras subárvores em sequência até que o nó raiz da última subárvore seja o nó raiz de toda a árvore . Veja a figura abaixo:
código mostrado abaixo:
public void shiftDown(int parent, int len){
int child = 2*parent + 1;
while(child < len){
if(child+1 < len && elem[child] < elem[child+1]){
child++;
}
if(elem[parent] < elem[child]){
swap(parent,child);
parent = child;
child = 2*parent + 1;
}else{
break;
}
}
}
3.2 shiftUp()
Este método consiste em inserir um elemento na forma de um heap ao inseri-lo.Essencialmente, as ideias de shiftDown e shiftUp são semelhantes:
public void shiftUp(int child){
int parent = (child-1)/2;
while(parent >= 0){
if(elem[child] > elem[parent]){
swap(child,parent);
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
3.3 Complexidade de tempo de shiftDown e shiftUp
4. Classificação de pilha
Por exemplo, se quisermos classificar em ordem crescente, primeiro precisamos criar um grande heap raiz e, em seguida, trocar o nó raiz pelo último nó.Neste momento, o último nó deve ser o maior, então execute shiftDown e em seguida, troque o nó raiz pelo penúltimo nó, troque e assim por diante. código mostrado abaixo:
/**
* 堆排序
* 时间复杂度:O(N*logN)
* 空间复杂度:O(1)
* 不稳定
*/
public void heapSort(int[] arr){
createHeap(arr);
int end = arr.length-1;
while(end > 0){
swap(arr,0,end);
shiftDown(arr,0,end);
end--;
}
}
private void shiftDown(int[] arr, int parent,int len) {
int child = 2*parent+1;
while(child < len){
if(child+1 < len && arr[child] < arr[child+1]){
child++;
}
if(arr[child] > arr[parent]){
swap(arr,child,parent);
parent = child;
child = 2*parent+1;
}else{
break;
}
}
}
public void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public void createHeap(int[] array) {
for (int parent = (array.length-2)/2; parent >= 0; parent--) {
shiftDown(array,parent,array.length);
}
}