Dividir na árvore AVL com complexidade


9

A operação de divisão pode ser implementada para árvores AVL com complexidade ? Estou interessado em links para artigos ou qualquer informação específica sobre este assunto.O(logn)

A operação de divisão divide a árvore AVL em duas árvores derivadas, com base na chave. Uma das árvores derivadas deve conter todos os vértices nos quais todas as chaves são menores que a chave original e a segunda o restante.

Eu sei que isso pode ser feito em time. Aqui está um link para implementação com complexidade : https://code.google.com/p/self-balancing-avl-tree/O(log2n)O(log2n)

Eu também sei como mesclar duas árvores AVL, de modo que as chaves de uma árvore sejam todas menores que as chaves da outra, em . Aqui está uma implementação com complexidade :O(logn)O(logn)

def Merge(l, r) {
    if (!l || !r) 
        return l ? l : r;
    if (l->h <= r->h)
        r->l = Merge(l, r->l), Rebalance(r);
    else
        l->r = Merge(l->r, r), Rebalance(l);
}

Respostas:


7

Sim, isso é possível.

Você pode ler sobre isso nas "Estruturas e algoritmos de dados em uma memória de dois níveis", de Ramzi Fadel e Kim Vagn Jakobsen, seção 3.1.6 , (espelho) ou na biblioteca padrão do OCaml, na função "dividir".

Uma das principais informações é que a função de mesclagem mencionada é, com uma contabilidade mais cuidadosa, , onde é a altura da árvore mais alta e é a altura da árvore mais curta. Assim, mesclar uma lista de árvores que têm alturas descendentes ou ascendentes custa apenas , uma vez que a soma é telescópica .O(h1h2)h1h2O(hmaxhmin)


-1

Vamos definir uma função split(T,v)que recebe uma árvore Te um valor para dividir em v,. Suponha que cada nó da árvore armazene seu filho esquerdo e filho direito, além do valor nesse nó. Use o seguinte algoritmo:

  1. Primeiro, verificamos se a árvore de entrada é simplesmente uma folha ou não.

  2. Se Tnão for uma folha, compare o valor do nó raiz v'com v.

  3. Se, v' < ventão, chamar recursivamente a divisão na subárvore esquerda. Armazene os valores da chamada recursiva como L'(árvore esquerda retornada), R'(árvore direita retornada) e r(tipo de opção indicado se o valor vfoi encontrado ou não). Construa a nova árvore certa,, newR = Node(R',v',R)e retorne (L',r,newR).

  4. Caso contrário, se v' > vrecursivamente chamar divisão na subárvore direita. Armazene os valores da chamada recursiva como L'(árvore esquerda retornada), R'(árvore direita retornada) e r(tipo de opção indicado se o valor vfoi encontrado ou não). Construa a nova árvore esquerda,, newL = Node(L',v',L)e retorne (newL,r,R').

  5. Caso contrário v' = v, volte L, SOME(v), R.

  6. Se Tfor uma folha, devemos ter atingido a raiz da árvore sem encontrar o valor de entrada v para dividir. Retorne que você não conseguiu encontrar a folha retornando NONE.

Por que isso é logarítmico? Bem, você apenas percorre um caminho raiz-a-folha da árvore, no máximo. Podemos reconstruir facilmente nós em tempo constante, pois estamos apenas reatribuindo referências (em uma linguagem imperativa) ou reatribuindo valores que levam um tempo constante para gerar (em uma linguagem funcional) )O(logn)O(logn)

Aqui está o código correspondente para o algoritmo. Está escrito em SML, mas eu gostaria de esclarecer o que qualquer coisa significa nos comentários.

fun split(T,v) = case T of Leaf => (Leaf, NONE, Leaf) | Node(L,v,R) => case compare(v, v') of LESS => let val (L',r,R') = split(L,k) in (L',r,Node(R',r,R)) end | GREATER => let val (L',r,R') = split(R,k) in (Node(L',v',L),r,R') end | EQUAL => (L, SOME(v), R)

Veja este documento para mais detalhes. Ele fornece uma explicação mais completa do que foi dito acima.


Isso não trata das condições de saldo do AVL.
Jbapple #
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.