MAC0122  Princípios de Desenvolvimento de Algoritmos
Home  |   Administração  |   Fórum  |   Livros  |   WWW  |   Diário  |   Tarefas  |   Alunos

 

Árvores de busca

Esta página é parcialmente baseada nas seções 13.xxx e 13.xxx do capítulo 13 (Trees) do livro de Eric Roberts.

Árvores binárias generalizam a idéia de listas encadeadas.  Da mesma forma, árvores (binárias) de busca (= search trees) generalizam a idéia de listas encadeadas crescentes.  Pode-se dizer, informalmente, que uma árvore de busca é uma estrutura que permite fazer busca binária em uma lista ligada.

 

O conceito

Dizemos que uma árvore binária é de busca se cada um de seus nós tem a seguinte propriedade: a chave do nó é

Em outras palavras, se t é o endereço de um nó qualquer, se x é o endereço de algum nó na subárvore esquerda de t e se y é o endereço de algum nó na subárvore direita de t então

x->key   <   t->key   <   y->key.

Há uma outra maneira, talvez mais clara, de descrever essa propriedade:  a ordem e-r-d das chaves é crescente.

Vamos supor, no que segue, que cada nó da árvore tem a estrutura

typedef struct nodeT {
   string        key;
   struct nodeT *left;
   struct nodeT *right;
} nodeT;

typedef struct nodeT *treeT;

Exercício

  1. [Roberts 9, ch 13, p.586. Importante!]  Escreva uma função que decida se uma dada árvore binária é ou não é de busca.

  2. Suponha dada uma árvore binária com a seguinte propriedade: para cada nó x tem-se x->left->key < x->key < x->right->key.  Essa árvore é de busca?

 

Busca

Problema: Dada uma árvore de busca, encontrar um nó da árvore cuja chave tenha um certo valor k.  Eis uma função recursiva que devolve um nó y tal que y->key é igual a k;  se um tal y não existe, a função devolve NULL.

// Recebe uma string k e uma árvore de busca t.
// Devolve o endereço de um nó cuja chave é k;
// se tal nó não existe então devolve NULL.

treeT busca( treeT t, string k) {
    if (t == NULL || strcmp( t->key, k) == 0)
       return t;
    if (strcmp( t->key, k) > 0)
       return busca( t->left, k);
    else
       return busca( t->right, k);
}

 

Exercícios

  1. Escreva uma função min que encontre a menor chave em uma árvore de busca. Escreva uma função max que encontre a maior chave.

  2. Suponha que as chaves de nossa árvore de busca são distintas duas a duas. Escreva uma função que receba uma chave k e devolva a chave seguinte na ordem crescente.

  3. Há uma relação muito íntima entre uma árvore de busca e o algoritmo de busca binária num vetor. Qual é, exatamente, essa relação?

  4. Escreva uma função que transforme um vetor crescente em uma árvore de busca balanceada.

  5. Escreva uma função que transforme uma árvore de busca em um vetor crescente. Comece por alocar dinamicamente o vetor.

 

Inserção

Problema: Inserir um novo nó em uma dada árvore de busca. É claro que a nova árvore deve também ser de busca;  é aí que está a dificuldade do problema.

Vamos adotar a política de criar o novo nó fora da função de inserção:

        nodeT *new;
        new = malloc( sizeof (nodeT));
        new->key = k;
        new->left = new->right = NULL;

A função abaixo insere o novo nó na árvore de busca r. O endereço da nova árvore será igual a r a menos que r seja NULL.

// A função recebe uma árvore de busca r e um 
// nó new que não pertence à árvore. A função 
// insere new na árvore r de modo que a árvore 
// continue sendo de busca e devolve o endereço 
// da nova árvore.

treeT insere( treeT r, nodeT *new) {  
    treeT f, p;
    if (r == NULL)
       return new;
    f = r;
    do {
       p = f;
       if (strcmp( p->key, new->key) > 0)  
          f = p->left;
       else  
          f = p->right;
    } while (f != NULL); 
    if (p->key > new->key)  p->left = new;
    else  p->right = new;
    return r;
}

 

Exercícios

  1. Escreva uma versão recursiva de insere

 

Remoção

Problema: Remover um nó de uma árvore de busca de tal forma que a árvore continue sendo de busca.

Digamos que queremos remover um nó y cujo pai é x. Se y tem um só filho então basta dizer que o filho de y passa a ser filho de x.  Suponha agora que y tem dois filhos. Nesse caso, será preciso fazer um serviço mais complexo.

Seja c o predecessor de y na ordem e-r-d;  seja a o pai de c e b = c->left. Agora basta fazer com que b seja o filho de a e colocar o nó c no lugar de y (os filhos de y passarão a ser os filhos de c).
       y
      /
     .
    / \
       .
      / \
         a
        / \
          c
         /
        b                       

 

Exercícios

  1. Suponha que as chaves 50, 30, 70, 20, 40, 60, 80, 15, 25, 35, 45, 36 são inseridas, nesta ordem, numa árvore de busca que está inicialmente vazia. Desenhe a árvore que resulta. Em seguida remova o nó que contém 30.

 


Veja minhas anotações sobre árvores de busca.
URL of this site: www.ime.usp.br/~pf/mac0122-2002/
Last modified: Mon Oct 9 07:14:55 BRT 2017
Paulo Feofiloff
IME-USP