estrutura de dados - apostila i

40
Apostila de Estruturas de Dados Organizada pelo Prof. Célio F. Silva Estruturas de Dados – Estudo de Pilhas, Filas e Listas encadeadas As estruturas de dados têm larga aplicação na computação em geral. Sistemas Operacionais e aplicativos as utilizam para várias atividades importantíssimas, como gerenciamento de memória, execução de processos, armazenamento e gerenciamento de dados no disco, etc. Ou seja, não faltam motivos para um estudante da área ou qualquer desenvolvedor /programador saberem a fundo e com fluência sobre o assunto. É necessário que você tenha conhecimento prévio e boa fluência com ponteiros, passagem de parâmetros e comandos na linguagem C. Afinal, quem está desenvolvendo uma aplicação e busca solução em estruturas de dados, supõe-se que já tenha tal conhecimento sobre algoritmos “na ponta da língua”. Pilhas O funcionamento de uma pilha consiste numa estratégia chamada LIFO (last in, first out – último a entrar, primeiro a sair). O único elemento que se pode acessar na pilha é o elemento do topo da mesma, ou seja, o último a ser empilhado. Imagine uma pilha de pratos. Quando se vai lavá-los, você não começa retirando o que está mais abaixo, e sim o que está no topo da pilha. É basicamente assim que esse tipo de estrutura de dados funciona. Implementando as seguintes funções para Pilha: typedef struct pilha Pilha; //redefinição do tipo struct pilha para simplesmente Pilha - Pilha* pilha_cria (void); //cria uma pilha - void pilha_insere (Pilha* p, float v); // empilha um novo elemento - float pilha_retira (Pilha* p); //retira um elemento (o último) - int pilha_vazia (Pilha* p); //verifica se a pilha está vazia - void pilha_libera (Pilha* p); //esvazia toda a estrutura alocada para a pilha, liberando seus elementos Pode-se implementar uma pilha utilizando vetores, aonde os elementos serão armazenados, ocupando as primeiras posições do vetor. Dessa forma, se temos n elementos armazenados na pilha, o elemento vet[n-1] representa o do topo. Código: #define N 50

Upload: wanderson-santos

Post on 03-Jul-2015

318 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Estrutura de Dados - Apostila I

Apostila de Estruturas de Dados Organizada pelo Prof. Célio F. Silva

Estruturas de Dados – Estudo de Pilhas, Filas e Listas encadeadas

As estruturas de dados têm larga aplicação na computação em geral. Sistemas Operacionais e aplicativos as utilizam para várias atividades importantíssimas, como gerenciamento de memória, execução de processos, armazenamento e gerenciamento de dados no disco, etc. Ou seja, não faltam motivos para um estudante da área ou qualquer desenvolvedor/programador saberem a fundo e com fluência sobre o assunto. É necessário que você tenha conhecimento prévio e boa fluência com ponteiros, passagem de parâmetros e comandos na linguagem C. Afinal, quem está desenvolvendo uma aplicação e busca solução em estruturas de dados, supõe-se que já tenha tal conhecimento sobre algoritmos “na ponta da língua”.

Pilhas O funcionamento de uma pilha consiste numa estratégia chamada LIFO (last in, first out – último a entrar, primeiro a sair). O único elemento que se pode acessar na pilha é o elemento do topo da mesma, ou seja, o último a ser empilhado.

Imagine uma pilha de pratos. Quando se vai lavá-los, você não começa retirando o que está mais abaixo, e sim o que está no topo da pilha. É basicamente assim que esse tipo de estrutura de dados funciona.

Implementando as seguintes funções para Pilha:

typedef struct pilha Pilha; //redefinição do tipo struct pilha para simplesmente Pilha - Pilha* pilha_cria (void); //cria uma pilha - void pilha_insere (Pilha* p, float v); // empilha um novo elemento - float pilha_retira (Pilha* p); //retira um elemento (o último) - int pilha_vazia (Pilha* p); //verifica se a pilha está vazia - void pilha_libera (Pilha* p); //esvazia toda a estrutura alocada para a pilha, liberando seus elementos Pode-se implementar uma pilha utilizando vetores, aonde os elementos serão armazenados, ocupando as primeiras posições do vetor. Dessa forma, se temos n elementos armazenados na pilha, o elemento vet[n-1] representa o do topo.

Código:

#define N 50

Page 2: Estrutura de Dados - Apostila I

struct pilha { int n; float vet[N]; }; typedef struct pilha Pilha;

A função que cria a Pilha aloca dinamicamente essa estrutura e inicializa a pilha como sendo vazia, ou seja, com o número de elementos igual a zero. Código:

Pilha* pilha_cria (void) { Pilha* p = (Pilha*)malloc(sizeof(Pilha)); p->n = 0; //inicializa com zero elementos return p; }

Como nossa pilha está implementada usando um vetor de tamanho fixo, devemos sempre verificar se existe espaço para inserção de um novo elemento. Código:

void pilha_insere (Pilha* p, float v){ if (p->n == N) //verifica se a capacidade do vetor foi esgotada { printf (“Capacidade da pilha estourou!\n”); exit (1); } /*insere o novo elemento na próxima posição livre*/ p->vet[p->n] = v; p->n++; }

A função de remoção retira um elemento (o que estiver no topo) e retorna esse elemento, daí o retorno dessa função ser do tipo float, pois esse é o tipo do dado que estamos armazenando na pilha. Se for um dado de outro tipo (char, por exemplo), bastaria você alterar o tipo de retorno da função para char. Código:

float pilha_remove (Pilha* p) { float v; if (pilha_vazia (p)) { printf (“Pilha está vazia!\n”); exit(1); } /*agora retira o elemento do topo da pilha*/ v = p->vet[p->(n-1)]; p->n--; return v; }

Pode-se implementar a função que verifica se a pilha está vazia da seguinte maneira: Código:

int pilha_vazia (Pilha* p) {

Page 3: Estrutura de Dados - Apostila I

return (p->n == 0); /*se isto for verdade, será retornado o conteúdo dos parêntesis, caso contrário será retornado zero. */ }

E agora a função para liberar a memória alocada para a pilha: Código:

void pilha_libera (Pilha* p) { free(p); }

Filas Basicamente o que diferencia a Fila da Pilha é a ordem de saída dos elementos. Enquanto na pilha o elemento retirado é sempre o último a entrar (o do topo da pilha), na fila sempre é retirado o primeiro elemento a entrar na estrutura. Pode-se fazer uma analogia com uma fila de banco por exemplo, onde a primeira pessoa a ser atendida é a que chega primeiro. À medida que outras pessoas chegam na fila, deverão permanecer na fila aguardando que sejam atendidas, seguindo este critério.

Implementaremos as seguintes funções para filas:

typedef struct fila Fila; //redefinição do tipo struct fila para simplesmente Fila - Fila* fila_cria (void); //cria uma fila - void fila_insere (Fila* p, float v); // enfileira um novo elemento - float fila_retira (Fila* p); //retira um elemento (primeiro) - int fila_vazia (Fila* p); //verifica se a fila está vazia - void fila_libera (Fila* p); //esvazia toda a estrutura alocada para a fila, liberando seus elementos Mais uma vez, para simplificar, vamos implementar uma Fila a partir de um vetor. Como um vetor em C tem uma quantidade fixa de elementos, digamos, N elementos, observe que mais uma vez temos um limite na quantidade de elementos a serem armazenados na fila. De acordo com a idéia central da fila (FIFO - primeiro a entrar, primeiro a sair), ao usarmos um vetor para implementação da fila, observe que, quando retiramos elementos dela, é como se a fila “andasse” no vetor, pois ao se retirar elemento(s) da fila, o índice do vetor que representa o topo da fila se altera. Por exemplo, imagine um vetor de N = 6 posições. Se inserirmos 4 elementos, teremos as 4 primeiras posições da fila ocupadas. Ao retirarmos 2 elementos (lembre-se que serão retirados os 2 primeiros), temos agora a situação em que o 1º elemento da fila, ou seja, o topo dela, será representado pelo 3º elemento inicialmente inserido, conforme ilustra a figura:

Se retirarmos 2 elementos, teremos a seguinte situação:

Page 4: Estrutura de Dados - Apostila I

A partir de agora, o 1º elemento da fila passa a ser o elemento 55, que foi o 3º elemento inserido, presente no índice 2 do vetor. Observe que vetores em C sempre iniciam com índice 0 e não 1. Por causa desse fato, precisamos fazer uma alteração nos índices do vetor a cada vez que realizarmos uma remoção de quaisquer elementos da fila. Essa reordenação é feita de forma “circular” no vetor, afim de aproveitar as posições liberadas quando se retira elementos. No exemplo citado, teríamos a seguinte situação ilustrativa, após essa reordenação:

Essa reordenação de índices pode ser feita utilizando uma função específica: Código:

Static int incr (int i) { return (i+1)%N; }

Pode-se dispensar o uso desta função utilizando diretamente apenas o incremento circular, já que essa função seria sistematicamente chamada a cada vez que se retirar elementos da fila, e isso é um processo computacionalmente custoso. O incremento circular ficaria assim:

Código:

i = (i+1)%N;

Para a interface da fila, Pode-se utilizar uma estrutura contando 3 campos: um vetor de tamanho N, um inteiro n para representar o número de elementos armazenados na fila e um índice ini para o início da fila. Tendo o índice para o início da fila, Pode-se calcular o índice para o final da fila, que chamaremos de fim da seguinte maneira:

Código:

fim = (ini+n)%N;

A estrutura da fila fica então:

Código:

#define N 100

Page 5: Estrutura de Dados - Apostila I

struct fila { int n; int ini; float vet[N]; }

A função que cria a fila:

Código:

Fila* fila_cria (void) { Fila* f = (Fila*) malloc(sizeof(Fila)); f->n = 0; //inicializa a fila vazia; f->ini = 0; //escolhe uma posição inicial; return f; }

A função que insere elementos na fila deve verificar se existe espaço disponível para esta inserção, e inserir o elemento no final da fila, utilizando o índice fim, conforme já falamos anteriormente:

Código:

void fila_insere (Fila* f, float v) { int fim; if (f-> == N) { printf (“Fila não tem espaço disponível”); exit (1); } fim = (f->ini + f->n)%N; //cálculo do índice do último elemento f->vet[fim] = v; f->n++; }

A função para retirar um elemento da fila verifica se a fila já está ou não vazia:

Código:

float fila_retira (Fila* f) { float v; if (fila_vazia(f)) { printf (“Fila vazia\n”); exit (1); } v = f->vet[f->ini]; f->ini = (f->ini + 1) % N; f->n--; return v;

Função para verificar se a fila está vazia:

Page 6: Estrutura de Dados - Apostila I

Código:

int fila_vazia (Fila* f) { return (f->n == 0); }

Função para liberar toda a memória alocada para a fila:

Código:

void fila_libera (Fila* f) { free (f); }

Listas encadeadas Listas são estruturas de dados que contém um conjunto de blocos de memória que armazenam dados. Esses blocos são encadeados (ligados) por ponteiros, formando uma espécie de “corrente”, onde as peças dessa corrente estão ligadas umas as outras. O encadeamento de listas pode ser de dois tipos: simplesmente encadeada e duplamente encadeada. Listas simplesmente encadeadas Listas simplesmente encadeadas possuem um único ponteiro, que apontará para o próximo elemento da lista. Código:

struct lista { int info; struct lista* prox; } typedef struct lista Lista;

A função que cria uma lista simplesmente encadeada retornará um NULL do tipo da lista, ou seja, uma lista totalmente vazia:

Código:

Lista* lst_cria (void) { return NULL; }

A função de inserção insere novos elementos no início da lista encadeada, pois esta é a maneira mais simples de se inserir elementos numa lista, seja ela simplesmente ou duplamente encadeada. É importante lembrar que uma lista é representada pelo ponteiro para o primeiro elemento dela todos os demais elementos estão encadeados um a um sucessivamente, a partir do primeiro. Ou seja, para se acessar qualquer elemento da lista, basta fornecer o ponteiro para o primeiro elemento e ir percorrendo elemento a elemento até se chegar no elemento desejado.

Page 7: Estrutura de Dados - Apostila I

Código:

Lista* lst_insere (Lista* l, int i) { Lista* novo = (Lista*) malloc (sizeof(Lista)); novo->info = i; novo->prox = l; return novo; }

Observe que o novo elemento é encadeado aos demais já existentes na lista (caso ela já tenha outros elementos) e a nova lista é representada pelo novo elemento inserido, que passa a ser o 1º da lista. Função que imprime na tela os elementos da lista:

Código:

void lst_imprime (Lista* l) { Lista*p; for (p = l; p != NULL; p = p->prox) { printf (“info = %d\n”, p->info); } }

Função para verificar se a lista está vazia:

Código:

int lst_vazia (Lista* l) { return (l == NULL); }

Para criarmos uma função que busque um determinado elemento, vamos implementá-la de forma a retornar um ponteiro para o elemento buscado. Como sempre, você pode alterar a maneira de retorno de quaisquer das funções aqui mostradas, de forma a se enquadrar melhor ao seu caso.

Código:

Lista* lst_retira (Lista* l, int v) { Lista* ant = NULL; Lista* p = l; while (p != NULL && p->info != v) { ant = p; p = p->prox; } if (p == NULL)

Page 8: Estrutura de Dados - Apostila I

return l; if (ant == NULL) { l = p->prox; } else { ant->prox = p->prox; } free (p); return l; }

Função para liberar os elementos da lista:

Código:

void lst_libera (Libera* l) { Lista* p= l; while (p != NULL) { Lista* t = p->prox; free (p); p = t; } }

Listas duplamente encadeadas Uma lista duplamente encadeada possui 2 ponteiros em cada Nó, um para o próximo elemento e outro para o anterior (ant e prox respectivamente). Isso possibilita “andarmos” para “frente” e para “trás” ao longo da lista. Logicamente, a estrutura desse tipo de lista é diferente das listas simplesmente encadeadas:

Código:

struct lista2 { int info; struct lista2* ant; struct lista2* prox; }; typedef struct lista2 Lista2;

Função de inserção no início:

Page 9: Estrutura de Dados - Apostila I

Código:

Lista2* lst2_insere (Lista2* l, int v) { Lista2* novo = (Lista2*)malloc(sizeof(Lista2)); novo->info = v; novo->prox = l; novo->ant = NULL; if (l != NULL) l->ant = novo; return novo; }

Função de busca:

Código:

Lista2* lst2_busca (Lista2* l, int v) { Lista2* p; for (p = l; p != NULL; p = p->prox) if (p->info == v) return p; return NULL; //não encontrou o elemento

Função para retirar um elemento:

Código:

Lista2* lst2_retira (Lista* l, int v) { Lista2* p = busca(l, v); //procura o elemento if (p == NULL) //testa se é o 1º elemento return l; if (l == p) l = p->prox; else p->ant->prox = p->ant; if (p->prox != NULL) //verifica se é o último elemento p->prox->ant = p->ant; free (p); return l; }

Além dessas estruturas, é possível criar muitas outras até mesmo juntando estruturas diferentes para se formar uma. Por exemplo, você pode implementar uma pilha ou fila utilizando uma lista encadeada ao invés de um vetor, como fizemos. O retorno das funções podem ser diferentes, de acordo com a necessidade, e assim por diante. As estruturas de dados são “maleáveis” e você pode utilizá-las como quiser, desde que preserve os conceitos fundamentais de cada uma.

Page 10: Estrutura de Dados - Apostila I

Nada do que foi explanado aqui foi copiado de alguém, apenas os códigos das funções que utilizei de um livro de estruturas de dados, chamado “Introdução a Estruturas de Dados”, de Waldemar Celes. Sintam-se a vontade para fazer sugestões e críticas à respeito do texto, no intuito de melhorar o fácil entendimento do mesmo a todos, principalmente iniciantes. Este tópico é apenas para ensinar e tirar dúvidas a respeito das estruturas de dados em C, portanto peço que o utilizem com bom censo. __________________

"O importante é ganhar. Tudo e sempre. Essa história que o importante é competir não passa de demagogia." Senna

Observação (1)

� A alteração de uma estrutura de dados em um programa lento pode mudar sensivelmente seu tempo de execução.

� Esta alteração não muda a corretude do programa. � É importante projetar os programas tendo as estruturas

de dados como centro. � A construção de algoritmos em torno de estruturas de

dados tais como dicionários e Filas de prioridades leva a estruturas limpas e bons desempenhos.

� A escolha errada de uma estrutura de dados pode ser desastroso para o desempenho.

� A escolha da melhor estrutura de dados não é tão crítica, pois podem existir outras escolhas que se comportam similarmente.

Page 11: Estrutura de Dados - Apostila I

Tipos de dados fundamentais

� Um tipo de dado abstrato é uma coleção de operações bem definidas que podem ser feitas em uma estrutura particular.

� São estas operações que definem o que a estrutura faz, mas não como ela funciona.

Observação (2)

� É importante construir os programas de forma que implementações alternativas possam ser experimentadas.

� É importante separar os componentes da estrutura de dados de sua interface.

� Esta abstração de dados é importante para a limpeza, leitura e modificação dos programas.

� A ordenação é uma das partes princiPais de um algoritmo. Deveria ser a primeira coisa a ser feita para buscar eficiência.

� A ordenação pode ser usada para ilustrar muitos paradigmas de projeto de algoritmos. Técnicas de estruturas de dados, divisão e conquista, randomização e

Page 12: Estrutura de Dados - Apostila I

Containers

� É uma estrutura de dados que permite o armazenamento e a recuperação de dados independentemente de seu conteúdo.

� As operações fundamentais são: � Put(C,x) – insere o dado x no container C. � Get(C) – recupera o próximo item do container C.

Tipos diferentes de containers admitem diferentes tipos de recuperação, baseado na ordem ou posição de inserção.

Page 13: Estrutura de Dados - Apostila I

Containers (2)

� A utilidade de containers é maior quando a quantidade de dados é limitada e a ordem de recuperação é pré-definida ou irrelevante.

� Tipos de containers: � Pilhas � Filas � Tabelas

� A ordenação é uma das partes princiPais de um algoritmo. Deveria ser a primeira coisa a ser feita para buscar eficiência.

� A ordenação pode ser usada para ilustrar muitos paradigmas de projeto de algoritmos. Técnicas de estruturas de dados, divisão e conquista, randomização e construção incremental levam a algoritmos de ordenação.

Page 14: Estrutura de Dados - Apostila I

Estrutura de Dados - Árvores

Tree - Árvores

Uma árvore não ordenada simples, conforme este diagrama - o Nó rotulado 7 tem dois Filhos, o 2 e o 6, e um dos Pais, com o 2. O Nó raiz, no topo, não tem Pai.

Em ciência da computação , uma árvore é uma estrutura amplamente utilizada. Esta estrutura de dados que emula um sistema hierárquico (estrutura de árvore) com um conjunto de Nós associados.

Matematicamente, é uma árvore , mais especificamente uma arborescência : um acíclico conectado grafo onde cada Nó tem Filhos ou mais Nós de zero e em um Nó Pai. Além disso, os Filhos de cada Nó tem uma ordem específica.

Terminologia

Um Nó é uma estrutura que pode conter um valor, uma condição, ou representar uma estrutura de dados separados (o que poderia ser uma árvore de seu próprio). Cada Nó de uma árvore ou mais Nós Filho zero, que estão abaixo dele na árvore (por convenção, as árvores são extraídas cresce para baixo). Um Nó que tem um Filho é chamado Filho do Pai do Nó (ou nodo Pai, ou superior ). Um Nó tem no máximo um Pai.

Nós que não têm Filhos são chamados Nós folhas . Eles também são chamados de Nós terminais.

Uma árvore livre é uma árvore que não tem raízes.

A altura de um Nó é o comprimento do caminho mais longo para baixo de uma folha desse Nó. A altura da raiz é a altura da árvore. A profundidade de um Nó é o comprimento do caminho para a raiz (ou seja, o caminho de raiz). Isto é comumente necessária na manipulação dos diferentes árvores auto balanceamento, Árvores AVL , em particular. Convencionalmente, o valor -1 corresponde a um sub sem Nós, ao passo que zero corresponde a uma subárvore com um Nó.

O Nó mais alto em uma árvore é chamado Nó raiz. Sendo o Nó superior, o Nó raiz não têm Pais. É o Nó em que as operações sobre a árvore comumente começar (apesar de alguns algoritmos de começar com os Nós folha e trabalhar até terminar na raiz). Todos os outros Nós podem ser alcançados a partir dele, seguindo as bordas ou links. (Na definição formal, cada caminho como também é único). Nos diagramas, é tipicamente desenhado no topo. Em algumas árvores, tais como pilhas , o Nó raiz tem propriedades especiais. Cada Nó em uma árvore pode ser visto como o Nó raiz da subárvore enraizada naquele Nó.

Page 15: Estrutura de Dados - Apostila I

Um Nó interno ou Nó interno é qualquer Nó de uma árvore que Nós Filho , não sendo, portanto, um Nó folha .

A subárvore de uma árvore T é uma árvore que consiste em um Nó em T e todos os seus descendentes em T. (Isto é diferente da definição formal de sub utilizados na teoria dos grafos. [1] ) A sub-árvore correspondente ao Nó raiz é a árvore inteira, a subárvore correspondente a qualquer outro Nó é chamado de sub bom (em analogia ao termo apropriado subconjunto ).

Tree - Representações

Há muitas maneiras diferentes para representar árvores, representações comuns representam a Nós como registros alocado na heap (para não ser confundida com a estrutura de dados heap ) com ponteiros para seus Filhos, seus Pais, ou ambos, ou de itens em um array , com as relações entre eles determinada pela sua posição na matriz (por exemplo, heap binário ).

Árvores e gráficos

A árvore de estrutura de dados pode ser generalizada para representar dirigido gráficos , eliminando as restrições que um Nó pode ter, no máximo, um Pai, e que não são permitidos ciclos. As bordas são ainda considerados abstratamente como pares de Nós, no entanto, a mãe ea criança são termos usualmente substituída pela terminologia diferente (por exemplo, de

origem e destino). Diferentes estratégias de implementação existem, por exemplo, listas de adjacência .

Relação com as árvores em teoria dos grafos

Em teoria dos grafos , uma árvore é conectado acíclico gráfico , salvo indicação em contrário, as árvores e os gráficos são sem direção. Não existe uma correspondência um-para-um entre as árvores e as árvores, tais como estrutura de dados. Pode-se ter uma árvore sem direção arbitrária, arbitrariamente, escolher um de seus vértices como a raiz, fazer todas as suas arestas, tornando-os pontos de distância do Nó raiz - produzindo uma arborescência - e atribuir uma ordem para todos Nós. O resultado corresponde a uma estrutura de dados árvore. Escolher uma raiz diferente ou ordenação produz um diferente.

MétodoTraversal

Ver artigo principal: o percurso da árvore

Percorrendo os elementos de uma árvore, por meio das ligações entre Pais e Filhos, é chamado de pé da árvore, ea ação é um pé de árvore. Muitas vezes, uma operação pode ser realizada quando um ponteiro chega a um Nó particular. Uma caminhada em que cada Nó Pai é atravessado antes dos seus Filhos é chamada de pré-encomenda a pé, uma caminhada em que as crianças são percorridos antes de seus respectivos Pais são percorridos é chamado de pós-ordem a pé, uma caminhada em que deixou subárvore de um Nó , então o próprio Nó, e finalmente a sua subárvore direita são percorridos é chamado de passagem de fim-in. (Este último cenário, referindo-se exatamente duas subárvores, uma subárvore esquerda e uma subárvore direita, assume, especificamente, uma árvore binária ).

Operações comuns

• Enumerando todos os itens • Enumerando uma seção de uma árvore

Page 16: Estrutura de Dados - Apostila I

• Pesquisando um item • Adicionando um novo item em uma determinada posição na árvore • Excluindo um item • Remover uma seção inteira de uma árvore (chamada poda ) • Adicionando uma seção inteira de uma árvore (chamada de enxertia ) • Encontrando a raiz para qualquer Nó

Usos comuns

• Manipular hierárquica de dados • Tornar as informações fáceis de pesquisa (veja o percurso da árvore ) • Manipular as listas ordenadas de dados • Como um fluxo de trabalho para composição de imagens digitais para efeitos visuais • algoritmos Router • Forma de multi-estágio de tomada de decisão (ver xadrez negócio )

Estude também

• Árvore (teoria dos grafos) • Árvore (teoria dos conjuntos) • Estrutura em árvore • Hierarquia (matemática)

Outras árvores

• algoritmo DSW • Tiro • Árvore binária Esquerda-direita criança irmão

Page 17: Estrutura de Dados - Apostila I

Árvore binária

Uma árvore binária simples de tamanho 9 e 3 de altura, com um Nó raiz cujo valor é de 2. A árvore acima não é nem classificado nem uma árvore binária balanceada

Em ciência da computação , uma árvore binária é uma árvore de estrutura de dados em que cada Nó tem no máximo dois Filhos . Normalmente, o primeiro Nó é conhecido como o Pai e os Nós Filhos são chamados de esquerda e direita.

Na teoria dos tipos , uma árvore binária com os Nós do tipo A é definida indutivamente como T =

A μα. 1 + A × α × α. Árvores binárias são comumente usadas para implementar as árvores de busca binária e heaps binários .

Definições para árvores com raízes

• Uma aresta direcionada refere-se à ligação da mãe para o Filho (setas na imagem da árvore).

• O Nó raiz de uma árvore é o Nó sem Pais. Há mais de um Nó raiz de uma árvore enraizada.

• Um Nó folha não tem Filhos. • A profundidade de um Nó n é o comprimento do caminho desde a raiz até o Nó. O

conjunto de todos os Nós em uma determinada profundidade é às vezes chamado de um nível da árvore. O Nó raiz é zero em profundidade (ou uma [1] ).

• A altura de uma árvore é o comprimento do caminho desde a raiz até o mais profundo Nó na árvore. A raiz) da árvore (com apenas um Nó (a raiz) tem uma altura de zero (ou um [2] ).

• Os irmãos são os Nós que compartilham o mesmo Nó Pai. • Se existe um caminho de Nó a Nó p, q, onde p Nó está mais próximo do Nó de raiz do

que q, então p é um antepassado de Q e Q é um descendente de p. • O tamanho de um Nó é o número de descendentes que tem inclusive em si. • Em grau de um Nó é o número de arestas que chegam a esse Nó. • Fora grau de um Nó é o número de arestas que saem do Nó. • Raiz é o único Nó na árvore com In-degree = 0.

Tipos de árvores binárias

• Um binário é uma árvore enraizada enraizado árvore em que cada Nó tem no máximo dois Filhos.

• Uma árvore binária completa (às vezes boa árvore binária ou 2-árvore ou árvore estritamente binária) é uma árvore na qual todos os outros Nós do que as folhas tem dois Filhos.

Page 18: Estrutura de Dados - Apostila I

• Uma árvore binária perfeita é uma árvore binária completa em todas as folhas que estão na mesma profundidade ou mesmo nível. [3] (Esta é ambígua também chamada de árvore binária completa.)

• Uma árvore binária completa é uma árvore binária na qual todos os níveis, exceto

possivelmente o último, está completamente cheio, e todos os Nós são os mais à esquerda possível. [4]

• Uma árvore binária completa infinito é uma árvore com níveis, onde para cada nível d o número de Nós existentes a nível d é igual a 2 d. O número cardinal do conjunto de

todos Nós é . O número cardinal do conjunto de todos os caminhos é . O binário completo árvore infinita essencialmente descreve a estrutura do conjunto de

Cantor ; o intervalo de unidade sobre o eixo real (de cardinalidade ) É a imagem contínua do conjunto de Cantor, esta árvore é chamado às vezes o espaço de Cantor .

• Uma árvore binária balanceada é onde a profundidade de todas as folhas difere por no máximo, 1. Balanced as árvores têm uma profundidade previsível (quantos Nós são percorridos desde a raiz até uma folha, raiz contando como Nó 0 e posterior como 1, 2, ..., profundidade). Esta profundidade é igual à parte inteira do l o g 2 (n) onde n é o número de Nós na árvore equilibrada. Exemplo 1: árvore balanceada com um Nó, o l g 2 (1) = 0 (profundidade = 0). Exemplo 2: árvore balanceada com 3 Nós, o g l 2 (3) = 1,59 (profundidade = 1). Exemplo 3: árvore balanceada, com 5 Nós, o l g 2 (5) = 2,32 (profundidade da árvore é de 2 Nós).

• Uma árvore enraizada binário completo pode ser identificada com um magma livre . • Um degenerado árvore é uma árvore onde para cada Nó Pai, há apenas um associado

Nó Filho. Isto significa que em uma avaliação de desempenho, a árvore se comportar como uma estrutura de dados lista ligada.

Uma árvore de raízes tem um Nó de topo como root.

Propriedades de árvores binárias

• O número de Nós n em uma árvore binária perfeita pode ser encontrada usando a seguinte fórmula: n = 2

h + 1-1, onde h é a altura da árvore. • O número de Nós n em uma árvore binária completa é mínimo: n = 2

horas e máximo: n = h 2

+ 1-1, onde h é a altura da árvore. • O número de Nós n em um binário árvore perfeita também podem ser encontradas

usando esta fórmula: n = 2 L - 1, onde L é o número de Nós folhas da árvore. • O número de n Nós folha em uma árvore binária perfeita pode ser encontrada usando a

seguinte fórmula: n = 2 h, onde h é a altura da árvore.

• O número de links NULL em uma árvore binária completa do Nó n é (n +1). • O número de Nó de folha de uma árvore binária completa do Nó n é U p p e B r u o n d (n

/ 2). • Para qualquer vazio árvore não binária com os Nós folha n 0 e n 2 Nós de grau 2, n 0 n = 2

+ 1. [5] • n = n 0 + 1 n n n + 2 + 4 n + 3 n + 5 + .... B n-1 + n + B • B = n - 1, n = 1 + 1 * n 1 + n 2 * 2 + 3 * n 3 + 4 * 4 + n ... B + B * n, para não incluir n 0

- Note que essa terminologia varia frequentemente na literatura, especialmente no que diz respeito ao significado "completo" e "cheio".

Page 19: Estrutura de Dados - Apostila I

Prova

prova: n0 = 1 n2

Seja n = número total de Nós B = os ramos de T n0, n1, n2 representam os Nós sem Filhos, Filho único, e duas crianças, respectivamente. B = n - 1 B = 2 * n1 + n2 n = n1 * n2 + 2 + 1 n = n0 + n1 + n2 n1 + n2 * 2 + 1 = n0 + n1 + n2 ==> n0 = n2 + 1

Definição em teoria dos grafos

Teoria dos Gráficos usa a seguinte definição: Uma árvore binária é ligada grafo acíclico de tal forma que o grau de cada vértice não é mais do que três. Pode ser mostrado que, em qualquer árvore binária de dois ou mais Nós, há exatamente dois Nós mais de um grau do que há de grau três, mas não pode haver qualquer número de Nós de grau dois. Uma árvore binária enraizada é tal um gráfico que tem um de seus vértices de grau não superior a dois apontada como a raiz.

Com a raiz, assim escolhidos, cada vértice terá um Pai exclusivamente definido, e até dois Filhos, no entanto, até agora não há informação suficiente para distinguir um Filho esquerdo ou direito. Se deixar cair a exigência de conectividade, permitindo que vários componentes ligados no gráfico, que chamamos de uma estrutura de uma floresta.

Outra maneira de definir árvores binárias é uma definição recursiva em grafos dirigidos. Uma árvore binária é:

• Um único vértice. • Um gráfico formado por tomar duas árvores binárias, adicionando um vértice, e

adicionando uma borda dirigiu a partir do novo vértice para a raiz da cada árvore binária.

Isso também não estabelecer a ordem dos Filhos, mas não fixa um Nó raiz específica.

Combinatória

Os agrupamentos de pares de Nós em uma árvore pode ser representada como pares de letras, cercada por parênteses. Assim, (ab) denota a árvore binária cuja subárvore esquerda é um direito e cuja subárvore é b. Seqüências de pares de parênteses equilibrada pode ser utilizada para designar árvores binárias em geral. O conjunto de todas as seqüências possíveis consiste inteiramente de parênteses equilibrada é conhecida como a linguagem Dyck .

Dado n Nós, o número total de maneiras em que estes Nós podem ser organizados em uma árvore binária é dada pelo número de Catalão n C. Por exemplo, C 2 = 2 declara que (a 0) e 0 (a) são as únicas árvores binárias possível que Nós dois, e C 3 = 5 declara que ((a 0) 0), ((0 a 0) ), (0

(um 0)), (0 (0 a)) e (ab) são os únicos cinco árvores binárias possível que três Nós. Aqui 0 representa uma subárvore que não está presente.

A capacidade de representar árvores binárias como seqüências de símbolos e parênteses implica que árvores binárias pode representar os elementos de um magma . Por outro lado, o

Page 20: Estrutura de Dados - Apostila I

conjunto de todas as árvores binárias possível, juntamente com a operação natural de unir árvores para um outro, formam um magma, o magma livre .

Dada uma seqüência que representa uma árvore binária, o operador para obter a subárvore esquerda e direita são muitas vezes referidos como carro e cdr .

Métodos para o armazenamento de árvores binárias

Árvores binárias podem ser construídas a partir de linguagem de programação primitivas de várias maneiras.

Nós e as referências

Em uma linguagem com os registros e referências , árvores binárias são geralmente construídos por ter uma estrutura de Nó de árvore que contém alguns dados e referências a seu Filho e deixou seu Filho direito. Às vezes, ele também contém uma referência a seu Pai único. Se um Nó que tem menos de dois Filhos, alguns dos ponteiros Filho pode ser definido como um valor nulo especial, ou para um especial do linfonodo sentinela .

Em línguas com sindicatos marcou como ML , um Nó de árvore é muitas vezes uma união etiquetada de dois tipos de Nós, um dos quais é a-tupla de dados, deixou três Filhos, eo Filho a direita, e os outros de que é uma folha " "Nó, que não contém dados e funções bem como o valor nulo em uma linguagem com ponteiros.

Lista Ahnentafel

Árvores binárias podem também ser armazenados como uma estrutura de dados implícitos em arrays , e se a árvore é uma árvore binária completa, este método não desperdiça espaço. Neste arranjo compacto, se um Nó possui um índice i, seus Filhos são encontrados em dois índices i + 1 (para a esquerda da criança) e 2 i + 2 (para a direita), enquanto a sua mãe (se houver) é

encontrada em índice (Supondo que a raiz tem índice zero). Isto beneficia método de armazenamento compacto mais e melhor localização de referência , especialmente durante um percurso pré-venda. No entanto, é caro para crescer e desperdiça espaço proporcional a 2 h - n para uma árvore de altura h com n Nós.

A árvore binária pode também ser representada na forma de matriz, bem como adjacência lista ligada. No caso da matriz, cada Nó (raiz, esquerda, direita) é simplesmente colocado no índice, e não há nenhuma conexão mencionado sobre a relação entre Pais e Filhos. Mas, em representação lista ligada, Pode-se encontrar a relação entre Pais e Filhos. Em representação de matriz os Nós são acessados por meio do cálculo do índice. Este método é usado em linguagens como FORTRAN, que não tem alocação dinâmica de memória. Nós não Pode-se inserir um novo Nó na árvore binária matriz implementada com facilidade, mas isso é facilmente feito quando se utiliza uma árvore binária implementada como lista ligada.

Page 21: Estrutura de Dados - Apostila I

Métodos de iteração sobre árvores binárias

Muitas vezes, uma vontade de visitar cada um de Nós em uma árvore e analisar o valor lá. Existem várias ordens comum, na qual os Nós podem ser visitados, e cada um tem propriedades úteis que são explorados em algoritmos baseados em árvores binárias.

Essas ordens são as seguintes:

Pre-Order: Root em primeiro lugar, as crianças após o pós-ordem: crianças em primeiro lugar, depois de raiz em ordem: à esquerda da criança, de raiz, o Filho da direita.

Codificação

A estrutura de dados sucinta é aquele que toma o mínimo possível de espaço absoluto, tal como estabelecido por informações teóricas limites inferiores. O número de diferentes árvores binárias em Nós n é C n, n º número da Catalunha (assumindo que vemos árvores com estrutura idêntica idênticas). Para n grande, isto é cerca de 4 n, portanto precisamos de pelo menos cerca de log 2 4 n = 2 n bits para codificá-lo. Uma árvore binária sucinta, pois, que ocupam apenas 2 bits por Nó.

Uma representação simples que atende a esse limite é de visitar os nodos da árvore em pré-venda, saída 1 "para um Nó interno e" 0 "para uma folha. [1] Se a árvore contém dados, Pode-se simplesmente armazená-lo em simultâneo uma matriz consecutivos preorder. Esta função realiza o seguinte:

função EncodeSuccinct (n Nó, bitstring estrutura, os dados array) ( se n = nil então acréscimo de 0 a estrutura; outro acréscimo de 1 a estrutura; anexar n.data de dados; EncodeSuccinct (n.left, estrutura de dados); EncodeSuccinct (n.right, estrutura de dados); )

A estrutura da cadeia só tem 2 n + 1 bits, no final, onde n é o número de (interno) Nós, Nós nem sequer temos a loja de seu comprimento. Para mostrar que nenhuma informação é perdida, Pode-se converter a saída de volta para a árvore original como este:

função DecodeSuccinct (estrutura bitstring, dados array) ( remover primeiro bit de estrutura e colocá-lo em b se b = 1, então criar um novo Nó n remover o primeiro elemento de dados e colocá-lo em n.data n.left = DecodeSuccinct estrutura (os dados) n.right = DecodeSuccinct estrutura (os dados) n retorno outro nil retorno )

Page 22: Estrutura de Dados - Apostila I

Mais sofisticadas representações sucinta permitir não somente o armazenamento compacto de árvores, mas até mesmo operações úteis sobre as árvores diretamente enquanto eles ainda estão na sua forma sucinta.

Codificação de árvores em geral como árvores binárias

Existe um-para-um mapeamento entre um general ordenou árvores e árvores binárias, que em particular é usado por Lisp para representar as árvores geral ordenou como árvores binárias. Para converter uma árvore geral ordenou a árvore binária, só precisamos para representar a árvore de maneira geral, criança-irmão esquerda. O resultado desta representação será automaticamente árvore binária, se visto de uma perspectiva diferente. Cada Nó N da árvore ordenada corresponde a um Nó N 'na árvore binária, deixou o Filho de N' é o Nó que corresponde ao primeiro Filho do N, eo direito da criança N 'é o Nó correspondente ao' s N próximo irmão --- isto é, o próximo Nó na ordem entre os Filhos do Pai de N. Esta representação de árvore binária de uma árvore de ordem geral, é por vezes também referida como uma criança Esquerda-direita da árvore binária irmão (árvore LCRS), ou uma árvore duplamente encadeadas , ou uma cadeia de Filial-Herdeiro .

Uma maneira de pensar sobre isso é que cada Nó crianças estão em uma lista ligada , encadeadas com os seus campos a direita eo Nó só tem um ponteiro para o início ou cabeça dessa lista, através do seu campo de esquerda.

Por exemplo, na árvore à esquerda, um tem os 6 Filhos (B, C, D, E, F, G). Ela pode ser convertida em árvore binária, à direita.

A árvore binária pode ser pensada como a árvore original inclinado lateralmente, com as bordas pretas à esquerda representa o primeiro Filho e as bordas direita azul representando próximo

irmão. As folhas da árvore à esquerda seria escrita em Lisp como:

(((NO) IJ) CD ((P) (Q)) F (M))

que seria aplicado na memória como a árvore binária à direita, sem qualquer correspondência sobre os Nós que tem um Filho esquerdo.

Page 23: Estrutura de Dados - Apostila I

Estruturas de Dados Árvores - Tree

S. Sudarshan

Baseado parcialmente em material de Fawzi & Chau-Wen Tseng Emad

• Árvore o Nodes o Cada nó pode ter 0 ou mais filhos o Um nó pode ter no máximo um pai

• Árvore binária o Árvore com 0-2 filhos por nó

Árvore

Binary Tree

Árvores

• Terminologia

o ⇒ Root nenhum pai

o Folha ⇒ nenhuma criança

o ⇒ Interior não-folha

o Altura ⇒ distância da raiz até a última folha

nó raiz

nós folha

nós Interior

Altura

Árvores binárias de pesquisa

• propriedade Key o Valor em nó

� Os menores valores em subárvore esquerda � Valores maiores de subárvore direita

Page 24: Estrutura de Dados - Apostila I

o Exemplo � X> Y � X <Z

Y

X

Z

Árvores binárias de pesquisa

• Exemplos

Árvores binárias de pesquisa

Não é uma árvore de busca binária

5

10

30

2

25

45

5

10

45

2

25

30

5

Page 25: Estrutura de Dados - Apostila I

10

30

2

25

45

Implementação árvore binária

Nó da classe (

dados int / / Pode ser int, uma classe, etc

Nó * esquerda, direita *; / null se vazio

void inserir (int dados) (...)

void excluir (int dados) (...)

Nó * find (dados int) (...)

...

)

Iterativo Pesquisa de árvore binária

Nó * Encontrar (n * Node, chave int) (

while (n! = NULL) (

if (n-> dados == chave) / / Achei

n retorno;

if (> dados n> chaves) / / Na subárvore esquerda

n = n-> esquerda;

mais / / Em subárvore direita

Page 26: Estrutura de Dados - Apostila I

n = n> direita;

)

return null;

)

Nó * n = Pesquisar (root, 5);

Procura recursiva de árvore binária

Nó * Encontrar (n * Node, chave int) (

if (n == NULL) / / não encontrado

return (n);

outro if (n-> dados == chave) / / Achei

return (n);

else if (> dados n> chaves) / / Na subárvore esquerda

Pesquisar retorno (> esquerda n, chave);

else / / Na subárvore direita

Pesquisar retorno (> direita n, chave);

)

Nó * n = Pesquisar (root, 5);

Exemplo binário procurados

• Pesquisar (raiz de 2)

5

10

30

2

Page 27: Estrutura de Dados - Apostila I

25

45

5

10

30

2

25

45

10> 2, à esquerda

5> 2, à esquerda

2 = 2, encontrado

5> 2, à esquerda

2 = 2, encontrado

raiz

Exemplo binário procurados

• Pesquisar (raiz, 25)

5

10

30

2

25

45

Page 28: Estrutura de Dados - Apostila I

5

10

30

2

25

45

10 <25, a direita

30> 25, deixou

25 = 25, encontrado

5 <25, direita

45> 25, deixou

30> 25, deixou

<10 25, à direita

25 = 25, encontrado

Tipos de árvores binárias

• Degenerate - apenas uma criança • Complete - sempre duas crianças • Balanced - "quase" dois filhos

o definições mais formal existe, acima são idéias intuitivas

árvore binária degenerada

Balanced árvore binária

árvore binária completa

Binary Propriedades Árvores

Page 29: Estrutura de Dados - Apostila I

• Degenerar o Height = O (n) para n nós o Semelhante a lista ligada

• Equilibrado o Height = O (log (n)) para nós n o Útil para pesquisas

árvore binária degenerada

Balanced árvore binária

Binary Propriedades Search

• Tempo de busca o Proporcional à altura da árvore o Balanced árvore binária

� O (log (n)) tempo o árvore degenerada

� O (n) o tempo � Como pesquisar lista ligada / array unsorted

Binary Search Tree Construção

• Como construir e manter árvores binárias? o Inserção o Eliminação

• Manter a propriedade de chave (invariantes) o Os menores valores em subárvore esquerda o Os maiores valores na subárvore direita

Árvore de busca binária - Inserção

• Algoritmo o Executar a busca para o valor X o Pesquisa vai terminar no nó Y (se X não na árvore)

Page 30: Estrutura de Dados - Apostila I

o Se X <Y, X inserir novas folhas como subárvore esquerda de novo para Y

o Se X> Y, inserir novas folhas X como nova subárvore direita de Y • Observações

o O (log (n)) para operação de árvore balanceada o Inserções árvore desequilíbrio pode

Exemplo de Inserção

• Insert (20)

5

10

30

2

25

45

10 <20, direita

30> 20, deixou

25> 20, deixou

Insira 20 no lado esquerdo

20

Árvore de busca binária - Supressão

• Algoritmo o Executar a busca para o valor X o Se X é uma folha, exclua X o Else / / deve excluir nó interno

Page 31: Estrutura de Dados - Apostila I

a) Substituir o maior valor de Y na subárvore esquerda

Ou menor valor de Z na subárvore direita

b) valor de reposição Delete (Y ou Z) de subárvore

• Observação o O (log (n)) para operação de árvore balanceada o Supressões árvore desequilíbrio pode

Exemplo de exclusão (Folha)

• Delete (25)

5

10

30

2

25

45

10 <25, a direita

30> 25, deixou

25 = 25, excluir

5

10

30

2

45

Page 32: Estrutura de Dados - Apostila I

Exemplo de exclusão (nó interno)

• Apagar (10)

5

10

30

2

25

45

5

5

30

2

25

45

2

5

30

2

25

45

Substituição de 10 com o maior valor na subárvore esquerda

Substituindo 5 com maior valor em subárvore esquerda

Page 33: Estrutura de Dados - Apostila I

Excluindo folha

Exemplo de exclusão (nó interno)

• Delete (10)

5

10

30

2

25

45

5

25

30

2

25

45

5

25

30

2

45

Substituição de 10 com o menor valor na subárvore direita

Excluindo folha

árvore resultante

Page 34: Estrutura de Dados - Apostila I

Balanced Trees Search

• Tipos de árvores de busca binária balanceada o altura equilibrado contra equilibrada de peso o "Rotações Tree", utilizado para manter o equilíbrio em inserir /

excluir • Non-Árvores binárias de pesquisa

o / 3 árvores de duas � cada nó interno tem 2 ou 3 filhos � todas as folhas na mesma profundidade (altura equilibrada)

o B-árvores � de 03/02 árvores Generalização � Cada nó interno tem entre k / 2 e k crianças

� Cada nó tem uma matriz de ponteiros para as crianças � Amplamente utilizado em bancos de dados

(Não) Procura-outras árvores

• Árvores sintáticas o Converter a partir de representação textual para a representação da

árvore o programa Textual em árvore

� Usados extensivamente em compiladores o Árvore de representação de dados

� HTML por exemplo, dados podem ser representados como uma árvore

� chamada DOM (Document Object Model) da árvore � XML

� Assim como o HTML, mas usada para representar dados � Tree estruturado

Page 35: Estrutura de Dados - Apostila I

Parse Trees

• Expressões, programas, etc pode ser representado por estruturas de árvore o Por exemplo, árvore de expressão aritmética o A-(C / 5 * 2) + (D * 5% 4)

+

-%

A * 4 *

/ 2 D 5

C 5

Tree Traversal

• Objetivo: visitar cada nó de uma árvore • em ordem de passagem

void Node: inorder () (

if (esquerda! = NULL) (

cout <<"(";> inorder (esquerda); cout <<")"; )

<<Dados cout <<endl;

if (direita! = NULL)> inorder (à direita)

)

Saída: A - C / 5 * 2 + D *% 5 4

Para disambiguate: suportes de impressão

+

Page 36: Estrutura de Dados - Apostila I

-%

A * 4 *

/ 2 D 5

C 5

Tree Traversal (cont.)

• pré-ordem e pós-ordem:

void Node: preorder () (

<<Dados cout <<endl;

if (esquerda! = NULL) deixou-> preorder ();

if (direita! = NULL)> preorder (direita);

)

void Node: postorder () (

if (esquerda! = NULL) deixou-> preorder ();

if (direita! = NULL)> preorder (direita);

<<Tribunal de dados <<endl;

)

Saída: + - * A / C 5 2% * D 5 4

Saída: A C 02/05 * - * D 5 + 4%

+

-%

A * 4 *

Page 37: Estrutura de Dados - Apostila I

/ 2 D 5

C 5

XML

• Representação de Dados o Por exemplo,

<dependency> <object> sample1.o </ object> <Sample1.cpp <depends> / depende> <depends> <sample1.h / depende> <Rule> g + + sample1.cpp <c-regra> / </> Dependência

o representação da árvore

dependência

objeto

depende

sample1.o

sample1.cpp

depende

sample1.h

regra

g + +-c ...

Page 38: Estrutura de Dados - Apostila I

Estruturas de Dados Gráfico

• Por exemplo: redes aéreas, redes rodoviárias, circuitos elétricos • Nós e arestas • Por exemplo: representação da classe Node

� Lojas de nome � armazena ponteiros para todos os nós adjacentes

� i, e. == Ponteiro borda � Para armazenar os ponteiros múltiplos: matriz ou lista

ligada utilização

Ahm'bad

Délhi

Mumbai

Calcutá

Chennai

Madurai

Fim do Capítulo

Page 39: Estrutura de Dados - Apostila I

Lista Estática Seqüencial Uma lista estática sequencial é um arranjo de registros onde estão estabelecidos regras de precedência entre seus elementos ou é uma coleção ordenada de componentes do mesmo tipo. O sucessor de um elemento ocupa posição física subsequente. Ex: lista telefônica, lista de alunos

A implementação de operações pode ser feita utilizando array e record, onde o vetor associa o elemento a(i) com o índice i (mapeamento sequencial).

Características de Lista Estática Sequencial

• elementos na lista estão ordenados; • armazenados fisicamente em posições consecutivas; • inserção de um elemento na posição a(i) causa o deslocamento a direita do

elemento de a(i) ao último; • eliminação do elemento a(i) requer o deslocamento à esquerda do a(i+1) ao

último;

Mas, absolutamente, uma lista estática sequencial ou é vazia ou pode ser escrita como ( a(1), a(2), a(3), ... a(n) ) onde a(i) são átomos de um mesmo conjunto S.

Além disso, a(1) é o primeiro elemento, a(i) precede a(i+1), e a(n) é o último elemento.

Assim as propriedades estruturadas da lista permitem responder a questões como:

• qual é o primeiro elemento da lista • qual é o último elemento da lista • quais elementos sucedem um determinado elemento • quantos elementos existem na lista • inserir um elemento na lista • eliminar um elemento da lista

Consequência: As quatros primeiras operações são feitas em tempo constante. Mas, as operações de inserção e remoção requererão mais cuidados.

Veja algumas operações básicas relacionadas com lista estática sequencial.

Vantagem:

• acesso direto indexado a qualquer elemento da lista • tempo constante para acessar o elemento i - dependerá somente do índice.

Page 40: Estrutura de Dados - Apostila I

Desvantagem:

• movimentação quando eliminado/inserido elemento • tamanho máximo pré-estimado

Quando usar:

• listas pequenas • inserção/remoção no fim da lista • tamanho máximo bem definido

Vamos tentar evitar as desvantagens anteriores e usar endereços não consecutivos (Lista Encadeada Estaticamente).

Referências

• Eric W. Weisstein "Subtree." De MathWorld-A Wolfram Web Resource. http://mathworld.wolfram.com/Subtree.html

• Donald Knuth . A Arte da Programação de Computadores: Algoritmos Fundamentais Third Edition. Addison-Wesley, 1997. ISBN 0-201-89683-4 . Seção 2.3: Árvores, pp. 308-423.

• Thomas H. Cormen , Charles E. Leiserson , Ronald L. Rivest e Clifford Stein . Introdução

aos Algoritmos Second Edition. MIT Press e-McGraw Hill, 2001. ISBN 0-262-03293-7 . Secção 10,4: Representando árvores enraizadas, pp. 214-217. Estruturas capítulos 12-14 (árvores de busca binária, árvores rubro-negras, aumentando de Dados), pp. 253-320.

• www.wikipedia.com.br Wikipédia, a enciclopédia livre