Teoria da árvore (estrutura da árvore, árvore binária, árvore de pesquisa binária, árvore vermelho-preta, Btree, B+Tree, árvore de Huffman, árvore heap)
conceito de estrutura de árvore
Termos importantes na estrutura da árvore:
-
Nó: O elemento na árvore.
-
Relação pai-filho: arestas conectando nós
-
Subárvore: quando o nó é maior que 1, o restante dos nós é dividido em conjuntos mutuamente disjuntos chamados de subárvores
-
Grau: O número de subárvores que um nó possui é chamado de grau do nó
-
Folha: um nó com grau 0
-
Filho: A raiz da subárvore de um nó é chamada de nó filho
-
Pais: correspondem aos nós filhos
-
Irmão: o mesmo nó pai
-
Floresta: Uma floresta profunda é composta por N árvores mutuamente disjuntas
-
A altura do nó: o caminho mais longo do nó até o nó folha
-
Profundidade do nó: o número de arestas do nó raiz ao nó
-
O número de camadas do nó: a profundidade do nó mais 1
-
Altura da árvore: a altura do nó raiz
árvore binária
Árvore Binária: Uma estrutura de árvore especial, cada nó tem no máximo duas subárvores
2^(N-1)
Existe no máximo um nó no enésimo nível da árvore binária . Existe 2^N-1
um número máximo de nós.
Classificação:
- Árvore binária completa: além dos nós folha, cada nó tem dois nós filhos esquerdo e direito.
- Árvore binária completa: Exceto pela última camada, o número de outros nós deve atingir o máximo, e os nós da última camada são todos organizados continuamente à esquerda.
pensar
Por que precisamos dividir a árvore binária completa e a árvore binária completa? Porque pode ser visto por definição que uma árvore binária completa é apenas um subconjunto de uma árvore binária completa
Array: alto desempenho, se não for um desperdício de espaço para uma árvore binária completa
Lista vinculada: também pode ser implementado, o desempenho não é tão alto quanto um array
travessia de árvore binária
- Fórmula importante: saída do nó raiz! subárvore
- Preâmbulo: Raiz Esquerda e Direita (ABCDEFGHK)
- Em ordem: raiz esquerda direita (BCDAEFGHK)
- Pós-ordem: raízes esquerda e direita (BCDEFGHKA)
/**
* 二叉树--前中后链式遍历
* 前: A B C D E F G H K
* 中: B C D A E F G H K
* 后: B C D E F G H K A
* 层:
* A
* B E
* C F
* D G
* H K
*/
@Data
class TreeNode {
private char data;
private TreeNode left;
private TreeNode right;
public TreeNode(char data, TreeNode left, TreeNode right) {
this.data = data;
this.left = left;
this.right = right;
}
}
public class BinaryTree {
public void print(TreeNode node) {
System.out.print(node.getData() + " ");
}
public void pre(TreeNode root) {
//前序:根(输出) 左 右 A B C D E F G H K
print(root);
if (root.getLeft() != null) {
pre(root.getLeft()); //认为是子树,分解子问题
}
if (root.getRight() != null) {
pre(root.getRight());
}
}
public void in(TreeNode root) {
//中序:左 根(输出) 右 B C D A E F G H K
if (root.getLeft() != null) {
pre(root.getLeft()); //认为是子树,分解子问题
}
print(root);
if (root.getRight() != null) {
pre(root.getRight());
}
}
public void post(TreeNode root) {
//后序:左 右 根(输出) B C D E F G H K A
if (root.getLeft() != null) {
pre(root.getLeft()); //认为是子树,分解子问题
}
if (root.getRight() != null) {
pre(root.getRight());
}
print(root);
}
public List<List<Character>> level(TreeNode root) {
//层次遍历
if (root == null) return Collections.EMPTY_LIST;
List<List<Character>> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
List<Character> raw = new ArrayList<>();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode item = queue.poll();
raw.add(item.getData());
if (item.getLeft() != null) {
queue.add(item.getLeft());
}
if (item.getRight() != null) {
queue.add(item.getRight());
}
}
res.add(raw);
}
return res;
}
public static void main(String[] args) {
TreeNode D = new TreeNode('D', null,null);
TreeNode H = new TreeNode('H', null,null);
TreeNode K = new TreeNode('K', null,null);
TreeNode C = new TreeNode('C', D,null);
TreeNode G = new TreeNode('G', H,K);
TreeNode B = new TreeNode('B', null,C);
TreeNode F = new TreeNode('F', G,null);
TreeNode E = new TreeNode('E', F,null);
TreeNode A = new TreeNode('A', B,E);
BinaryTree tree = new BinaryTree();
System.out.print("前: ");
tree.pre(A);
System.out.println();
System.out.print("中: ");
tree.in(A);
System.out.println();
System.out.print("后: ");
tree.post(A);
System.out.println();
System.out.println("层: ");
List<List<Character>> res = tree.level(A);
for (List<Character> re : res) {
for (Character character : re) {
System.out.print(character + " ");
}
System.out.println();
}
}
}
Árvore de pesquisa binária (árvore de pesquisa binária, número de classificação binário)
- Se sua subárvore esquerda não estiver vazia, o valor do nó na subárvore esquerda é menor que o nó raiz
- Se sua subárvore direita não estiver vazia, o valor do nó na subárvore direita é maior que o nó raiz
- As subárvores também seguem os dois pontos acima
Percurso inorder - raiz esquerda (saída) direita: 0 3 4 5 6 8
Análise de desempenho
- encontrar logn
- inserir nlogn
- excluir
-
- O nó a ser excluído é um nó folha O(1)
-
- O nó a ser excluído possui apenas uma subárvore (esquerda ou direita) O(1)
-
- O nó a ser excluído possui duas subárvores: encontre o nó sucessor e a subárvore esquerda do nó sucessor deve estar vazia logn
-
/**
* 二叉搜索树 增删改查
*/
class BinaryNodeTeacher {
int data;
BinaryNodeTeacher left;
BinaryNodeTeacher right;
BinaryNodeTeacher parent;
public BinaryNodeTeacher(int data) {
this.data = data;
this.left = null;
this.right = null;
this.parent = null;
}
}
public class BinarySearchTreeTeacher {
public BinaryNodeTeacher find(BinaryNodeTeacher root, int key) {
BinaryNodeTeacher current = root;
while (current != null) {
if (key < current.data) {
current = current.left;
} else if (key > current.data) {
current = current.right;
} else {
return current;
}
}
return null;
}
public void insert(BinaryNodeTeacher root, int data) {
if (root.data < data) {
if (root.right != null) {
insert(root.right, data);
} else {
BinaryNodeTeacher newNode = new BinaryNodeTeacher(data);
newNode.parent = root;
root.right = newNode;
}
} else {
if (root.left != null) {
insert(root.left, data);
} else {
BinaryNodeTeacher newNode = new BinaryNodeTeacher(data);
newNode.parent = root;
root.left = newNode;
}
}
}
public BinaryNodeTeacher finSuccessor(BinaryNodeTeacher node) {
// 查找node的后继节点
if (node.right == null) {
// 表示没有右边 那就没有后继
return node;
}
BinaryNodeTeacher cur = node.right;
BinaryNodeTeacher pre = node.right; // 开一个额外的空间 用来返回后继节点,因为我们要找到为空的时候,那么其实返回的是上一个节点
while (cur != null) {
pre = cur;
cur = cur.left; // 注意后继节点是要往左边找,因为右边的肯定比左边的大,我们要找的是第一个比根节点小的,所以只能往左边
}
return pre; // 因为cur会变成null,实际我们是要cur的上一个点,所以就是pre来代替
}
public BinaryNodeTeacher remove(BinaryNodeTeacher root, int data) {
// 删除data
BinaryNodeTeacher delNode = find(root, data);
if (delNode == null) {
System.out.println("要删除的值不在树中");
return root;
}
// 1.删除的点没有左右子树
if (delNode.left == null && delNode.right == null) {
if (delNode == root) {
root = null;
} else if (delNode.parent.data < delNode.data) {
// 说明删除的点是右子节点
delNode.parent.right = null;
} else {
delNode.parent.left = null;
}
} else if (delNode.left != null && delNode.right != null) {
// 2.删除的节点有两颗子节点
BinaryNodeTeacher successor = finSuccessor(delNode); // 先找的后继节点
// 后继节点和删除节点进行交换,首先后继节点的左节点是肯定为空的
successor.left = delNode.left; // 后继的左边变为删除的左边
successor.left.parent = successor; // 删除点的左边parent指向后继节点
// 再来看后继节点的右边
if (successor.right != null && successor.parent != delNode) {
// 后继节点有右边,这其实就是下面情况3的第一种
successor.right.parent = successor.parent;
successor.parent.left = successor.right;
successor.right = delNode.right;
successor.right.parent = successor;
}else if(successor.right == null) {
//如果后继节点没有右边,那其实就是情况1,没有左右子树
if(successor.parent != delNode) {
//如果后继节点的parent不等于删除的点 那么就需要把删除的右子树赋值给后继节点
successor.parent.left = null; //注意原来的后继节点上的引用要删掉,否则会死循环
successor.right = delNode.right;
successor.right.parent = successor;
}
}
// 替换做完接下来就要删除节点了
if (delNode == root) {
successor.parent = null;
root = successor;
return root;
}
successor.parent = delNode.parent;
if (delNode.data > delNode.parent.data) {
// 删除的点在右边,关联右子树
delNode.parent.right = successor;
} else {
delNode.parent.left = successor;
}
} else {
// 3.删除点有一个节点
if (delNode.right != null) {
// 有右节点
if (delNode == root) {
root = delNode.right;
return root;
}
delNode.right.parent = delNode.parent; // 把右节点的parent指向删除点的parent
// 关联父节点的左右子树
if (delNode.data < delNode.parent.data) {
// 删除的点在左边
delNode.parent.left = delNode.right;
} else {
delNode.parent.right = delNode.right;
}
} else {
if (delNode == root) {
root = delNode.left;
return root;
}
delNode.left.parent = delNode.parent;
if (delNode.data < delNode.parent.data) {
delNode.parent.left = delNode.left;
} else {
delNode.parent.right = delNode.left;
}
}
}
return root;
}
public void inOrde(BinaryNodeTeacher root) {
if (root != null) {
inOrde(root.left);
System.out.print(root.data);
inOrde(root.right);
}
}
// 用于获得树的层数
public int getTreeDepth(BinaryNodeTeacher root) {
return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right)));
}
/**
*
* 测试用例
* 15
* 10
* 19
* 8
* 13
* 16
* 28
* 5
* 9
* 12
* 14
* 20
* 30
* -1
* 删除:15 8 5 10 12 19 16 14 30 9 13 20 28
*
* 15
* / \
* 10 19
* / \ / \
* 8 13 16 28
* / \ / \ / \
* 5 9 12 14 20 30
*/
public static void main(String[] args) {
BinarySearchTreeTeacher binarySearchTree = new BinarySearchTreeTeacher();
BinaryNodeTeacher root = null;
Scanner cin = new Scanner(System.in);
int t = 1;
System.out.println("二叉搜索树假定不存重复的子节点,重复可用链表处理,请注意~~");
System.out.println("请输入根节点:");
int rootData = cin.nextInt();
root = new BinaryNodeTeacher(rootData);
System.out.println("请输入第" + t + "个点:输入-1表示结束");
while (true) {
//
int data = cin.nextInt();
if (data == -1)
break;
binarySearchTree.insert(root, data);
t++;
System.out.println("请输入第" + t + "个点:输入-1表示结束");
}
binarySearchTree.show(root); //找的别人写的打印二叉树形结构,感觉还不错,可以更加清晰
System.out.println("删除测试:");
while(true) {
System.out.println("请输入要删除的点:-1表示结束");
int key = cin.nextInt();
root = binarySearchTree.remove(root, key);
binarySearchTree.show(root);
if(root == null) {
System.out.println("树已经没有数据了~~");
break;
}
}
}
private void writeArray(BinaryNodeTeacher currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
// 保证输入的树不为空
if (currNode == null)
return;
// 先将当前节点保存到二维数组中
res[rowIndex][columnIndex] = String.valueOf(currNode.data);
// 计算当前位于树的第几层
int currLevel = ((rowIndex + 1) / 2);
// 若到了最后一层,则返回
if (currLevel == treeDepth)
return;
// 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
int gap = treeDepth - currLevel - 1;
// 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
if (currNode.left != null) {
res[rowIndex + 1][columnIndex - gap] = "/";
writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
}
// 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
if (currNode.right != null) {
res[rowIndex + 1][columnIndex + gap] = "\\";
writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
}
}
public void show(BinaryNodeTeacher root) {
if (root == null) {
System.out.println("EMPTY!");
return ;
}
// 得到树的深度
int treeDepth = getTreeDepth(root);
// 最后一行的宽度为2的(n - 1)次方乘3,再加1
// 作为整个二维数组的宽度
int arrayHeight = treeDepth * 2 - 1;
int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
// 用一个字符串数组来存储每个位置应显示的元素
String[][] res = new String[arrayHeight][arrayWidth];
// 对数组进行初始化,默认为一个空格
for (int i = 0; i < arrayHeight; i++) {
for (int j = 0; j < arrayWidth; j++) {
res[i][j] = " ";
}
}
// 从根节点开始,递归处理整个树
writeArray(root, 0, arrayWidth / 2, res, treeDepth);
// 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可
for (String[] line : res) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < line.length; i++) {
sb.append(line[i]);
if (line[i].length() > 1 && i <= line.length - 1) {
i += line[i].length() > 4 ? 2 : line[i].length() - 1;
}
}
System.out.println(sb.toString());
}
}
}
Árvore rubro-negra (laboratório: árvore binária balanceada AVL) árvore de busca binária degenera em uma lista encadeada
Propriedades das árvores rubro-negras:
- Cada nó é vermelho ou preto
- É impossível ter nós vermelhos conectados entre si (os pretos estão bem), e cada nó folha é um nó preto vazio (NIL), ou seja, o nó folha não armazena dados
- Os nós raiz são todos raízes negras
- Para cada nó, todos os caminhos desse nó até seus nós folha alcançáveis contêm o mesmo número de nós pretos
Regras de rotação e transformação de cores ao inserir:
- Situação de mudança de cor: o pai do nó atual é vermelho e outro nó filho de seu nó avô
também é vermelho. (Nó tio):
(1) Defina o nó pai para preto
(2) Defina o tio para preto
(3) Defina o avô, ou seja, o pai do pai, para vermelho (avô)
(4) Defina o ponteiro para o nó principal (Grandpa) é configurado para a operação atual. - Rotação à esquerda: quando o nó pai atual é vermelho e o tio é preto, e o nó atual é a subárvore direita. Canhoto
Use o nó pai como canhoto. Ponteiro para o nó pai - Rotação à direita: quando o nó pai atual é vermelho e o tio é preto, e o nó atual é a subárvore esquerda. Rotação à direita
(1) torne o nó pai preto
(2) torne o nó avô vermelho (vovô)
(3) gire com o nó avô (vovô)
Canhoto
Destro
Aplicação de árvore rubro-negra
- HashMap
- TreeMap
- Parte inferior do Windows: Localizar
- Agendamento de processos do Linux, nginx, etc.
CÓPIA DE
public class RedBlackTree {
private final int R = 0;
private final int B = 1;
private class Node {
int key = -1;
int color = B; // 颜色
Node left = nil; // nil表示的是叶子结点
Node right = nil;
Node p = nil;
Node(int key) {
this.key = key;
}
@Override
public String toString() {
return "Node [key=" + key + ", color=" + color + ", left=" + left.key + ", right=" + right.key + ", p=" + p.key + "]" + "\r\n";
}
}
private final Node nil = new Node(-1);
private Node root = nil;
public void printTree(Node node) {
if (node == nil) {
return;
}
printTree(node.left);
System.out.print(node.toString());
printTree(node.right);
}
private void insert(Node node) {
Node temp = root;
if (root == nil) {
root = node;
node.color = B;
node.p = nil;
} else {
node.color = R;
while (true) {
if (node.key < temp.key) {
if (temp.left == nil) {
temp.left = node;
node.p = temp;
break;
} else {
temp = temp.left;
}
} else if (node.key >= temp.key) {
if (temp.right == nil) {
temp.right = node;
node.p = temp;
break;
} else {
temp = temp.right;
}
}
}
fixTree(node);
}
}
private void fixTree(Node node) {
while (node.p.color == R) {
Node y = nil;
if (node.p == node.p.p.left) {
y = node.p.p.right;
if (y != nil && y.color == R) {
node.p.color = B;
y.color = B;
node.p.p.color = R;
node = node.p.p;
continue;
}
if (node == node.p.right) {
node = node.p;
rotateLeft(node);
}
node.p.color = B;
node.p.p.color = R;
rotateRight(node.p.p);
} else {
y = node.p.p.left;
if (y != nil && y.color == R) {
node.p.color = B;
y.color = B;
node.p.p.color = R;
node = node.p.p;
continue;
}
if (node == node.p.left) {
node = node.p;
rotateRight(node);
}
node.p.color = B;
node.p.p.color = R;
rotateLeft(node.p.p);
}
}
root.color = B;
}
void rotateLeft(Node node) {
if (node.p != nil) {
if (node == node.p.left) {
node.p.left = node.right;
} else {
node.p.right = node.right;
}
node.right.p = node.p;
node.p = node.right;
if (node.right.left != nil) {
node.right.left.p = node;
}
node.right = node.right.left;
node.p.left = node;
} else {
Node right = root.right;
root.right = right.left;
right.left.p = root;
root.p = right;
right.left = root;
right.p = nil;
root = right;
}
}
void rotateRight(Node node) {
if (node.p != nil) {
if (node == node.p.left) {
node.p.left = node.left;
} else {
node.p.right = node.left;
}
node.left.p = node.p;
node.p = node.left;
if (node.left.right != nil) {
node.left.right.p = node;
}
node.left = node.left.right;
node.p.right = node;
} else {
Node left = root.left;
root.left = root.left.right;
left.right.p = root;
root.p = left;
left.right = root;
left.p = nil;
root = left;
}
}
public void creatTree() {
int data[]= {
23,32,15,221,3};
Node node;
System.out.println(Arrays.toString(data));
for(int i = 0 ; i < data.length ; i++) {
node = new Node(data[i]);
insert(node);
}
printTree(root);
}
/**
* [23, 32, 15, 221, 3]
* Node [key=3, color=0, left=-1, right=-1, p=15]
* Node [key=15, color=1, left=3, right=-1, p=23]
* Node [key=23, color=1, left=15, right=32, p=-1]
* Node [key=32, color=1, left=-1, right=221, p=23]
* Node [key=221, color=0, left=-1, right=-1, p=32]
*/
public static void main(String[] args) {
RedBlackTree bst = new RedBlackTree();
bst.creatTree();
}
}
Árvore B&B+Árvore
A diferença entre B-Tree e B+Tree
- Todos os nós da árvore B armazenarão dados
- O nó folha da árvore b não possui lista encadeada
Que tipo de estrutura de dados é o índice do banco de dados? Por que ele pode encontrá-lo com tanta eficiência? Que tipo de algoritmo é usado?
select * from table where id = 10
select * from table where id > 12
select * from table where id > 12 and id < 20
Transforme a árvore de busca binária:
Pode resolver todas as nossas instruções SQL acima;
Registro de eficiência
2^32=2,1 bilhões;
IO: Refere-se à leitura de dados do disco. 32 camadas serão lidas 32 vezes. CPU, memória, IO;
quantos dados o IO lerá uma vez do disco? Princípios de composição computacional. Página. Página,
quanto espaço ocupa 4KB Int? 4B
pensar
Questão 1: Eficiência de busca: 32 vezes (B+Tree multi-fork tree)
Questão 2: Número de consultas: (B+Tree range)
Questão 3: Disk IO: Resolva este problema; (B+Tree armazena apenas endereços de dados na folha nós)
Estrutura de dados B+Tree
Como o Mysql usa B+Tree para resolver problemas
Mysql é determinado pelo tamanho da página, que geralmente é de 16 Kb. Quanto espaço consome para criar um índice com um tipo de chave primária bigint?
int 8 bytes, um ponteiro conta como 4 bytes, um nó de página: 16kb/(8+8)=1k valor chave + ponteiro terceira ordem:
1024 1024 1024=10 7374 1824
Como construir o índice corretamente:
- Não pode haver muitos índices, porque a inserção e exclusão da árvore B+ precisa ser mantida, e muitos índices retardarão a inserção.
- O campo indexado não pode ser usado como '%%' senão será inválido
- O tipo de campo para indexação não pode ser muito grande. Quanto menor o campo, maior a ordem e maior a eficiência. Int e bigint, varchar(10), varchar(100), text, lontext; B+Tree. índice de texto completo
- Os valores dos campos para indexação não podem ser muitos e iguais. Existe algo na matemática chamado mais hashing (discreto). Por exemplo, o que acontece quando indexamos gênero? Os valores à esquerda são todos iguais e não podem ser filtrados pela metade. O sexo do usuário é indexado separadamente 0 1
- O princípio de correspondência mais à esquerda do índice comum. Selecione * do usuário onde nome = 'mx' e id = 1 Meu índice construído em (id, nome) será otimizado automaticamente quando o mysql for analisado.
Select * from user where name = ‘mx’ and age=10
Meu índice construído em (id, nome, idade) - NOT IN não está indexado não em (1,2,3) Se houver muitos valores de In, o mysql irá reportar um erro
Árvore de Huffman (árvore de Huffman, codificação de Huffman, codificação de prefixo) -- software de compressão, telegrama de comunicação
Projeto do telegrama:
1. Quanto menor o telegrama criptografado, melhor e mais rápida a transmissão.
2. Difícil de quebrar.
3. Fácil de decodificar
. 4. Mais rápido para mudar a árvore criptografada
. 5. Reversível
Calcule a soma dos comprimentos de caminho ponderados das três árvores binárias a seguir:
WPL(a):7*2+5*2+2*2+4*2=36()
WPL(b):7*3+5*3+2*1+4*2=46()
WPL(c):7*1+5*2+2*3+4*3=35()
A borda do nó esquerdo é definida como 0
e a borda do nó direito é definida como 1
© Huffman codificação é
A:0
B:10
C:110
D:111
Construir uma árvore Huffman:
1. Pegue os dois nós com os menores valores de cada vez e transforme-os em uma subárvore.
2. Remova os dois pontos originais
3. Em seguida, coloque a subárvore composta na sequência original
4. Repita 1 2 3 até restar apenas o último ponto
/**
* 赫夫曼树
*/
class HuffmanNode implements Comparable<HuffmanNode> {
String chars;
int fre; //频率 权重
HuffmanNode parent;
HuffmanNode left;
HuffmanNode right;
@Override
public int compareTo(HuffmanNode o) {
return this.fre - o.fre;
}
}
public class HuffmanTree {
HuffmanNode root;
List<HuffmanNode> leafs; //叶子节点
Map<Character, Integer> weights; //叶子节点
Map<Character, String> charmap;
Map<String, Character> mapchar;
public HuffmanTree(Map<Character,Integer> weights) {
this.weights = weights;
leafs = new ArrayList<>();
charmap = new HashMap<>();
mapchar = new HashMap<>();
}
public void code() {
for (HuffmanNode node : leafs) {
Character c = new Character(node.chars.charAt(0));
HuffmanNode current = node;
String code = "";
do {
if (current.parent != null && current == current.parent.left) {
//left
code = "0" + code;
} else {
code = "1" + code;
}
current = current.parent;
} while (current.parent != null);
charmap.put(c, code);
mapchar.put(code, c);
}
}
public void createTree() {
Character keys[] = weights.keySet().toArray(new Character[0]);
PriorityQueue<HuffmanNode> priorityQueue = new PriorityQueue<>();
for (Character key : keys) {
HuffmanNode huffmanNode = new HuffmanNode();
huffmanNode.chars = key.toString();
huffmanNode.fre = weights.get(key);
priorityQueue.add(huffmanNode);
leafs.add(huffmanNode);
}
int len = priorityQueue.size();
for (int i = 1; i <= len-1; i++) {
HuffmanNode n1 = priorityQueue.poll();
HuffmanNode n2 = priorityQueue.poll();
HuffmanNode newNode = new HuffmanNode();
newNode.fre = n1.fre + n2.fre;
newNode.chars = n1.chars + n2.chars;
newNode.left = n1;
newNode.right = n2;
n1.parent = newNode;
n2.parent = newNode;
priorityQueue.add(newNode);
}
root = priorityQueue.poll();
}
public String encode(String body) {
StringBuilder builder = new StringBuilder();
for (char c : body.toCharArray()) {
builder.append(charmap.get(c));
}
return builder.toString();
}
public String decode(String body) {
StringBuilder builder = new StringBuilder();
while (!body.equals("")) {
for (String code : mapchar.keySet()) {
if (body.startsWith(code)) {
body = body.replaceFirst(code,"");
builder.append(mapchar.get(code));
}
}
}
return builder.toString();
}
/**
* a : 10110
* b : 01
* c : 1010
* d : 00
* e : 11
* f : 10111
* g : 100
* encode: 0010111101111010
* decode: dffc
*/
public static void main(String[] args) {
Map<Character, Integer> weights = new HashMap<>();
weights.put('a',3);
weights.put('b',24);
weights.put('c',6);
weights.put('d',20);
weights.put('e',34);
weights.put('f',4);
weights.put('g',12);
HuffmanTree huffmanTree = new HuffmanTree(weights);
huffmanTree.createTree();
huffmanTree.code();
for (Map.Entry<Character, String> entry : huffmanTree.charmap.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
String encode = huffmanTree.encode("dffc");
System.out.println("encode: " + encode);
String decode = huffmanTree.decode(encode);
System.out.println("decode: " + decode);
}
}
pilha de árvores
Suponha que você receba uma sequência: 8 4 20 7 3 1 25 14 17
Ordenando usando uma árvore heap:
- Primeiro armazenamento em uma árvore binária completa em ordem de sequência: construa um heap
- Heapify a partir do último nó não-folha. Por que é o último nó não folha em vez do último nó folha? (O último nó folha não tem nós esquerdo e direito e não pode ser comparado)
Existem duas implementações de inserção de heap:
- baixo para cima
- De cima para baixo
O processo de inserção é chamado de heapização
**
* 堆树
*
* 建堆
* 排序
*
* 1. 优先级队列问题: 删除最大的
* 2. top n 热搜排行榜问题:1000万的数字
* 3. 定时器 堆顶
* 4. 给你1亿不重复的数字,求出top10,前10最大的数字,还可动态添加
*/
public class HeapTree {
//建大顶堆
public static void maxHeap(int data[], int parent, int end) {
int leftSon = parent * 2 + 1; //下标从0开始+1
while (leftSon < end) {
int temp = leftSon;
//temp表示的是 我们左右节点大的那一个
if (leftSon + 1 < end && data[leftSon] < data[leftSon + 1]) {
temp = leftSon + 1; //右节点比左节点大
}
//比较左右节点大的那一个temp和父节点比较大小
if (data[parent] > data[temp]) {
return; //不需要交换
} else {
int t = data[parent]; //交换
data[parent] = data[temp];
data[temp] = t;
parent = temp; //继续堆化
leftSon = parent * 2 + 1;
}
}
}
public static void heapSort(int data[]) {
int len = data.length;
//从后向上建
//建堆从哪里开始 最后一个的父元素开始(len/2 - 1)(父元素中:最后一个父元素是第几个,从0开始)
for (int parent = len/2 - 1; parent >= 0; parent--) {
//nlog(n)
maxHeap(data, parent, len);
}
//从上向下建
//最后一个数和第一个数交换
int headHeap = 0;
for (int tailHeap = len - 1; tailHeap > 0; tailHeap--) {
//nlog(n)
int temp = data[headHeap];
data[headHeap] = data[tailHeap];
data[tailHeap] = temp;
maxHeap(data, headHeap, tailHeap);
}
}
/**
* Arrays: [1, 3, 4, 7, 8, 14, 17, 20, 25]
*/
public static void main(String[] args) {
int data[] = {
8, 4, 20, 7, 3, 1, 25, 14, 17};
heapSort(data);
System.out.printf("Arrays: " + Arrays.toString(data));
}
}
TopK
class TopK {
private int k = 10;
private int data[] = new int[k];
public void topK() {
Random random = new Random();
long time = System.currentTimeMillis();
int size = 0;
boolean init = false;
for (int i = 0; i < 100000000; i++) {
int num = random.nextInt(100000000);
if (size < k) {
data[size] = num;
size++;
} else {
if (!init) {
for (int j = k/2 - 1; j >=0; j--) {
minHeap(data, 0, k);
}
init = true;
}
if (num > data[0]) {
data[0] = num;
minHeap(data, 0, k);
}
}
}
System.out.println("耗时:" + (System.currentTimeMillis() - time) + "ms \n");
for (int datum : data) {
System.out.print(datum+", ");
}
}
private void minHeap(int[] data, int start, int end) {
int parent = start;
int son = parent * 2 + 1;
while (son < end) {
int temp = son;
if (son + 1 < end && data[son] > data[son +1]) {
//右节点比左节点大
temp = son + 1; //根号右节点和父节点
}
//temp表示我们左右节点小的那一个
if (data[parent] < data[temp]) {
return;
} else {
int t = data[parent];
data[temp] = t;
parent = temp;
son = parent * 2 + 1;
}
}
return;
}
public static void main(String[] args) {
TopK topK = new TopK();
topK.topK();
}
}