• Nenhum resultado encontrado

Algoritmos e Estruturas de Dados 2008/2009. Conjunto de nós e conjunto de arestas que ligam pares de nós

N/A
N/A
Protected

Academic year: 2021

Share "Algoritmos e Estruturas de Dados 2008/2009. Conjunto de nós e conjunto de arestas que ligam pares de nós"

Copied!
23
0
0

Texto

(1)

Algoritmos e Estruturas de Dados 2008/2009

Árvores

• Conjunto de nós e conjunto de arestas que ligam pares de nós

– Um nó é a raiz

– Com excepção da raiz, todo o nó está ligado por uma aresta a 1 e 1 só nó

(o pai)

– Há um caminho único da raiz a cada nó; o tamanho do caminho para um

nó é o número de arestas a percorrer

A B C D E F G H I J Nós sem descendentes: folhas

(2)

AED - 2008/09 3

• Ramos da árvore

Árvore de N nós tem N-1 ramos • Profundidade de um nó

– Comprimento do caminho da raiz até ao nó • Profundidade da raiz é 0

• Produndidade de um nó é 1 + a profundidade do seu pai • Altura de um nó

– Comprimento do caminho do nó até à folha a maior profundidade • Altura de uma folha é 0

• Altura de um nó é 1 + a altura do seu filho de maior altura – Altura da árvore: altura da raiz

• Se existe caminho do nó u para o nó v – u é antepassado de v

– v é descendente de u

• Tamanho de um nó: número de descendentes

Árvores

A B G C D E J F H I profundidade=0 profundidade=1 profundidade=2 profundidade=3

altura(A) = altura árvore = 3

altura(D) = 2

pai(G) = D

(3)

AED - 2008/09 5

• Uma árvore binária é uma árvore em que cada nó não tem mais

que dois filhos

• Propriedades:

– Uma árvore binária não vazia com profundidade h tem no mínimo h+1,

e no máximo 2

h+1

-1 nós

– A profundidade de uma árvore com n elementos (n>0) é no mínimo

log

2

n, e no máximo n-1

– A profundidade média de uma árvore de n nós é O(√n)

Árvores

• Percorrer árvores

Os elementos de uma árvore (binária) podem ser enumerados por quatro

ordens diferentes. As três primeiras definem-se recursivamente:

– Pré-ordem: Primeiro a raiz, depois a sub-árvore esquerda, e finalmente a

sub-árvore direita

– Em-ordem: Primeiro a sub-árvore esquerda, depois a raiz, e finalmente a

sub-árvore direita

– Pós-ordem: Primeiro a sub-árvore esquerda, depois a sub-árvore direita,

e finalmente a raiz

– Por nível: Os nós são processados por nível (profundidade) crescente, e

dentro de cada nível, da esquerda para a direita

(4)

AED - 2008/09 7

Percorrer árvores

- exemplo

+ * sen j -a b f * h Pré-ordem + * a b – sen f * h j Em-ordem a * b + f sen – h * j Pós-ordem a b * f sen h j * - + Por nível + * - a b sen * f h j

Árvores binárias:

implementação

• Operações:

– Criar uma árvore vazia

– Determinar se uma árvore está vazia

– Criar uma árvore a partir de duas sub-árvores

– Eliminar os elementos da árvore (esvaziar a árvore)

– Definir iteradores para percorrer a árvore

– Imprimir uma árvore

– ...

(5)

AED - 2008/09 9

Nó da árvore binária

template <class T> class BTNode { T element;

BTNode<T> *left, *right; friend class BinaryTree<T>; friend class BTItrIn<T>; friend class BTItrPre<T>; friend class BTItrPos<T>; friend class BTItrLevel<T>; public:

BTNode(const T & e, BTNode<T> *esq = 0, BTNode<T> *dir = 0) : element(e), left(esq), right(dir) {}

};

Árvores binárias:

implementação

Declaração da classe BinaryTree em C++

(secção privada)

template <class T> class BinaryTree { private:

BTNode<T> *root;

void makeEmpty(BTNode<T> *r);

BTNode<T> *copySubtree(const BTNode<T> *n) const; void outputPreOrder(ostream & out, const BTNode<T> *n) const; friend class BTItrIn<T>;

friend class BTItrPre<T>; friend class BTItrPos<T>; friend class BTItrLevel<T>; //...

(6)

AED - 2008/09 11

Declaração da classe BinaryTree em C++

(secção pública)

template <class T> class BinaryTree { public:

BinaryTree() { root = 0; } BinaryTree(const BinaryTree & t); BinaryTree(const T & elem);

BinaryTree(const T & elem, const BinaryTree<T> & e, const BinaryTree<T> & d); ~BinaryTree { makeEmpty(); }

const BinaryTree & operator=(const BinaryTree<T> & rhs); bool isEmpty() const { return ( root == 0 ) ? true : false; } T & getRoot() const {

if ( root ) return root->element ; else throw Underflow(); } void makeEmpty();

void outputPreOrder(ostream & out) const ; //...

};

Árvores binárias:

implementação

• classe BinaryTree : construtores

template <class T>

BinaryTree<T>:: BinaryTree(const T & elem) { root = new BTNode<T>(elem); }

template <class T>

BinaryTree<T>:: BinaryTree(const BinaryTree<T> & t) { root = copySubTree(t.root); }

template <class T>

BinaryTree<T>:: BinaryTree(const T & elem, const BinaryTree<T> & e, const BinaryTree<T> & d)

{

root = new BTNode<T>(elem, copySubTree(e.root), copySubTree(d.root) ); }

(7)

AED - 2008/09 13

• classe BinaryTree : copiar sub-árvores

template <class T>

BTNode<T> *BinaryTree<T>:: copySubTree(const BTNode<T> *n) const {

if ( n) {

BTNode<T> *node = new BTNode<T>(n->element,

copySubTree(n->left), copySubTree(n->right) ); return node;

} else return 0; }

template <class T>

const BinaryTree<T> & BinaryTree<T>:: operator=(const BinaryTree<T> & rhs) {

if ( this != & rhs ) { makeEmpty(); root = copySubTree(rhs.root); } return *this;

}

Árvores binárias:

implementação

classe BinaryTree : esvaziar uma árvore

template <class T>

void BinaryTree<T>:: makeEmpty() {

makeEmpty(root); root = 0;

}

template <class T>

void BinaryTree<T>:: makeEmpty(BTNode<T> * r) { if ( r ) { makeEmpty(r->left); makeEmpty(r->right); delete r; } }

(8)

AED - 2008/09 15

classe BinaryTree : impressão em pré-ordem

template <class T>

void BinaryTree<T>:: outputPreOrder(ostream & out) const { outputPreOrder(out, root); }

template <class T>

void BinaryTree<T>:: outputPreOrder(ostream & out, const BTNode<T> *r) const { out << ‘(´; if ( r ) { out << r->element << ´)´; outputPreOrder(out, r->left); out << ‘ ‘; outputPreOrder(out, r->right); } out << ´)´; }

Árvores binárias:

implementação

classe BTItrPre : iterador em pré-ordem

template <class T> class BTItrPre { public:

BTItrPre(const BinaryTree<T> & t); void advance();

T & retrieve();

bool isAtEnd() { return itrStack.empty(); } private:

stack<BTNode<T> *> itrStack; };

template <class T> BTItrPre<T>:: BItrPre(const BinaryTree<T> & t) { if ( !t.isEmpty() ) itrStack.push(t.root); }

template <class T> T & BTItrPre<T>:: retrieve() { return itrStack.top()->element; }

(9)

AED - 2008/09 17

classe BTItrPre : iterador em pré-ordem

template <class T> void BTItrPre<T>:: advance() {

BTNode<T> * actual = itrStack.top(); BTNode<T> * seguinte = actual->left; if ( seguinte )

itrStack.push(seguinte); else {

while ( ! itrStack.empty() ) {

actual = itrStack.top(); itrStack.pop(); seguinte = actual->right; if (seguinte) { itrStack.push(seguinte); break; } } } }

Árvores binárias:

implementação

classe BTItrIn : iterador em-ordem

template <class T> class BItrIn { public:

BTItrIn(const BinaryTree<T> & t); void advance();

T & retrieve();

bool isAtEnd() { return itrStack.empty(); } private:

stack<BTNode<T> *> itrStack; void slideLeft(BTNode<T> *n); };

template <class T> BTItrIn<T>:: BItrIn(const BinaryTree<T> & t) { if ( !t.isEmpty() ) slideLeft(t.root); }

template <class T> T & BTItrIn<T>:: retrieve() { return itrStack.top()->element; }

(10)

AED - 2008/09 19

classe BTItrIn : iterador em-ordem

template <class T> void BTItrIn<T>:: slideLeft(BTNode<T> *n) { while ( n ) { itrStack.push(n); n = n->left; } }

template <class T> void BTItrIn<T>:: advance() {

BTNode<T> * actual = itrStack.top(); itrStack.pop();

BTNode<T> * seguinte = actual->right; if ( seguinte )

slideLeft(seguinte); }

Árvores binárias:

implementação

classe BTItrIn : iterador em pós-ordem

template <class T> class BTItrPos { public:

BTItrPos(const BinaryTree<T> & t); void advance();

T & retrieve();

bool isAtEnd() { return itrStack.isEmpty(); } private:

stack<BTNode<T> *> itrStack; stack<bool> visitStack;

void slideDown(BTNode<T> *n); };

template <class T> BTItrPos<T>:: BItrPos(const BinaryTree<T> & t) { if ( !t.isEmpty() )

slideDown(t.root); }

(11)

AED - 2008/09 21

classe BTItrPos : iterador em pós-ordem

template <class T> T & BTItrPos<T>:: retrieve() { return itrStack.top()->element; } template <class T>

void BTItrPos<T>:: advance() {

itrStack.pop(); visitStack.pop();

if ( ( ! itrStack.empty() ) && ( visitStack.top() == false ) ) { visitStack.pop();

visitStack.push(true);

slideDown(itrStack.top()->right); }

}

Árvores binárias:

implementação

classe BTItrPos : iterador em pós-ordem

template <class T>

T & BTItrPos<T>:: slideDown(BTNode<T> *n) { while ( n ) { itrStack.push(n); if ( n->left ) { visitStack.push(false); n = n->left; } else if ( n->right ) { visitStack.push(true); n = n->right; } else { visitStack.push(true); break; } } }

(12)

AED - 2008/09 23

classe BTItrLevel : iterador por nivel

template <class T> class BTItrLevel { public:

BTItrLevel(const BinaryTree<T> & t); void advance();

T & retrieve();

bool isAtEnd() { return itrQueue.empty(); } private:

queue<BTNode<T> *> itrQueue; };

template <class T> BTItrLevel<T>:: BItrLevel(const BinaryTree<T> & t) { if ( !t.isEmpty() ) itrQueue.push(t.root); }

template <class T> T & BTItrLevel<T>:: retrieve() { return itrQueue.front()->element; }

Árvores binárias:

implementação

classe BTItrLevel : iterador por nivel

template <class T>

void BTItrLevel<T>:: advance() {

BTNode<T> * actual = itrQueue.front(); itrQueue.pop();

BTNode<T> * seguinte = actual->left; if ( seguinte ) itrQueue.push(seguinte); seguinte = actual->right; if ( seguinte ) itrQueue.push(seguinte); }

(13)

AED - 2008/09 25

Expressões aritméticas

+ 2 1 * -4 * 1 + 2 3 Expressão = 1 * ( 2 + 3 ) + ( 2 * ( 4 – 1 ) )

Árvores binárias:

aplicações

Construção da árvore de expressões

– O algoritmo é similar ao algoritmo de conversão infixa->RPN mas usa duas pilhas, uma para guardar os operadores e outra para guardar sub-árvores correspondentes a sub-expressões.

– O algoritmo procede da seguinte forma:

• Números são transformados em árvores de 1 elemento e colocados na pilha de operandos.

• Operadores são tratados como no programa de conversão de infixa -> RPN (usando uma pilha apenas para operadores e ‘(’).

• Quando um operador é retirado da pilha, duas sub-árvores (operandos) são retirados da pilha de operandos e combinados numa nova sub-árvore, que por sua vez é colocada na pilha.

• Quando a leitura da expressão chega ao fim, todos os operadores existentes na pilha são processados.

(14)

AED - 2008/09 27

Elementos de uma expressão

enum operador { numero, parentesis_esq, mais, menos, vezes, dividir }; // por ordem de prioridade class ExprElem {

public:

operador tipo; double num;

ExprElem(double n) : tipo(numero), num(n) { }; ExprElem(operador op) : tipo(op) { };

};

Processar um operador binário typedef BinaryTree<ExprElem> ArvArit;

void executaOpBin(operador op, stack<ArvArit> & operandStack) {

ArvArit right = operandStack.top (); operandStack.pop (); ArvArit left = operandStack.top (); operandStack.pop (); ExprElem e1(op);

ArvArit novaArv(e1, left, right); operandStack.push(novaArv); }

Árvores binárias:

aplicações

Processar os operadores de maior prioridade

void processaOp(operador op, stack<operador> &operatorStack, stack<ArvArit> &operandStack) {

while ((!operatorStack.empty()) && (op <= operatorStack.top())) { operador opx = operatorStack.top();

operatorStack.pop();

executaOpBin(opx, operandStack); }

operatorStack.push(op); }

(15)

AED - 2008/09 29

Núcleo do processamento de expressões int main()

{

stack<operador> operatorStack; stack<ArvArit> operandStack; string expressao;

cout << "Escreva uma expressão: "; cin >> expressao; for (int i=0; i<expressao.length(); i++) {

char c1=expressao[i]; switch(c1) {

case '(': operatorStack.push(parentesis_esq); break; case ')': while (operatorStack.top() != parentesis_esq) {

operador op=operatorStack.top(); operatorStack.pop(); executaOpBin(op, operandStack);

}

operatorStack.pop(); break;

case '+': processaOp(mais, operatorStack, operandStack); break; case '-': processaOp(menos, operatorStack, operandStack); break; case '*': processaOp(vezes, operatorStack, operandStack); break; case '/': processaOp(dividir, operatorStack, operandStack); break;

Árvores binárias:

aplicações

default: float y=c1-'0'; ExprElem e(y); ArvArit arv(e); operandStack.push(arv); break; } } while (!operatorStack.empty() ) { operador opx = operatorStack.top(); operatorStack.pop();

executaOpBin(opx, operandStack); }

// imprimir árvore final

ArvArit arv1= operandStack.top(); BTItrIn<ExprElem> it1(arv1); while ( !it1.isAtEnd() ) {

ExprElem e1=it1.retrieve();

cout << e1.tipo << ":" << e1.num << " "; it1.advance();

} }

(16)

AED - 2008/09 31

Árvore binária de pesquisa

Árvore binária, sem elementos repetidos, que verifica a seguinte propriedade: – Para cada nó, todos os valores da sub-árvore esquerda são menores, e todos os

valores da sub-árvore direita são maiores, que o valor desse nó

15

12

17 21

10 14

Árvores binárias de pesquisa

Estrutura linear com elementos ordenados

– A pesquisa de elementos pode ser realizada em O(log n) – ... mas não inserção ou remoção de elementos

Estrutura em árvore binária

– pode manter o tempo de acesso logarítmico nas operações de inserção e remoção de elementos

– Árvore binária de pesquisa

• mais operações do que árvore binária básica: pesquisar, inserir, remover • objectos nos nós devem ser comparáveis (Comparable)

(17)

AED - 2008/09 33

Pesquisa

– usa a propriedade de ordem na árvore para escolher caminho, eliminando uma sub-árvore a cada comparação

Inserção

– como pesquisa; novo nó é inserido onde a pesquisa falha

Máximo e mínimo

– procura, escolhendo sempre a subárvore direita (máximo), ou sempre a sub-árvore esquerda (mínimo)

Remoção

– Nó folha : apagar nó

– Nó com 1 filho : filho substitui o pai

– Nó com 2 filhos: elemento é substituído pelo menor da sub-árvore direita (ou maior da esquerda); o nó deste tem no máximo 1 filho que substitui o pai.

Árvores binárias de pesquisa:

implementação

Declaração da classe BST em C++

(secção privada)

template <class Comparable> class BST { private:

BinaryNode<Comparable> *root; const Comparable ITEM_NOT_FOUND;

const Comparable & elementAt( BinaryNode<Comparable> *t ) const; void insert( const Comparable & x, BinaryNode<Comparable> * & t ); void remove( const Comparable & x, BinaryNode<Comparable> * & t ); BinaryNode<Comparable> * findMin( BinaryNode<Comparable> *t ) const; BinaryNode<Comparable> * findMax( BinaryNode<Comparable> *t ) const; BinaryNode<Comparable> * find( const Comparable & x,

BinaryNode<Comparable> *t ) const; void makeEmpty( BinaryNode<Comparable> * & t );

void printTree( BinaryNode<Comparable> *t ) const;

BinaryNode<Comparable> * copySubTree( BinaryNode<Comparable> *t ); //...

(18)

AED - 2008/09 35

Declaração da classe BST em C++

(secção pública)

template <class Comparable> class BST { public:

explicit BST(const Comparable & notFound) { } BST(const BST & t);

~BST();

const Comparable & findMin() const; const Comparable & findMax() const;

const Comparable & find(const Comparable & x) const; bool isEmpty() const;

void printTree() const; void makeEmpty();

void insert(const Comparable & x); void remove(const Comparable & x); const BST & operator =(const BST & rhs); //...

};

Árvores binárias de pesquisa:

implementação

classe BST : construtores e destrutor

template <class Comparable>

BST<Comparable>::BST( const Comparable & notFound ) : root(NULL), ITEM_NOT_FOUND( notFound )

{ }

template <class Comparable>

BST<Comparable>::BST( const BST<Comparable> & rhs ) : root( NULL ), ITEM_NOT_FOUND( rhs.ITEM_NOT_FOUND ) { *this = rhs; }

template <class Comparable> BST<Comparable>::~BST( ) { makeEmpty( ); }

(19)

AED - 2008/09 37

classe BST : pesquisa de elementos

template <class Comparable>

const Comparable & BST<Comparable>:: find( const Comparable & x ) const { return elementAt( find( x, root ) ); }

template <class Comparable> const Comparable & BST<Comparable>::findMin( ) const { return elementAt( findMin( root ) ); }

template <class Comparable> const Comparable & BST<Comparable>::findMax( ) const { return elementAt( findMax( root ) ); }

template <class Comparable>

const Comparable & BST<Comparable>:: elementAt( BinaryNode<Comparable> *t ) const {

if( t == NULL ) return ITEM_NOT_FOUND; else return t->element;

}

Árvores binárias de pesquisa:

implementação

• classe BST : find

template <class Comparable> BinaryNode<Comparable> *

BST<Comparable>:: find(const Comparable & x, BinaryNode<Comparable> * t) const {

if ( t == NULL ) return NULL; else if ( x < t->element )

return find(x, t->left); else if ( t->element < x )

return find(x, t->right); else return t;

}

(20)

AED - 2008/09 39

classe BST : findMin , findMax

template <class Comparable> BinaryNode<Comparable> * BST<Comparable>:: findMin(BinaryNode<Comparable> * t) const {

if ( t == NULL ) return NULL; if ( t->left == NULL ) return t; return findMin(t->left); }

template <class Comparable> BinaryNode<Comparable> * BST<Comparable>:: findMax(BinaryNode<Comparable> * t) const {

if ( t != NULL )

while ( t->right != NULL ) t = t->right; return t;

}

Árvores binárias de pesquisa:

implementação

classe BST : insert

template <class Comparable>

void BST<Comparable>:: insert(const Comparable & x) { insert (x,root); }

template <class Comparable>

void BST<Comparable>:: insert(const Comparable & x, BinaryNode<Comparable> * & t) {

if ( t == NULL )

t = new BinaryNode<Comparable>(x, NULL, NULL); else if ( x < t->element)

insert(x, t->left); else if (t->element < x)

insert(x, t->right); else

; // não fazer nada. nó repetido }

(21)

AED - 2008/09 41

classe BST : remove

template <class Comparable>

void BST<Comparable>:: remove(const Comparable & x, BinaryNode<Comparable> * & t) {

if ( t == NULL ) return; // não existe if ( x < t->element ) remove(x, t->left); else if ( t->element < x ) remove(x, t->right); else if ( t->left != NULL && t->right != NULL ) {

t->element = findMin(t->right)->element; remove(t->element, t->right);

} else {

BinaryNode<Comparable> * oldNode = t; t = ( t->left != NULL ) ? t->left : t->right; delete oldNode;

} }

Árvores binárias de pesquisa:

implementação

classe BST : cópia e atribuição

As operações de cópia e atribuição são implementadas como na classe BinaryTree • make Empty , como na classe BinaryTree

• operator = , como na classe BinaryTree • copySubTree , como na classe BinaryTree

classe BST : iteradores

classe BSTItrIn : iterador em-ordemclasse BSTItrPre : iterador em pre-ordemclasse BSTItrPost : iterador em pos-ordemclasse BSTItrLevel : iterador em nivel – métodos dos iteradores:

BSTItrIn(const BST<Comparable> &arv) // construtor da BSTItrIn

BSTItrPre(const BST<Comparable> &arv) // construtor da BSTItrPre

BSTItrPost(const BST<Comparable> &arv) // construtor da BSTItrPost

BSTItrLevel(const BST<Comparable> &arv) // construtor da BSTItrLevel • void advance ()

Comparable & retrieve() • bool isAtEnd()

(22)

AED - 2008/09 43

• Contagem de ocorrências de palavras

Pretende-se escrever um programa que leia um ficheiro de texto e

apresente uma listagem ordenada das palavras nele existentes e o

respectivo número de ocorrências.

• Guardar as palavras e contadores associados numa árvore binária de pesquisa.

• Usar ordem alfabética para comparar os nós.

Árvores binárias de pesquisa:

aplicação

classe PalavraFreq : representação das palavras e sua frequência

class PalavraFreq {

string palavra; int frequencia; public:

PalavraFreq() : palavra(“”), frequencia(0) {}; PalavraFreq(string p) : palavra(p), frequencia(1) {};

bool operator < (const PalavraFreq & p) const { return palavra < p.palavra; } bool operator == (const PalavraFreq & p) const { return palavra == p.palavra; } friend ostream & operator << (ostream & out, const PalavraFreq & p); void incFrequencia() { frequencia ++; }

};

ostream & operator << (ostream & out, const PalavraFreq & p) { out << p.palavra << ‘ : ‘ << p.frequencia << endl; return out; }

(23)

AED - 2008/09 45

main() {

PalavraFreq notF(“”);

BST<PalavraFreq> palavras(notF); string palavra1 = getPalavra(); while ( palavra1 != “” ) {

PalavraFreq pesq = palavras.find(PalavraFreq(palavra1)); if ( pesq == notF ) palavras.insert(PalavraFreq(palavra1)); else { palavras.remove(pesq); pesq.incFrequencia(); palavras.insert(pesq); } palavra1 = getPalavra(); } BSTItrIn<PalavraFreq> itr(palavras); while ( ! itr.isAtEnd() ) {

cout << itr.retrieve(); itr.advance(); }

Referências

Documentos relacionados

Haveria agora algo que dizer -e haverá muito mais que estudar, pois não têm sido regiões que tenham merecido particular atenção por parte dos historiadores- sobre certas

[r]

void setEmRequisicao(bool r) { emRequisicao = r; } bool getEmRequisicao() { return emRequisicao; } bool operator &lt;(const InfoLivro &amp;info1) const; bool operator ==(const

[r]

Fernandes, morador no lugar de Ourentã, termo da Vila de Cantanhede e de Francisco Afonso, morador no lugar de Fornos, termo da cidade de Coimbra, para fornecimento de

Na questão que abordou o conhecimento sobre a localização da doença, o deficiente saber quanto à percepção sobre a saúde bucal foi comprovado quando somente 30 indivíduos

Portanto, deve-se reconhecer que o tipo de movimento ortodôntico pode influenciar no risco de desenvolvimento de recessão óssea e gengival, como nos casos de movimento

REDES INSTALACAO E COMERCIO DE REDES