contente
1. Armazenamento sequencial de árvore binária
2.2 Operação - (sink & float) Este exemplo é um heap máximo
2.3 Construir heap - código completo (heap máximo)
1. Armazenamento sequencial de árvore binária
1.1 Método de armazenamento
Use o array para salvar a estrutura da árvore binária, o método é colocar a árvore binária no array por camada de ordem de travessia .Geralmente, é adequado apenas para representar árvores binárias completas, e o principal uso desse método é a representação de heaps .Porque as árvores binárias não completas desperdiçarão espaço (todas as árvores binárias não completas são armazenadas em cadeias).
1.2 Relação de subscrito
Dados os subscritos dos pais , então:subscrito filho esquerdo (esquerdo) = 2 * pai + 1;subscrito filho direito (direito) = 2 * pai + 2;Conhecendo o subscrito filho (sem distinguir esquerda e direita) (filho) , então:pai (pai) subscrito = (filho - 1) / 2;
2. Pilha _
2.1 Conceito
1. O heap é logicamente uma árvore binária completa2. O heap é armazenado fisicamente em uma matriz3. Certifique-se de que o valor de qualquer nó é maior que o valor do nó em sua subárvore, que é chamada de heap grande, heap raiz grande ou heap máximo4. Pelo contrário, é um heap pequeno, ou um heap de raiz pequeno, ou um heap mínimo5. A função básica do heap é encontrar rapidamente o maior valor no conjunto
2.2 Operação - (afundar e flutuar) Este exemplo é o heap máximo
Elementos dissipadores:
/**
* 下沉操作
*/
public void siftDown(int k){
//还存在子树
while (leftChild(k) < data.size()){
int j = leftChild(k);
//判断是否存在右子树且大于左子树的值
if(j+1 < data.size() && data.get(j+1) > data.get(j)){
j=j+1;
}
//此时j为左右子树最大值
//和当前节点比较大小
if(data.get(j) <= data.get(k)){
break;
}else {
swap(k,j);
k=j;
}
}
}
Elementos flutuam para cima:
/**
* 上浮操作
*/
// 上浮操作的终止条件: 已经走到根节点 || 当前节点值 <= 父节点值
// 循环的迭代条件 : 还存在父节点并且当前节点值 > 父节点值
private void siftUp(int k) {
while (k>0 && data.get(k)>data.get(parent(k))){
swap(k,parent(k));
k=parent(k);
}
}
O método de troca é uma operação de troca:
//交换三连
private void swap(int i,int j) {
int temp = data.get(j);
data.set(j,data.get(i));
data.set(i,temp);
}
Matriz empilhada:
/**
* 将任意数组堆化
* @param arr
*/
public MaxHeap(int[] arr){
data = new ArrayList<>(arr.length);
// 1.先将arr的所有元素复制到data数组中
for(int i : arr){
data.add(i);
}
// 2.从最后一个非叶子结点开始进行siftDown
for (int i = parent(data.size()-1); i >=0 ; i--) {
siftDown(i);
}
}
Ícone:
Tome esta matriz como exemplo:
// 调整前
int[] array = { 27,15,19,18,28,34,65,49,25,37 };
// 调整后
int[] array = { 15,18,19,25,28,34,65,49,27,37 };
Análise de complexidade de tempo:
O pior caso é o caso mostrado na figura, da raiz à folha, o número de comparações é a altura da árvore binária completaOu seja, a complexidade de tempo é O(log(n))
2.3 Construir heap - código completo (heap máximo)
/**
* 基于整形最大堆实现
* 时根节点从0开始编号,若此时节点编号为k
* 左孩子为2k+1
* 右孩子为2k+2
* 父节点为(k-1)/2
*/
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
public class MaxHeap {
// 使用JDK的动态数组(ArrayList)来存储一个最大堆
List<Integer> data;
// 构造方法的this调用
public MaxHeap(){
this(10);
}
// 初始化的堆大小
public MaxHeap(int size){
data = new ArrayList<>(size);
}
/**
* 将任意数组堆化
* @param arr
*/
public MaxHeap(int[] arr){
data = new ArrayList<>(arr.length);
// 1.先将arr的所有元素复制到data数组中
for(int i : arr){
data.add(i);
}
// 2.从最后一个非叶子结点开始进行siftDown
for (int i = parent(data.size()-1); i >=0 ; i--) {
siftDown(i);
}
}
/**
* 向最大堆中增加值为Value的元素
* @param value
*/
public void add(int value){
//1.先直接加到堆的末尾
data.add(value);
//2.元素上浮操作
siftUp(data.size()-1);
}
/**
* 只找到堆顶元素值
* @return
*/
public int peekMax (){
if(isEmpty()){
throw new NoSuchElementException("heap is empty!connot peek");
}
return data.get(0);
}
/**
* 取出当前最大堆的最大值
*/
public int extractMax(){
// 取值一定注意判空
if(isEmpty()){
throw new NoSuchElementException("heap is empty!connot extract");
}
int max = data.get(0);
// 1.将数组末尾元素顶到堆顶
int lastValue =data.get(data.size()-1);
data.set(0,lastValue);
// 2.将数组末尾的元素删除
data.remove(data.size()-1);
// 3.进行元素的下沉操作
siftDown(0);
return max;
}
/**
* 下沉操作
*/
public void siftDown(int k){
//还存在子树
while (leftChild(k) < data.size()){
int j = leftChild(k);
//判断是否存在右子树且大于左子树的值
if(j+1 < data.size() && data.get(j+1) > data.get(j)){
j=j+1;
}
//此时j为左右子树最大值
//和当前节点比较大小
if(data.get(j) <= data.get(k)){
break;
}else {
swap(k,j);
k=j;
}
}
}
/**
* 上浮操作
*/
// 上浮操作的终止条件: 已经走到根节点 || 当前节点值 <= 父节点值
// 循环的迭代条件 : 还存在父节点并且当前节点值 > 父节点值
private void siftUp(int k) {
while (k>0 && data.get(k)>data.get(parent(k))){
swap(k,parent(k));
k=parent(k);
}
}
//交换三连
private void swap(int i,int j) {
int temp = data.get(j);
data.set(j,data.get(i));
data.set(i,temp);
}
//判读堆为空
public boolean isEmpty(){
return data.size() == 0;
}
//根据索引找父节点
public int parent(int k){
return (k-1)>>1;
}
//根据索引找左孩子
public int leftChild(int k){
return k<<2+1;
}
//根据索引找右孩子
public int rightChild(int k){
return k<<2+2;
}
@Override
public String toString() {
return data.toString();
}
}
ps: operação de números aleatórios
int[] data=new int[10000];
//随机数
ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < data.length; i++) {
data[i] = random.nextInt();
}
3. Fila de prioridade
Consulte a próxima seção para obter detalhes: "Java Heap & Priority Queue (Parte 2) "