Triangulação de polígono simples com o algoritmo de Lee & Preparata

Documentos relacionados
Verificando corretude da triangulação

Métodos Computacionais. Listas Encadeadas

Árvores. Prof. César Melo DCC/ICE/UFAM

Estruturas de Dados. Módulo 17 - Busca. 2/6/2005 (c) Dept. Informática - PUC-Rio 1

Árvores. Prof. César Melo DCC/ICE/UFAM

Introdução a Programação. Listas Encadeadas

ÁRVORES E ÁRVORE BINÁRIA DE BUSCA

DAINF - Departamento de Informática

Listas Ligadas (Encadeadas) Listas Simplesmente Encadeadas

REVISÃO DE C. Vanessa Braganholo Estruturas de Dados e Seus Algoritmos

ÁRVORES ABB (ÁRVORES BINÁRIAS DE BUSCAS) Sérgio Carlos Portari Júnior

Algoritmos e Programação

Computadores Digitais 2. Prof. Rodrigo de Souza Couto

ÁRVORE BINÁRIA DE BUSCA

ÁRVORES BINÁRIAS DE BUSCA. Vanessa Braganholo Estruturas de Dados e Seus Algoritmos

INF 1620 P2-23/10/04 Questão 1 Nome:

Árvores Binárias de Busca

Estrutura de Dados. Carlos Eduardo Batista. Centro de Informática - UFPB

INF 1620 P3-25/11/05 Questão 1 Nome:

INF 1620 P4 11/12/06 Questão 1 Nome:

Lista de Exercícios sobre Listas Implementadas por Encadeamento

Árvores Binárias de Busca (ABB) 18/11

Árvore Binária de Busca. Prof. César Melo

A regra de acesso aos dados de uma fila é a seguinte: O primeiro elemento que entra (na fila) é o primeiro que sai (FIFO first in, first out).

Aluno: Valor Nota Q1 3.0 Q2 2.5 Q3 2.5 Q4 2.0 Total 10.0

SUMÁRIO. Fundamentos Árvores Binárias Árvores Binárias de Busca

Árvores Binária de Busca. Prof. César Melo DCC/ICE/UFAM

AED2 - Aulas 06 e 07 Árvores AVL e rubro-negras

Aula 14 Listas Duplamente Encadeadas. prof Leticia Winkler

Departamento de Informática - PUC-Rio INF 1007 Programação 2 P3 23/06/2010

Algoritmos e Estruturas de Dados Prof. Osório PIP/CA - Aula 05 Pag.: 1

CAP. IX - MANIPULAÇÃO DE ARQUIVOS Generalidades sobre Arquivos. 9.2 Abertura e Fechamento de Arquivos. Operações com arquivos:

Listas: a última das 3 estruturas lineares (Pilhas, Filas e Listas)... árvores e grafos são não lineares!

Árvores Binárias. SCC Algoritmos e Estruturas de Dados I. Prof. Fernando V. Paulovich

INF1007: Programação 2 8 Listas Encadeadas. (c) Dept. Informática - PUC-Rio 1

INF 1620 P4 30/06/07 Questão 1 Nome:

Árvores binárias de busca

Prof. Jesus José de Oliveira Neto

Algoritmos e Estruturas de dados

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

Lista de Exercícios 04

Árvores Binárias de Busca (ABB) 18/11

INF 1620 P3-21/06/08 Questão 1 Nome:

Árvores binárias de busca

Professora Jeane Melo

Árvores Estrutura de Dados. Universidade Federal de Juiz de Fora Departamento de Ciência da Computação

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

INF1010 Lista de Exercícios 2

ESTRUTURA DE DADOS DCC013. Árvore Binária de Busca

INF 1620 P3-29/06/04 Questão 1 Nome:

Árvores Binárias Balanceadas Estrutura de Dados I

Instituto de C. Linguagem C: Listas. Luis Martí Instituto de Computação Universidade Federal Fluminense -

Árvores & Árvores Binárias

Árvores. Fabio Gagliardi Cozman. PMR2300 Escola Politécnica da Universidade de São Paulo

INF 1010 Estruturas de Dados Avançadas. Árvores binárias

Departamento de Informática - PUC-Rio INF 1007 Programação 2 P4 07/12/2010

Estrutura de Dados Listas

Listas Lineares. continuando...

ESTRUTURA DE DADOS E ALGORITMOS. Árvores Binárias de Busca. Cristina Boeres

Lista Encadeada (Linked List)

INF 1620 P4-09/07/03 Questão 1 Nome:

INF 1620 P3-27/11/04 Questão 1 Nome:

Listas Lineares Ordenadas

Listas (Parte 1) Túlio Toffolo BCC202 Aula 09 Algoritmos e Estruturas de Dados I

Edital de Seleção 053/2016 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Módulo 10 Listas Encadeadas

Algoritmos e Estrutura de Dados II. Árvore. Prof a Karina Oliveira.

ÁRVORE B. Vanessa Braganholo Estruturas de Dados e Seus Algoritmos

Programação: Vetores

INF 1620 P4-01/07/08 Questão 1 Nome:

Listas ligadas/listas encadeadas

Listas Estáticas. SCC Algoritmos e Estruturas de Dados I. Prof. Fernando V. Paulovich. *Baseado no material do Prof.

Programação II. Listas Encadeadas (Linked Lists) Bruno Feijó Dept. de Informática, PUC-Rio

Árvores. SCC-202 Algoritmos e Estruturas de Dados I. Lucas Antiqueira

Edital de Seleção 024/2017 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

EPs 1 e 2. EP2: veja. EP1: veja

INF 1620 P4-27/06/02 Questão 1 Nome:

AED2 - Aula 04 Vetores ordenados e árvores de busca

Pesquisa em Memória Primária Árvores de Busca. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

INF 1620 P2-14/10/05 Questão 1 Nome:

Grafos - Representação

Árvores Binárias de Busca

Árvores Binárias de Busca

Introdução a Programação. Ponteiros e Strings, Alocação Dinâmica

Árvores de Pesquisa. A árvore de pesquisa é uma estrutura de dados muito eficiente para armazenar informação.

Listas - Outras. Listas Circulares Nós Cabeça Listas Duplamente Ligadas/Encadeadas Aplicações

Universidade Federal de Uberlândia Faculdade de Computação. Linguagem C: ponteiros e alocação dinâmica

Árvores Binárias de Busca

INF 1620 P3-06/12/03 Questão 1 Nome:

Alocação Dinâmica de Memória - Exercício

ÁRVORES E ÁRVORES BINÁRIAS. Adaptado de Alexandre P

Árvore Vermelho-Preta. Estrutura de Dados II Jairo Francisco de Souza

Aula 26: Arquivos de texto

Árvores Binárias. 9/11 e 11/11 Conceitos Representação e Implementação

Listas Encadeadas. David Menotti Algoritmos e Estruturas de Dados II DInf UFPR

1. Selecione a Estrutura de Dados que melhor representa os diretórios ou pastas de arquivos do computador.

Aula 3 Listas Lineares Sequenciais Ordenadas. prof Leticia Winkler

Reinaldo Gomes Alocação Dinâmica

Listas Lineares. Livro Projeto de Algoritmos Nívio Ziviani Capítulo 3 Seção 3.1

Transcrição:

Triangulação de polígono simples com o algoritmo de Lee & Preparata Tássio Naia dos Santos 20 de dezembro de 2011 às 10 h 33 Sumário 1. Objetivo....................................................... 1 2. Algoritmos e estruturas de dados......................................... 2 10. O Merge-Sort.................................................... 5 21. A varredura..................................................... 8 31. Árvore rubro-negra................................................. 11 33. Inserção....................................................... 12 45. Remoção...................................................... 15 64. A lista de arestas duplamente ligada (DCEL).................................. 20 1. Objetivo Desejamos triangular um polígono cujos vértices nos são fornecidos. Nosso objetivo aqui é apresentar o algoritmo de Lee e Preparata de complexidade O(n lg n). Entrada e saída do programa O programa lê da entrada as coordenadas dos vértices do polígono. Cada linha da entrada contém as coordenadas x e y de algum ponto do polígono, seguidas de uma quebra de linha ou do fim do arquivo. Os pontos devem ser apresentados em uma ordem representando um percurso no sentido anti-horário dos vértices do polígono. O programa imprime uma série de linhas, cada uma das quais representa uma diagonal do polígono triangulado. Cada linha é composta por quatro coordenadas x 1, y 1, x 2, e y 2, com um espaço entre coordenadas. A linha representa que existe uma diagonal ligando os pontos (x 1, y 1 ) e (x 2, y 2 ). ENTRADA 0 0 1 0 1 1.1 0 1 SAÍDA 0 1 1 0 1

2. Algoritmos e estruturas de dados Pontos Vamos armazenar as coordenadas dos pontos em dois vetores, X e Y. O número de pontos é n. Depois de armazenados, vamos nos referir a um ponto por sua posição no vetor, de modo que o ponto i tem coordenadas (X[i], Y [i]). Triangulação O algoritmo de Lee e Preparata para triangular polígnos simples é um algoritmo de linha de varredura 1, que percorre os pontos em ordem, digamos, decrescente de y-coordenada, adicionando diagonais que o particionam em polígonos y-monótonos. Cada uma das partes é então triangulada. Representamos o polígono particionado por meio de uma lista duplamente ligada de arestas (dcel doublyconnected edge list), também chamada de estrutura de meia-aresta (half-edge) ou de arestas gêmeas (twin-edge. Essa estrutura que descreveremos com mais detalhe adiante é composta por três tipos de objetos: vértices, arestas e faces. Os vértices são exatamente os vértices da subdivisão do plano que resulta do particionamento do polígono em componentes monótonas. Faces são as regiões da partição do plano (uma delas é infinita ). Meias-arestas gêmeas representam as duas orientações possíveis de uma aresta. Cada uma das meias-arestas está associada a um vértice origem, à sua aresta-gêmea (a outra orientação possível da aresta), à face que delimita em sentido anti-horário, e à próxima meia-aresta encontrada percorrendo essa face nesse sentido. Linha de varredura A descrição combinatória da linha de varredura contém um conjunto ordenado, que é atualizado à medida que o vértices são encontrados. Usamos uma árvore rubro-negra inclinada à esquerda para manter esse conjunto. Ordenação Por fim, para que possamos percorrer os vértices em ordem decrescente de y-coordenada, vamos ordená-los usando o merge-sort. Complexidade O particionamento é feito em O(n lg n), e a triangulação de cada parte é feita em tempo linear paa cada partição. Como cada partição possui ao menos um vértice que não está em nenhuma outra partição, e, como a quantidade de vértices que pertence simultaneamente a cada partição é liitada (a 2), a triangulação do polígono particionado é O(n). Testes da implementação Escrevemos um curto programa que une a descrição do polígo e a saída deste programa, gerando um arquivo pdf com o desenho do polígono antes e depois da triangulação, para facilitar a identificação de erros no programa. Criamos uma ferramenta que verifica a corretude da saída do programa, retornando Correto se a saída do programa de fato é uma triangulação válida para sua entrada, e Incorreto caso contrário. Cabeçalho 6 Pré-declaração de funções 12 Funções 13 Rotina principal 3 3. Rotina principal 3 int main (int argc, char argv ) Variáveis locais de main 4 Leitura do polígono 7 Ordenação dos pontos 10 Varredura dos pontos (partição do polígono e construção da dcel) 21 Triangulação das partições 67 Impressão das diagonais 68 Liberação das estruturas usadas 69 return 0; Código utilizado no trecho 2. 4. Como não sabemos a princípio quantos pontos o polígono tem, iniciamos usando um vetor estaticamente alocado de pontos, e passamos para alocação dinâmica quando ele é totalmente usado. Seu tamanho é tamanho buffer, e o o tamanho estático é tamanho estatico. 1 O algoritmo constoi uma trapezoidação do polígono também chamada mapa de visibilidade horizontal. 2

#define tamanho estatico 1000 / Precisa ser ao menos 1. / Variáveis locais de main 4 double X estatico[tamanho estatico], Y estatico[tamanho estatico], X = X estatico, Y = Y estatico; int tamanho buffer = tamanho estatico; Veja também os trechoss 8, 11 e 22. Código utilizado no trecho 3. 5. Para aumentar o tamanho do buffer de coordenadas usamos a macro aumenta tamanho xy ( ). Se o buffer é o vetor estático, precisamos alocar memória e depois copiar os dados; caso contrário simplesmente realocamos a região da memória apontada por X e Y. A liberação de memória restaura a situação inicial (memória alocada estaticamente). #define aumenta tamanho xy () while (0) #define libera xy () do if (tamanho buffer tamanho estatico) tamanho buffer = 2; X = realloc(x, sizeof (double) tamanho buffer ); Y = realloc(y, sizeof (double) tamanho buffer ); else X = malloc(sizeof (double) tamanho buffer 2); Y = malloc(sizeof (double) tamanho buffer 2); memcpy (X, X estatico, sizeof (double) tamanho buffer ); memcpy (Y, Y estatico, sizeof (double) tamanho buffer ); tamanho buffer = 2; do if (tamanho buffer tamanho estatico) free (X); free (Y ); X = X estatico; Y = Y estatico; tamanho buffer = tamanho estatico; while (0) 6. Cabeçalho 6 #include <string.h> / Para memcpy ( ). / Veja também os trechoss 9, 17 e 24. Código utilizado no trecho 2. 7. Mantemos o número de pontos armazenado em uma variável num pontos lidos. Leitura do polígono 7 if (argc 2) fprintf (stderr, "! uso: %s <arquivo descrevendo polígono>.\n", argv [0]); exit (1); if ((entrada = fopen (argv [1], "r")) Λ) fprintf (stderr, "! N~ao consegui abrir o arquivo %s.\n", argv [1]); exit (1); while (fscanf (entrada, "%lf %lf", &X[num pontos lidos ], &Y [num pontos lidos ]) 2) 3

++num pontos lidos ; if (num pontos lidos tamanho buffer ) aumenta tamanho xy ( ); printf ("Li %d pontos.\n", num pontos lidos ); fclose (entrada ); entrada = Λ; Código utilizado no trecho 3. 8. Variáveis locais de main 4 + FILE entrada = Λ; int num pontos lidos = 0; 9. Cabeçalho 6 + #include <stdio.h> #include <stdlib.h> 4

10. O Merge-Sort Escolhemos o Merge-Sort para fazer a ordenação dos pontos. Como a ordem em que os pontos nos foram dados na entrada é importante, queremos que a ordenação seja indireta retorne um vetor a que contenha os pontos ordenados da seguinte forma: a[0] indica que o ponto (X[a[0]], Y [a[0]]) é o menor da coleção, a[1] é o segundo menor ponto, etc. #define eps 1 10 7 Ordenação dos pontos 10 a = merge sort (X, Y, num pontos lidos ); Código utilizado no trecho 3. 11. Variáveis locais de main 4 + int a = Λ; 12. Pré-declaração de funções 12 int merge sort (double X[ ], double Y [ ], int N); int merge sort rec(double X[ ], double Y [ ], int buffer, int p, int r, int qual buffer ); Veja também os trechoss 30, 42, 44 e 62. Código utilizado no trecho 2. 13. Introduzimos aqui uma pequena alteração no merge sort que fazemos na esperança de diminuir a memória alocada pelo algoritmo. Usaremos dois buffers para armazenar a ordenação indireta, e alternaremos entre um e outro, à medida que a ordenação prossegue. Funções 13 int merge sort (double X[ ], double Y [ ], int N) int buffer [2] = Λ, Λ, i, buffer da resposta ; buffer [0] = malloc(n sizeof (int)); buffer [1] = malloc(n sizeof (int)); for (i = 0; i < N; ++i) buffer [0][i] = i; buffer da resposta = merge sort rec(x, Y, buffer, 0, N 1, 0); if (buffer da resposta 0) free (buffer [1]); return buffer [0]; else free (buffer [0]); return buffer [1]; Veja também os trechoss 14, 25, 28, 29, 34, 40, 41, 43, 46, 47, 51, 55 e 60. Código utilizado no trecho 2. 14. Cada chamada do merge sort recebe um intervalo de índices sob o qual operar e o buffer em que os valores dos pontos podem ser encontrados. A função retorna o buffer em que os pontos do intervalo estão após a ordenação. Com esse procedimento, o vetor auxiliar não precisa ser recopiado sobre o vetor original uma vez que a intercalação está feita. Usamos o seguinte expediente: se buffer é o íncide de um buffer, assumindo valores 0 e 1, então 1 buffer se refere ao outro buffer. Por fim, usamos as variáveis buffer esquerda e buffer direita para saber onde estão os pontos ordenados pelas chamadas recursivas. Funções 13 + int merge sort rec(double X[ ], double Y [ ], int a, int p, int r, int buffer ) int q, buffer esquerda, buffer direita ; 5

Outras variáveis locais do merge sort rec 20 if (r < p + 2) Resolve caso base do merge sort rec 15 else q = (p + r)/2; buffer esquerda = merge sort rec(x, Y, a, p, q, buffer ); buffer direita = merge sort rec(x, Y, a, q + 1, r, buffer ); Intercala para merge sort rec 18 return buffer final ; 15. Resolve caso base do merge sort rec 15 if (r < p + 1) return buffer ; / r p / else if (Y [a[buffer ][p]] + eps < Y [a[buffer ][r]] Iguais, mas a x-coordenada de a[buffer ][p] é menor 16 ) return buffer ; / r p + 1 e buffer já ordenado. / else / Troca elementos de posição. / a[1 buffer ][p] = a[buffer ][r]; a[1 buffer ][r] = a[buffer ][p]; return 1 buffer ; Código utilizado no trecho 14. 16. Iguais, mas a x-coordenada de a[buffer ][p] é menor 16 (fabs (Y [a[buffer ][p]] Y [a[buffer ][r]]) < eps X[a[buffer ][p]] + eps < X[a[buffer ][r]]) Código utilizado no trecho 15. 17. Cabeçalho 6 + #include <math.h> / Para fabs ( ). / 18. Intercala para merge sort rec 18 buffer final = 1 buffer esquerda ; i = p; j = q + 1; k = p; while (i q j r) if (Y [a[buffer esquerda ][i]] + eps < Y [a[buffer direita ][j]] Iguais, mas ponto no buffer esquerda tem x-coordenada menor 19 ) a[buffer final ][k] = a[buffer esquerda ][i]; ++i; ++k; else a[buffer final ][k] = a[buffer direita ][j]; ++j; ++k; while (i q) a[buffer final ][k] = a[buffer esquerda ][i]; ++i; ++k; 6

while (j r) a[buffer final ][k] = a[buffer direita ][j]; ++j; ++k; Código utilizado no trecho 14. 19. Iguais, mas ponto no buffer esquerda tem x-coordenada menor 19 (fabs (Y [a[buffer esquerda ][i]] Y [a[buffer direita ][j]]) < eps X[a[buffer esquerda ][i]] + eps < X[a[buffer direita ][j]]) Código utilizado no trecho 18. 20. Outras variáveis locais do merge sort rec 20 int i, j, k, buffer final ; Código utilizado no trecho 14. 7

21. A varredura O laço da varredura manipula três estruturas de dados. O vetor com a ordenação dos pontos, do qual lê o ponto seguinte a ser processado, uma árvore, em que guarda a representação combinatória da linha, e também a dcel, que constrói pouco a pouco. Varredura dos pontos (partição do polígono e construção da dcel) 21 Inicializa dcel com as arestas do polígono 64 Inicializa a descrição combinatória da linha 65 i = 0; while (i < num pontos lidos ) Processa ponto i 66 ++i; Código utilizado no trecho 3. 22. Variáveis locais de main 4 + int i; 23. Descrevemos agora com um pouco mais de detalhe o cerne da partição do polígono em polígonos monótonos. A descrição combinatória da linha contém os trapézios que a intersectam, armazenados de modo a que quando um ponto novo é processado seja rápido identificar a que trapézio ele pertence. Aqui fazemos isso criando um modo de comparar trapézios, e armazenando os trapézios em uma árvore rubronegra inclinada à esquerda (arnie). Incluir descrição dos trapézios As próximas seções tratam das estruturas de dados e das primitivas para a monipulação da arnie. 24. Definimos agora o tipo t arnie das árvores. Cada nó da árvore possui cinco campos. Para cada um desses campos a árvore possui um vetor, que armazena os valores desses campos para todos os nós da árvore. Os campos são pai, filho menor, filho maior, cor e info. Os três primeiros dizem respeito às ligações entre nós na árvore, cor é rubro ou negro, e info é o inteiro armazenado na árvore. assim, um determinado nó ni está associado aos campos pai [ni ], cor [ni ], etc. Por fim, como a árvore pode sofrer expansões, armazenamos seu tamanho, assim como alguns ponteiros para gerenciamento de memória, que discutiremos oportunamente. Ademais, como os apontadores de uma arnie são números inteiros, definimos NIL, o valor de um. #define NIL 1 #define rubro 0 #define negro 1 #define eh rubro(a, p) ((p) NIL (A) cor [p] negro) #define eh negro(a, p) ((p) NIL (A) cor [p] rubro) / Equivale a eh rubro(a, p). / format t arnie int / Tipo para arnies. / Cabeçalho 6 + typedef struct int pai ; int filho menor ; int filho maior ; short cor ; int info; Variáveis para gerenciar memória na arnie 26 int raiz ; t arnie; 8

25. Gerenciamos o espaço livre com o auxílio das variáveis tamanho e proxima posicao livre. A primeira é o número de posições que existem em cada um dos vetores apontados por ponto, pai, etc., enquanto a segunda aponta para o início de uma lista ligada de posições livres, em que os elementos estão ligados pelo seu campo pai, como veremos. A variável proxima posicao livre é mantida sempre < tamanho arvore, e a posição com esse índice está livre (em todos os vetores). Além disso, ou pai [proxima posicao livre ] NIL, ou pai [proxima posicao livre ], aponta para uma posição livre (em todos os vetores), de modo que atribuir proxima posicao livre = pai [proxima posicao livre ] mantém as propriedades da variável. O pedido de um no livre (t arnie A) retorna o índice de uma posição ainda não usada nos vetores, e avança o apontador para uma posição livre. Se esse avanço invalida as propriedades do apontador (i.e., se com isso proxima posicao livre tamanho), fazemos uma chamada para aumenta tamanho arvore (t arnie A), que dobra o espaço disponível. O primeiro aumento é algo diferente dos demais, já que passamos da alocação estática para um regime de alocação dinâmica, e o conteúdo dos vetores estáticos é copiado para os vetores alocados. Temos ainda inicializa arvore ( ) e libera arvore ( ), com as funções óbvias de inicialização e liberação da estrutura de dados. Com isso podemos escrever o código que gerencia a alocação e liberação de memória. Funções 13 + void inicializa arvore (t arnie A, int tamanho inicial ) A proxima posicao livre = 0; A pai [A proxima posicao livre ] = NIL; A raiz = NIL; if (tamanho inicial > 0) A info = malloc(sizeof (int) tamanho inicial ); A pai = malloc(sizeof (int) tamanho inicial ); A filho menor = malloc(sizeof (int) tamanho inicial ); A filho maior = malloc(sizeof (int) tamanho inicial ); A cor = malloc(sizeof (short) tamanho inicial ); A tamanho = tamanho inicial ; void aumenta tamanho arvore (t arnie A) A tamanho = 2; A info = realloc(a info, sizeof (int) (A tamanho)); A pai = realloc(a pai, sizeof (int) (A tamanho)); A filho menor = realloc(a filho menor, sizeof (int) (A tamanho)); A filho maior = realloc(a filho maior, sizeof (int) (A tamanho)); A cor = realloc(a cor, sizeof (short) (A tamanho)); void libera arvore (t arnie A) if (A tamanho > 0) / Memória foi alocada. / free (A info); free (A pai ); free (A filho maior ); free (A filho menor ); free (A cor ); A tamanho = 0; A info = Λ; A pai = Λ; A filho menor = Λ; A filho maior = Λ; A cor = Λ; 9

26. Variáveis para gerenciar memória na arnie 26 int tamanho, proxima posicao livre ; Código utilizado no trecho 24. 27. Libera memória alocada 27 void libera arvore (t arnie A); void aumenta tamanho arvore (t arnie A); void inicializa arvore (t arnie A, int tamanho inicial ); 28. Obtendo nó livre, e liberando nó. Funções 13 + void libera no(t arnie A, int ni ) A pai [ni ] = A proxima posicao livre ; A proxima posicao livre = ni ; 29. Funções 13 + int no livre (t arnie A) int resposta ; resposta = A proxima posicao livre ; if (A pai [A proxima posicao livre ] NIL) ++(A proxima posicao livre ); if (A proxima posicao livre A tamanho) aumenta tamanho arvore (A); A pai [A proxima posicao livre ] = NIL; else A proxima posicao livre = A pai [A proxima posicao livre ]; return resposta ; 30. Pré-declaração de funções 12 + void libera no(t arnie A, int ni ); int no livre (t arnie A); 10

31. Árvore rubro-negra Uma implementação de árvore rubro-negra inclinada para a esquerda. Garantimos que todo nó é rubro ou negro, toda folha (NIL) é negra, os filhos de um nó rubro são negros, nenhum filho direito é rubro, e, por fim, o caminho de um nó até qualquer de suas folhas tem o mesmo número de nós negros. Para compreender melhor esse tipo de árvore, discutiremos um pouco suas propriedades. Quando percorremos um caminho a partir da raiz da árvore (embora isso seja válido para qualquer nó, de modo geral) até alguma das folhas da árvore, as restrições da árvore mencionadas acima implicam que nunca passaremos por dois nós vermelhos em seguida. Como o número de nós negros pelos quais passamos é constante, não importa em qual folha terminamos, o caminho mais longo até alguma folha é no máximo duas vezes mais longo que o caminho mais curto. Para entender o balanceamento de árvores rubro-negras, vamos virar o problema e observar outros tipos de árvores, a árvore 2 3 e árvore 2 3 4. Veremos como esse tipo de árvore pode ser mantido perfeitamente balanceado, e que árvores rubro-negras (em particular árvores rubro-negras inclinadas, digamos, à esquerda) possuem uma correspondência com essas estruturas. Essa correspondência pode ser mantida sem grandes penas, e o resultado é uma árvore com certas garantias de balanceamento e, consequentemente, no tempo que leva para buscar, inserir e remover elementos seus. Esses tipos de árvore são generalizações de árvores binárias. Em uma árvore 2 3, os nós podem ter dois ou três filhos, enquanto em uma árvore 2 3 4 podem ter dois, três ou quatro filhos. Esses nós são chamados 2-nós, 3-nós ou 4-nós, dependendo da quantidade de filhos que têm. Um nó com k filhos têm k 1 chaves. Por exemplo, um 3-nó p = (a, b) com chaves a e b (a < b) aponta para três filhos. Um deles é raiz de uma sub-árvore em que todas as chaves têm valor < a; os nós de outra das sub-árvores têm chaves com valores entre a e b. A terceira sub-árvore, não é surpresa, tem nós com chaves de valor maior do que b. Árvores 2 3 4 são definidas de modo análogo. Acontece que é possível manter essas árvores completamente balanceadas (qualquer caminho da raiz até alguma folha passa pelo mesmo número de nós). Por enquanto, nos limitamos a descrever como é o mapeamento (um para um!) desse tipo de árvores às árvores rubro-negras inclinadas para a esquerda (arnie). Desenvolvemos o ferramental para manutenção da estrutura (preservando o mapeamento!) aos poucos, nas próximas seções. Ao fim deste último mergulho, voltaremos quiçá um pouco mais sábios, e podemos cuidar das mariposas (as que ainda não houverem fugido). O mapeamento é o seguinte: cada 2-nó corresponde a um nó negro na arnie; um 3-nó está associado a um nó negro com um filho menor rubro; um 3 nó equivale a um nó negro com dois filhos rubros. As folhas e raiz da árvore são negras. 32. Buscamos na arnie de modo análogo ao que faríamos em uma árvore binária. Se a chave buscada não é a do nó em que estamos, identificamos a qual intervalo definido pelas chaves do nó a chave pertence, e seguimos a busca recursivamente pela sub-árvore associada a esse intervalo. 11

33. Inserção A inserção em árvores 2 3 4 ocorre na última folha encontrada em pela busca. Se o nó é um 2-nó ou um 3-nó a inserção pode ser feita sem alterar o balanceamento da árvore. Para evitar que a folha seja um 4-nó, podemos transformar (leia-se dividir) os 4-nós que encontramos pelo caminho. Se a raiz for um 4-nó, podemos transformá-la em três 2-nós ( subindo sua chave do meio). Ou seja, a busca sempre parte de um nó com menos de três chaves. Se, em algum passo intermediário, vamos passar de um 2-nó ou de um 3-nó para um 4-nó, podemos subir uma chave desse 4-nó para o nó em que estamos, antes de avançar para a sub-árvore do (ex-4)-nó. Assim, garantimos que nunca estamos em um 4-nó, e a inserção é simples. Acontece que não precisamos fazer isso! Ao menos não quando descemos pela árvore. Numa etapa final, após a inserção, podemos retornar pelo caminho percorrido, consertando os problemas que criamos... desde que consigamos assegurar que qualquer problema criado de fato fica no caminho de volta. O que chamamos acima de subir uma chave nada mais é do que dividir um nó que tem mais de uma chave. As ferramentas que nos ajudam a fazer isso em nossa arnie são rotações e trocas de cor. Não descreveremos aqui o que é uma rotação, mas vale comentar que tanto rotações quanto trocas de cor podem destruir as propriedades da arnie pelas quais tanto zelamos. Podemos consertar as coisas, contudo, se depois de inserido o nó (ou removido, já que a ideia é parecida) retrocedermos geração por geração na árvore, seguindo os ponteiros pai dos nós. Esse trabalho é feito por Conserta o que foi bagunçado 38. 34. A inserção começa com uma busca pelo ponto de inserção. Essa busca continua até que os limites da árvore. Nesse ponto, no atual aponta para o último vértice visitado antes de uma folha da árvore ter sido atingida, e proximo no aponta para um nó (já alocado) em que só falta acrescentar o ponto as folhas são sempre NIL. #define verdade 1 Funções 13 + void insere (t arnie A, int info, int( compara )(int, int)) Variáveis locais de insere 39 if (A raiz NIL) / Árvore não está vazia. / no atual = A raiz ; while (verdade ) Segue a busca, avançando no atual. Prepara a inserção em uma folha de no atual e escapa do loop 35 Insere info na folha 37 Conserta o que foi bagunçado 38 else Insere info na árvore vazia 36 35. Segue a busca, avançando no atual. Prepara a inserção em uma folha de no atual e escapa do loop 35 if (compara (info, A info[no atual ]) < 0) printf ("inserç~ao: (%d) < (%d)\n", info, A info[no atual ]); if (A filho menor [no atual ] NIL) A filho menor [no atual ] = no livre (A); proximo no = A filho menor [no atual ]; break; no atual = A filho menor [no atual ]; else printf ("inserç~ao: (%d) > (%d)\n", info, A info[no atual ]); if (A filho maior [no atual ] NIL) A filho maior [no atual ] = no livre (A); proximo no = A filho maior [no atual ]; break; no atual = A filho maior [no atual ]; Código utilizado no trecho 34. 12

36. Insere info na árvore vazia 36 A raiz = no livre (A); A info[a raiz ] = info; A cor [A raiz ] = negro; A filho menor [A raiz ] = A filho maior [A raiz ] = NIL; A pai [A raiz ] = NIL; Código utilizado no trecho 34. 37. Insere info na folha 37 A info[proximo no] = info; A cor [proximo no] = rubro; A filho menor [proximo no] = A filho maior [proximo no] = NIL; A pai [proximo no] = no atual ; Código utilizado no trecho 34. 38. Subimos pela árvore, ancestral por ancestral, consertando as infrações às regras da arnie que foram deixadas em nosso caminho. Dá algum trabalho mostrar que todo estrago que pode ter sido feito vai ser encontrado nesse processo... (quem sabe façamos isso um dia?) Mas ao menos vamos comentar os três tipos de infração. Um tipo de infração é um nó com filho direito vermelho. Esse caso resolvemos com uma rotação na direção do filho menor. Outro tipo é a ocorrência de dois filho menor vermelhos em seguida. Isso resolvemos com uma rotação na direção do filho maior do primeiro ancestral comum 2. Finalmente, se ambos os filhos de um nó são vermelhos, trocamos as cores dos filhos e do nó (isso é necessário para que que possamos avançar para o ancestral sem medo de algum infração de sete cabeças). Conserta o que foi bagunçado 38 proximo no = no atual ; while (proximo no NIL) no atual = proximo no; if (eh rubro(a, A filho maior [no atual ])) no atual = rotaciona menor (A, no atual ); if (eh rubro(a, A filho menor [no atual ]) eh rubro(a, A filho menor [A filho menor [no atual ]])) no atual = rotaciona maior (A, no atual ); if (eh rubro(a, A filho menor [no atual ]) eh rubro(a, A filho maior [no atual ])) troca cores (A, no atual ); proximo no = A pai [no atual ]; A raiz = no atual ; A cor [A raiz ] = negro; printf ("[fim do conserto]"); fflush (stdin ); Código citado no trecho 33. Código utilizado nos trechos 34, 47, 51 e 58. 39. Variáveis locais de insere 39 int no atual, proximo no; Código utilizado no trecho 34. 40. A função rotaciona menor ( ) assume que A filho maior [p] NIL e A cor [A filho maior [p]] rubro, e rotaciona maior ( ) assume que A filho menor [p] NIL e A cor [A filho menor [p]] rubro. Funções 13 + int rotaciona menor (t arnie A, int p) int q; 2 Note que isso significa que o concerto não pode iniciar com o pai de uma folha, não haveria níveis o suficiente para esse teste sendo assim, se houvesse algo que consertar para o pai da folha, isso tem que ser feito antes deste trecho. 13

q = A filho maior [p]; A filho maior [p] = A filho menor [q]; A filho menor [q] = p; A pai [q] = A pai [p]; A pai [A filho maior [p]] = p; A pai [p] = q; A cor [q] = A cor [p]; A cor [p] = rubro; return q; 41. Funções 13 + int rotaciona maior (t arnie A, int p) int q; q = A filho menor [p]; A filho menor [p] = A filho maior [q]; A filho maior [q] = p; A pai [q] = A pai [p]; A pai [A filho menor [p]] = p; A pai [p] = q; A cor [q] = A cor [p]; A cor [p] = rubro; return q; 42. Pré-declaração de funções 12 + int rotaciona menor (t arnie A, int p); int rotaciona maior (t arnie A, int p); 43. Ajuste de cores. Se o nó p é interno, podemos usar: #define outra cor (c) (1 c) Funções 13 + void troca cores (t arnie A, int ni ) A cor [ni ] = outra cor (A cor [ni ]); A cor [A filho menor [ni ]] = outra cor (A cor [A filho menor [ni ]]); A cor [A filho maior [ni ]] = outra cor (A cor [A filho maior [ni ]]); 44. Pré-declaração de funções 12 + void troca cores (t arnie A, int ni ); 14

45. Remoção Tornamos agora para a retirada de um nó da árvore. Vamos usar uma combinação de duas ideias. Primeiro, e analogamente ao que acontece na inserção, notamos que existem nós cuja remoção é simples de fazer. Aqui, estamos falando dos nós que contém a maior e a menor chaves de alguma sub-árvore, desde que esse nó não seja uma 2-nó. Buscaremos um meio de garantir que o caso indesejado não aconteça, transformando a árvore à medida que buscamos o nó que vamos remover (como na inserção, isso invalida algumas propriedades da arnie, que conseguimos consertar exatamente como fizemos antes). A segunda ideia é remover um nó qualquer usando, por exemplo, a remoção do menor elemento de sua sub-árvore filho maior. Para remover o nó de maior chave, passamos de filho maior para filho maior, enquanto pudermos. Idealmente, quando não pudermos mais prosseguir, estaremos em um 3-nó ou 4-nó, e a remoção do nó não estraga a árvore! Trata-se então de garantir que nesse percurso o no atual nunca é um 2-nó. Podemos fazer isso combinando nósirmãos (caso tanto o nó para o qual se quer seguir descendo como seu irmão sejam 2-nós) ou, se o irmão do nó tem mais do que uma chave, tomando emprestado dele uma das chaves. Como antes, usaremos as rotações e trocas de cores para obter as transformações equivalentes da arnie, de modo que qualquer transgressão de suas propriedades fique na linha de ancestrais do nó por onde continuamos procurando assim podemos consertar o estrago na volta. O procedimento é análogo para a remoção do nó de menor chave, mas em geral precisamos bagunçar menos as coisas, já que, por definição da arnie, os 3-nós e 4-nós são inclinados no sentido da sub-árvore das chaves menores. Uma última palavra sobre o significado de carregarmos 3-nós e 4-nós à medida que prosseguimos. O invariante que mantemos é que ou o no atual é rubro, ou o seu filho por onde pretendemos prosseguir é rubro ( ). O único caso em que não conseguimos carregar esses nós ocorre quando a árvore só tem um nó: a raiz. Esse caso podemos tratar à parte sem problemas. 46. Usando rotações e trocas de cores, vamos construir agora duas novas primitivas: move rubro para menor (no) e move rubro para maior (no). São análogas, então vamos discutir apenas a segunda. A ideia é assegurar o invariante ( ) para o filho maior (em suma, estamos em um 3-nó ou 4-nó). Partimos do pressuposto que no é rubro, pois se seu filho maior é rubro, esse método não terá sido chamado. Trocamos as cores de no e de seus filhos com troca cores (no). Mas isso pode ter causado uma violação das propriedades da arnie no filho menor do nó. Isso acontece se filho menor [filho menor [no]] é vermelho também. Nesse caso, eliminamos a transgressão com uma rotação e ainda outra troca de cores. Funções 13 + int move rubro para maior (t arnie A, int ni ) troca cores (A, ni ); if (eh rubro(a, A filho menor [A filho menor [ni ]])) ni = rotaciona maior (A, ni ); troca cores (A, ni ); return ni ; int move rubro para menor (t arnie A, int ni ) troca cores (A, ni ); if (eh rubro(a, A filho menor [A filho maior [ni ]])) A filho maior [ni ] = rotaciona maior (A, A filho maior [ni ]); ni = rotaciona menor (A, ni ); troca cores (A, ni ); return ni ; 47. Conseguimos agora remover o máximo! Se o filho menor do nó é rubro, movemos a ligação rubra com uma rotação no sentido do filho maior e então podemos prosseguir para esse nó mantendo o invariante ( ). Se tanto o próximo nó na busca filho maior [no] quanto seus filhos 3 são negros, então movemos a ligação rubra do nó atual para o seu filho maior usando a primitiva que acabamos de criar. Por fim, quando encontramos o máximo, podemos removê-lo sem maiores pesares, passando depois disso à tarefa de normalizar os ancestrais na arnie. 3 Na verdade só os filhos menores de nó e de seu filho maior precisam ser testados, graças à inclinação da arnie. 15

Funções 13 + void remove maximo de subarvore (t arnie A, int ni ) Variáveis locais de remove maximo de subarvore 50 if (ni NIL) / Sub-árvore não está vazia. / no atual = ni ; while (verdade ) Segue a busca pelo máximo, garantindo nunca estar em um 2-nó. Sai do loop garantindo proximo no filho maior [no atual ] é o máximo 48 Remove o máximo (proximo no) 49 Conserta o que foi bagunçado 38 48. Segue a busca pelo máximo, garantindo nunca estar em um 2-nó. Sai do loop garantindo proximo no filho maior [no atual ] é o máximo 48 if (eh rubro(a, A filho menor [no atual ])) no atual = rotaciona maior (A, no atual ); if (A filho maior [no atual ] NIL) / Encontramos o nó com a maior chave! / proximo no = no atual ; no atual = A pai [no atual ]; break; if (eh negro(a, A filho maior [ni ]) eh negro(a, A filho menor [A filho maior [no atual ]])) / REVER: na condição é ni ou proximo no? / no atual = move rubro para maior (A, no atual ); no atual = A filho maior [no atual ]; Código utilizado no trecho 47. 49. Testamos se no atual (que é o pai do nó a ser removido) é NIL. Isso acontece quando a árvore da qual queremos remover o máximo tem apenas um nó. Remove o máximo (proximo no) 49 if (no atual NIL) A filho maior [no atual ] = NIL; libera no(a, proximo no); else A raiz = NIL; Código citado no trecho 53. Código utilizado no trecho 47. 50. Variáveis locais de remove maximo de subarvore 50 int no atual, proximo no; Código utilizado no trecho 47. 51. Vamos cuidar do mínimo agora. A ideia básica é a mesma: garantir que não estamos em um 2-nó à medida que a busca progride, remover o menor nó, arrumar a casa depois que a farra termina. Funções 13 + void remove minimo de subarvore (t arnie A, int ni ) Variáveis locais de remove minimo de subarvore 54 if (ni NIL) / Sub-árvore não está vazia. / no atual = ni ; while (verdade ) Segue a busca pelo mínimo, garantindo nunca estar em um 2-nó. Sai do loop garantindo proximo no filho menor [no atual ] é o mínimo 52 16

Remove o mínimo (proximo no) 53 Conserta o que foi bagunçado 38 52. Segue a busca pelo mínimo, garantindo nunca estar em um 2-nó. Sai do loop garantindo proximo no filho menor [no atual ] é o mínimo 52 if (A filho menor [no atual ] NIL) / Encontramos o nó com a menor chave! / proximo no = no atual ; no atual = A pai [no atual ]; break; if (eh negro(a, A filho menor [ni ]) eh negro(a, A filho menor [A filho menor [no atual ]])) no atual = move rubro para menor (A, no atual ); no atual = A filho menor [no atual ]; Código utilizado no trecho 51. 53. Assim como em Remove o máximo (proximo no) 49, o teste é necessário. Remove o mínimo (proximo no) 53 if (no atual NIL) A filho menor [no atual ] = NIL; libera no(a, proximo no); else A raiz = NIL; Código utilizado no trecho 51. 54. Variáveis locais de remove minimo de subarvore 54 int no atual, proximo no; Código utilizado no trecho 51. 55. Combinamos agora as ferramentas que construímos, para poder remover um elemento arbitrário info da árvore. A ideia é a seguinte: partindo da raiz, seguimos procurando o nó que queremos remover. Nesse processo, asseguramos (usando move rubro para menor ( ) e move rubro maior ( ) que estamos em um 3-nó ou em um 4-nó. Quando encontramos o elemento buscado, se estamos em uma folha, simplesmente removemos o nó; caso contrário substituímos o valor de sua chave pelo valor da chave de seu sucessor. A seguir, removemos sucessor, que é o nó de menor chave na sub-árvore filho maior do nó! (Note que uma estratégia simétrica, que troca o nó pelo seu antecessor e depois o remove, funciona igualmente aqui.) Note que não é preciso se preocupar em arrumar os nós que foram manipulados no início, já que o último passo de remove minimo de subarvore ( ) é iniciar o concerto da árvore, que prossegue até a raiz da árvore. Funções 13 + void remove da arvore (t arnie A, int info, int( compara )(int, int)) Variáveis locais de remove da arvore 63 if (A raiz NIL) / Árvore não está vazia. / no atual = A raiz ; while (verdade ) Caminha pela árvore buscando info, e removendo-o se encontrado. Conserta a árvore e retorna. 56 17

56. Se o nó está à esquerda, use move rubro para menor, e analogamente, até encontrar o nó... Uma palavra sobre a condição ( ). Testamos apenas o filho maior para saber se o nó é uma folha. Por que? A condição anterior garante que se o filho menor fosse vermelho, aconteceria uma rotação, e então o filho maior seria vermelho. Se o nó vermelho é nulo, significa que a condição anterior resultou falsa, e o nó filho menor é negro também. Como para qualquer nó a altura negra é igual, se o nó filho maior é NIL, então o filho menor tem que ser NIL também. Sobre a consição ( ): neste ponto, sabemos que a compara (info, info[no atual ]), e que, se p sai ponto[no atual ], então não estamos em uma folha (mais ainda, a sub-árvore maior do no atual tem pelo menos um nó não NIL). Sendo assim, ou o nó atual é o nó buscado, ou o nó procurado está na sub-árvore maior. Em ambos os casos, queremos garantir que a busca possa prosseguir por essa árvore. Caminha pela árvore buscando info, e removendo-o se encontrado. Conserta a árvore e retorna. 56 if (compara (info, A info[no atual ])) printf ("remoç~ao1: (%d) < (%d)\n", info, A info[no atual ]); Empurra um link rubro para o filho menor se necessário, e prossegue para esse filho. 57 else printf ("remoç~ao2: (%d) > (%d)\n", info, A info[no atual ]); if (eh rubro(a, A filho menor [no atual ])) no atual = rotaciona maior (A, no atual ); if (A info[no atual ] info A filho maior [no atual ] NIL) / ( ) / Estamos em uma folha (no atual ), que é o nó a ser removido. Remove no atual e percorre a árvore ao contrário, consertando o que houver de errado; retorna da rotina 58 if (eh negro(a, A filho maior [no atual ]) eh negro(a, A filho menor [A filho maior [no atual ]])) / ( ) / no atual = move rubro para maior (A, no atual ); if (A info[no atual ] info) Estamos em um nó interno (no atual ), que é o nó a ser removido. Coloca o valor do sucessor de no atual em no atual e atualiza sucessor, remove o mínimo do filho maior do no atual ; retorna da rotina 59 else no atual = A filho maior [no atual ]; Código utilizado no trecho 55. 57. Empurra um link rubro para o filho menor se necessário, e prossegue para esse filho. 57 if (eh negro(a, A filho menor [no atual ]) eh negro(a, A filho menor [A filho menor [no atual ]])) no atual = move rubro para menor (A, no atual ); no atual = A filho menor [no atual ]; Código utilizado no trecho 56. 58. Estamos em uma folha (no atual ), que é o nó a ser removido. Remove no atual e percorre a árvore ao contrário, consertando o que houver de errado; retorna da rotina 58 if (A pai [no atual ] NIL) / O nó é folha, e é raiz da árvore. Vamos deixar a árvore vazia... / libera no(a, no atual ); A raiz = NIL; else if (A filho menor [A pai [no atual ]] no atual ) A filho menor [A pai [no atual ]] = NIL; else A filho maior [A pai [no atual ]] = NIL; libera no(a, no atual ); no atual = A pai [no atual ]; Conserta o que foi bagunçado 38 return; Código utilizado no trecho 56. 18

59. Fazemos duas intervenções cirúrgicas, que não usam as primitivas já escritas para manipulação da árvore. A saber: copiamos o valor do sucessor do ponto atual para ele (como é um nó interno, sabemos que não é o máximo, donde o seu sucessor não é NIL); e também corrigimos o ponteiro do nó para o seu sucessor (que passa a ser H[H[no atual ]]). Para o resto contamos com as funções que já escrevemos. Estamos em um nó interno (no atual ), que é o nó a ser removido. Coloca o valor do sucessor de no atual em no atual e atualiza sucessor, remove o mínimo do filho maior do no atual ; retorna da rotina 59 A info[no atual ] = A info[sucessor (A, no atual )]; remove minimo de subarvore (A, A filho maior [no atual ]); return; Código utilizado no trecho 56. 60. Funções 13 + int sucessor (t arnie A, int ni ) if (A filho maior [ni ] NIL) ni = A filho maior [ni ]; while (A filho menor [ni ] NIL) ni = A filho menor [ni ]; return ni ; else while ( ni é filho maior de pai [ni ] e pai [ni ] NIL 61 ) ni = A pai [ni ]; return A pai [ni ]; 61. ni é filho maior de pai [ni ] e pai [ni ] NIL 61 (ni A filho maior [A pai [ni ]] A pai [ni ] NIL) Código utilizado no trecho 60. 62. Pré-declaração de funções 12 + int sucessor (t arnie A, int ni ); 63. Variáveis locais de remove da arvore 63 int no atual, proximo no; Código utilizado no trecho 55. 19

64. A lista de arestas duplamente ligada (DCEL) Essa estrutura de dados representa um grafo planar por meio de vértices, meias-arestas e faces. Nós a usaremos para representar o polígono, sua partição e triangulação. 1. Faces representam as regiões da partição ou da triangulação. As faces estão associadas às regiões conexas do plano que restam quando removemos os vértices e arestas do polígono (ou do polígono e arestas que a ele acrescentamos). Cada face tem em sua fronteira um certo número de meias-arestas. A face aponta para alguma de suas arestas. 2. Meias-arestas representam as orientações possíveis de uma aresta. Como cada aresta delimita duas faces, associamos cada meia-aresta a uma delas, de modo que a orientação de uma aresta esteja de acordo com um percurso anti-horário dessa face. A outra orientação de uma aresta é a sua aresta gêmea. Cada uma das arestas gêmeas está orientada de seu vértice origem para o vértice origem de sua gêmea. Resumindo, cada meia-aresta aponta para seu vértice de origem, a face que a meia-aresta percorre em sentido anti-horário, a próxima meia-aresta encontrada nesse percurso, e sua meia-aresta gêmea. 3. Vértices são os próprios vértices do polígono. Apontam para alguma das meias arestas que dele partem. Vamos representar a dcelusando alguns vetores de inteiros. O inteiro guardado em um apontador representa o elemento apontado. aresta da face [ ] é a aresta apontada pela face; aresta do vertice [ ] é a aresta apontada pelo vértice; face da aresta [ ], gemea da aresta [ ], origem da aresta [ ] e proxima da aresta [ ] são, resspectivamente, a face, aresta gêmea, vértice origem e aresta seguinte a uma aresta. Como conhecemos o número de faces, meias-arestas e de vértices na dcel para o polígono triangulado vértices: num vertices ; meias-arestas: 2 (2 num vertices 3) = 4 num vertices 6; faces num vertices 2. Podemos alocar memória para esses vetores uma única vez. A inicialização dessa estrutura tem uma particularidade. Não podemos, criar novas faces à medida que particionamos o polígono, porque isso implicaria que a cada diagonal acidionada precisaríamos percorrer complete ao menos uma das faces, alterando o a face associada a suas arestas. Sendo assim construímos apenas a face de fora do polígono (que nunca é particionada), mantendo as demais arestas em uma face auxiliar. Depois inicializamos as faces, percorrendo as meias-arestas em Inicializa faces da dcel 0. #define face de fora #define face auxiliar 1 2 Inicializa dcel com as arestas do polígono 64 Código utilizado no trecho 21. 65. Inicializa a descrição combinatória da linha 65 Código utilizado no trecho 21. 66. Processa ponto i 66 Código utilizado no trecho 21. 20

67. Triangulação das partições 67 Código utilizado no trecho 3. 68. Impressão das diagonais 68 Código utilizado no trecho 3. 69. Liberação das estruturas usadas 69 Código utilizado no trecho 3. 21

Índice Remissivo A: 25, 27, 28, 29, 30, 34, 40, 41, 42, 43, 44, 46, 47, 51, 55, 60, 62. a: 11, 14. aresta da face : 64. aresta do vertice : 64. argc: 3, 7. argv : 3, 7. aumenta tamanho arvore : 25, 27, 29. aumenta tamanho xy : 5, 7. buffer : 12, 13, 14, 15, 16. buffer da resposta : 13. buffer direita : 14, 18, 19. buffer esquerda : 14, 18, 19. buffer final : 14, 18, 20. compara : 34, 35, 55, 56. cor : 24, 25, 36, 37, 38, 40, 41, 43. eh negro: 24, 48, 52, 56, 57. eh rubro: 24, 38, 46, 48, 56. entrada : 7, 8. eps : 10, 15, 16, 18, 19. exit : 7. fabs : 16, 17, 19. face auxiliar : 64. face da aresta : 64. face de fora : 64. fclose : 7. fflush : 38. filho maior : 24, 25, 35, 36, 37, 38, 40, 41, 43, 45, 46, 47, 48, 49, 55, 56, 58, 59, 60, 61. filho menor : 24, 25, 31, 35, 36, 37, 38, 40, 41, 43, 46, 47, 48, 52, 53, 56, 57, 58, 60. filhos menores : 47. fopen : 7. fora : 64. fprintf : 7. free : 5, 13, 25. fscanf : 7. gemea da aresta : 64. i: 13, 20, 22. info: 24, 25, 34, 35, 36, 37, 55, 56, 59. inicializa arvore : 25, 27. insere : 34. j: 20. k: 20. libera arvore : 25, 27. libera no: 28, 30, 49, 53, 58. libera xy : 5. main : 3. maior no: 24. malloc: 5, 13, 25. memcpy : 5, 6. menor no: 24. merge sort : 10, 12, 13, 14. merge sort rec: 12, 13, 14. move rubro maior : 55. move rubro para maior : 46, 48, 56. move rubro para menor : 46, 52, 55, 56, 57. N: 12, 13. negro: 24, 31, 36, 38. ni : 24, 28, 30, 43, 44, 46, 47, 48, 51, 52, 60, 61, 62. NIL: 24, 25, 29, 31, 34, 35, 36, 37, 38, 40, 47, 48, 49, 51, 52, 53, 55, 56, 58, 59, 60, 61. no: 46, 47. no atual : 34, 35, 37, 38, 39, 45, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 63. no livre : 25, 29, 30, 35, 36. num pontos lidos : 7, 8, 10, 21. num vertices : 64. origem da aresta : 64. outra cor : 43. p: 12, 14, 40, 41, 42. p sai : 56. pai : 24, 25, 28, 29, 33, 36, 37, 38, 40, 41, 48, 52, 58, 60, 61. ponto: 25, 56. printf : 7, 35, 38, 56. proxima da aresta : 64. proxima posicao livre : 25, 26, 28, 29. proximo no: 34, 35, 37, 38, 39, 48, 49, 50, 52, 53, 54, 63. q: 14, 40, 41. qual buffer : 12. r: 12, 14. raiz : 24, 25, 34, 36, 38, 49, 53, 55, 58. realloc: 5, 25. remove da arvore : 55. remove maximo de subarvore : 47. remove minimo de subarvore : 51, 55, 59. resposta : 29. rotaciona maior : 38, 40, 41, 42, 46, 48, 56. rotaciona menor : 38, 40, 42, 46. rubro: 24, 31, 37, 40, 41, 45. rubros : 31. stderr : 7. stdin : 38. sucessor : 59, 60, 62. t arnie: 24, 25, 27, 28, 29, 30, 34, 40, 41, 42, 43, 44, 46, 47, 51, 55, 60, 62. tamanho: 25, 26, 29. tamanho arvore : 25. tamanho buffer : 4, 5, 7. tamanho estatico: 4, 5. tamanho inicial : 25, 27. troca cores : 38, 43, 44, 46. verdade : 34, 47, 51, 55. X: 4, 12, 13, 14. X estatico: 4, 5. Y : 4, 12, 13, 14. Y estatico: 4, 5. 22

Lista de trechos Cabeçalho 6, 9, 17, 24 Utilizado no trecho 2. Caminha pela árvore buscando info, e removendo-o se encontrado. Conserta a árvore e retorna. 56 Utilizado no trecho 55. Conserta o que foi bagunçado 38 Citado no trecho 33. Utilizado nos trechos 34, 47, 51 e 58. Estamos em um nó interno (no atual ), que é o nó a ser removido. Coloca o valor do sucessor de no atual em no atual e atualiza sucessor, remove o mínimo do filho maior do no atual ; retorna da rotina 59 Utilizado no trecho 56. Estamos em uma folha (no atual ), que é o nó a ser removido. Remove no atual e percorre a árvore ao contrário, consertando o que houver de errado; retorna da rotina 58 Utilizado no trecho 56. Funções 13, 14, 25, 28, 29, 34, 40, 41, 43, 46, 47, 51, 55, 60 Utilizado no trecho 2. Iguais, mas a x-coordenada de a[buffer ][p] é menor 16 Utilizado no trecho 15. Iguais, mas ponto no buffer esquerda tem x-coordenada menor 19 Utilizado no trecho 18. Impressão das diagonais 68 Utilizado no trecho 3. Inicializa dcel com as arestas do polígono 64 Utilizado no trecho 21. Inicializa a descrição combinatória da linha 65 Utilizado no trecho 21. Inicializa faces da dcel 0 Citado no trecho 64. Insere info na árvore vazia 36 Utilizado no trecho 34. Insere info na folha 37 Utilizado no trecho 34. Intercala para merge sort rec 18 Utilizado no trecho 14. Leitura do polígono 7 Utilizado no trecho 3. Liberação das estruturas usadas 69 Utilizado no trecho 3. Libera memória alocada 27 Ordenação dos pontos 10 Utilizado no trecho 3. Outras variáveis locais do merge sort rec 20 Utilizado no trecho 14. Pré-declaração de funções 12, 30, 42, 44, 62 Utilizado no trecho 2. Processa ponto i 66 Utilizado no trecho 21. Remove o máximo (proximo no) 49 Citado no trecho 53. Utilizado no trecho 47. Remove o mínimo (proximo no) 53 Utilizado no trecho 51. Resolve caso base do merge sort rec 15 Utilizado no trecho 14. Rotina principal 3 Utilizado no trecho 2. Segue a busca pelo máximo, garantindo nunca estar em um 2-nó. Sai do loop garantindo proximo no filho maior [no atual ] é o máximo 48 Utilizado no trecho 47. Segue a busca pelo mínimo, garantindo nunca estar em um 2-nó. Sai do loop garantindo proximo no filho menor [no atual ] é o mínimo 52 Utilizado no trecho 51. Segue a busca, avançando no atual. Prepara a inserção em uma folha de no atual e escapa do loop 35 Utilizado no trecho 34. Triangulação das partições 67 Utilizado no trecho 3. Variáveis locais de insere 39 Utilizado no trecho 34. Variáveis locais de main 4, 8, 11, 22 Utilizado no trecho 3. Variáveis locais de remove da arvore 63 Utilizado no trecho 55. Variáveis locais de remove maximo de subarvore 50 Utilizado no trecho 47. Variáveis locais de remove minimo de subarvore 54 Utilizado no trecho 51. Variáveis para gerenciar memória na arnie 26 Utilizado no trecho 24. Varredura dos pontos (partição do polígono e construção da dcel) 21 Utilizado no trecho 3. Empurra um link rubro para o filho menor se necessário, e prossegue para esse filho. 57 Utilizado no trecho 56. ni é filho maior de pai [ni ] e pai [ni ] NIL 61 Utilizado no trecho 60. 23