fundo
No projeto, muitas vezes encontramos problemas de consulta de subcategoria de classificação multinível, como estrutura organizacional, menu multinível, tipo de mercadoria, etc. A estrutura de dados comum é a seguinte:
Problemas com este projeto:
-
A consulta de subcategoria para classificação multinível precisa ser implementada por meio de recursão, a complexidade de tempo é 0(n) e a eficiência é relativamente baixa.
-
A recursão muito profunda pode facilmente causar estouro de pilha.
Portanto, para consulta de subcategoria de classificação em vários níveis, existe uma solução melhor? Podemos resolver o problema de consulta de subcategoria de classificação multinível pré-ordenando o algoritmo de travessia de árvore (MPTT).
Introdução
MPTT (Modified Preorder Tree Taversal) algoritmo de árvore de pré-ordenação de pré-ordenação, usado principalmente no armazenamento e travessia de relacionamentos hierárquicos.
Fundamental
O princípio do algoritmo de árvore transversal de pré-ordenação: a relação do nível da árvore é representada pelo valor dos nós esquerdo e direito do número.
Como mostrado abaixo:
A estrutura em árvore é convertida nos dados de estrutura do banco de dados da seguinte forma:
Descrição: A estrutura de dados MPTT contém informações como nó esquerdo, nó direito, nível, nó pai, etc.
- treeid: O id da árvore é usado para identificar uma árvore no banco de dados.
- nível: indica o nível da árvore, o nível do nó raiz é 1 e o nível do nó filho é o nível do nó pai mais 1.
- parentId: ID do nó pai, já que o nó raiz não tem nó pai, o valor é -1.
- lftNode: valor do nó esquerdo.
- rgtNode: valor do nó direito.
Aplicativo básico
atravessar a árvore inteira
Para percorrer toda a árvore, basta consultar a condição treeId.
Localizar todos os nós descendentes em um nó
Para encontrar todos os nós descendentes em Fruit, você só precisa descobrir que o valor do nó esquerdo é maior que 2 e o valor do nó direito é menor que 11.
SELECT * FROM t_tree t WHERE t.`lftNode`>2 AND t.`rgtNode`<11;
复制代码
resultado da pesquisa
Uma consulta com uma eficiência de 0(1) é muito mais eficiente do que uma consulta recursiva.
Localizar todos os nós filhos em um nó
Para encontrar todos os subpontos em Frutas, você só precisa consultar o nível igual a 2
SELECT * FROM t_tree t WHERE t.`level`='2';
复制代码
resultado da pesquisa
Encontrar o caminho de um nó
Para encontrar o caminho do nó Banana, você só precisa consultar se o nó esquerdo é menor que 8 e o nó direito é maior que 9.
SELECT * FROM t_tree t WHERE t.`lftNode`<8 AND t.`rgtNode`>9;
复制代码
resultado da pesquisa
Através do resultado da consulta, o caminho do nó da Banana é: Food->Fruit->Yellow
Quantos descendentes tem um nó?
Encontre o número de todos os nós descendentes em Fruit, a fórmula específica é a seguinte:
子孙总数=(右值-左值-1)/2
复制代码
Através do cálculo da fórmula, sabemos que o número total de nós descendentes de Fruit é (18-1-1)/2=8
Adicionar nó filho
Embora a eficiência da classificação pré-compilada seja muito alta em nós de consulta, ela é muito ineficiente em adicionar e excluir nós, porque adicionar e excluir nós requer recálculo e movimentação de nós, os quais serão mais lentos.
implementar lógica
-
Adicione um novo nó a uma árvore que não existe e crie uma nova árvore. Então o parentid é -1, o nível é 1 e o treeId é baseado no treeId máximo da árvore existente mais 1.
-
Adicione um novo nó a uma árvore existente. parentId é o id do nó pai, level é o nível do nó pai mais 1 e treeId é o mesmo que o nó pai.
-
Repare o valor esquerdo de outros nós cujo saldo está quebrado e adicione 2 ao valor esquerdo de todos os nós maiores que o valor direito de parentId.
-
Repare os rvalues de outros nós cujo saldo está quebrado e adicione 2 aos rvalues de todos os nós maiores que o rvalue de parentId.
Implementação
DELIMITER $$
DROP PROCEDURE IF EXISTS `SP_TREE_ADD`$$
CREATE PROCEDURE `SP_TREE_ADD`(
IN p_oid INT (11),
IN p_name VARCHAR (30),
IN p_parentOid INT (11)
)
BEGIN
#获取父节点的相关数据信息
SELECT @myRight :=rgtNode,@oid :=oid,@level :=LEVEL,@treeid :=treeid FROM t_tree t WHERE oid = p_parentOid;
#大于parentId右值的所有节点的右值加2。
UPDATE t_tree SET rgtNode = rgtNode + 2 WHERE rgtNode > @myRight;
#大于parentId右值的所有节点的左值加2
UPDATE t_tree SET lftNode = lftNode + 2 WHERE lftNode > @myRight;
UPDATE t_tree SET rgtNode=rgtNode+2 WHERE oid=@oid;
#插入数据
INSERT INTO t_tree(oid,NAME,parentOid,LEVEL,lftNode,rgtNode,treeid)
VALUES(p_oid, p_name, p_parentOid,@level+1,@myRight, @myRight +1,@treeid);
END$$
DELIMITER ;
复制代码
teste
Adicione um subnó de maçã no nó Vermelho e execute o procedimento armazenado acima
CALL SP_TREE_ADD('11','apple','3');
复制代码
Após a inserção bem-sucedida, o resultado é o seguinte
Para verificar novamente o número de nós em Vermelho, você só precisa consultar se o nó esquerdo for maior que 3 e o nó direito for menor que 8.
SELECT * FROM t_tree t WHERE t.`lftNode`>3 AND t.`rgtNode`<8;
复制代码
excluir nó filho
implementar lógica
- Excluir nós cujo valor esquerdo é maior que o nó pai e menor que o valor direito
- Repare o valor esquerdo de outros nós cujo saldo está quebrado e subtraia o valor da diferença do valor esquerdo de todos os nós que são maiores que o valor direito de parentId.
- Repare os rvalues de outros nós cujo saldo está quebrado e subtraia a diferença dos rvalues de todos os nós que são maiores que o rvalue de parentId.
Implementação
DELIMITER $$
DROP PROCEDURE IF EXISTS `SP_TREE_DEETE`$$
CREATE PROCEDURE `SP_TREE_DEETE`(
IN p_Oid INT (11)
)
BEGIN
SELECT
@myleft := lftNode,
@myright :=rgtNode,
@oid :=oid,
@mywidth := rgtNode-lftNode+1
FROM t_tree WHERE oid = p_Oid;
DELETE FROM t_tree WHERE lftNode BETWEEN @myleft AND @myright;
UPDATE t_tree SET rgtNode = rgtNode - @mywidth WHERE rgtNode > @myright;
UPDATE t_tree SET lftNode = lftNode - @mywidth WHERE lftNode > @myright;
END$$
DELIMITER ;
复制代码
teste
Exclua o subnó da maçã no nó Vermelho e execute o procedimento armazenado acima.
CALL SP_TREE_DEETE(11);
复制代码
O resultado após a exclusão é o seguinte
Para verificar novamente o número de nós em Vermelho, você só precisa consultar se o nó esquerdo for maior que 3 e o nó direito for menor que 6.
SELECT * FROM t_tree t WHERE t.`lftNode`>3 AND t.`rgtNode`<6;
复制代码
Vantagens e desvantagens
vantagem
A eficiência da consulta de árvore transversal de pré-classificação é alta, a eficiência da consulta não é afetada pelo aumento do nível de classificação e é adequada para cenários com muitas operações de consulta.
deficiência
Adicionar e excluir requer recalcular os valores esquerdo e direito do nó, portanto, a eficiência de execução é baixa
Resumir
Este artigo explica o algoritmo de pré-ordenação. Nenhuma solução é perfeita. Precisamos dominar suas vantagens e desvantagens. Em projetos reais, precisamos escolher a solução adequada de acordo com cenários de negócios específicos.
Estou participando do recrutamento do programa de assinatura de criadores da Comunidade de Tecnologia Nuggets, clique no link para se cadastrar e enviar .