Ementrio Noes de hardware e software Conceitos Fundamentais

  • Slides: 19
Download presentation
Ementário • Noções de hardware e software. • Conceitos Fundamentais. • Vetores, cadeia de

Ementário • Noções de hardware e software. • Conceitos Fundamentais. • Vetores, cadeia de caracteres e registros. • Definição e manipulação de arquivos externos. • Subprogramação. • Alocação dinâmica de memória. • Estruturas de dados básicas: listas lineares, pilha e fila. Árvores e grafos. • Métodos de ordenação. • Aplicações práticas utilizando uma linguagem estruturada de alto nível (Linguagem C). • Noções de Programação Orientada a Objetos com a Linguagem C++.

Definição Básica Um grafo é constituído de um conjunto “não” vazio de objetos denominados

Definição Básica Um grafo é constituído de um conjunto “não” vazio de objetos denominados de vértices e um conjunto de arestas conectando pares de vértices. 0 1 4 vértice aresta 3 2 5 Grafo não direcionado Grafo rotulado

Grafo Direcionado (ou Digrafo) Um grafo direcionado G é um par (V, A), em

Grafo Direcionado (ou Digrafo) Um grafo direcionado G é um par (V, A), em que V é um conjunto finito de vértices e A é um conjunto de arestas com uma relação binária em V. Usando o grafo direcionado ilustrado na figura: 0 3 1 2 4 5 Em grafos direcionados podem existir arestas de um vértice para ele mesmo, chamados de self-loops. Tem-se: • conjunto de vértices: V = {0, 1, 2, 3, 4, 5} • conjunto de arestas: A = {(0, 1), (0, 3), (1, 2), (1, 3), (2, 2), (2, 3), (3, 0), (5, 4)}

Grafo Não Direcionado Um grafo não direcionado G é um par (V, A), em

Grafo Não Direcionado Um grafo não direcionado G é um par (V, A), em que o conjunto de arestas A é constituído de pares de vértices não ordenados. As arestas (u, v) e (v, u) são consideradas como única. Em um grafo não direcionado self-loops “não” são permitidos. Usando o grafo não direcionado ilustrado na figura: Tem-se: • conjunto de vértices: V = {1, 2, 3, 4, 5, 6} • conjunto de arestas: A = {(1, 2), (1, 5), (2, 1), (2, 3), (2, 5), (3, 2), (3, 4), (4, 3), (4, 5), (4, 6), (5, 1), (5, 2), (5, 4) (6, 4)}

Adjacência Em um grafo direcionado, a aresta (u, v) sai do vértice u e

Adjacência Em um grafo direcionado, a aresta (u, v) sai do vértice u e entra no vértice v. Por exemplo, na figura abaixo os arcos que saem do vértice 2 são (2, 2) e (2, 3), e os arcos que incidem sobre o vértice 2 são (1, 2) e (2, 2). Se (u, v) é uma aresta no grafo G = (V, A), o vértice v é adjacente ao vértice u. Quando o grafo é não direcionado, a relação de adjacência é simétrica. 0 1 4 3 2 5 Relação de adjacências: 0 é adjacente de 3 1 é adjacente de 0 2 é adjacente de 1 e 2 3 é adjacente de 0, 1 e 2 4 é adjacente de 5

Grau de um Vértice (1/2) Em um grafo direcionado, o grau de um vértice

Grau de um Vértice (1/2) Em um grafo direcionado, o grau de um vértice corresponde ao número de arestas que saem do vértice (out-degree) somado ao número de arestas que chegam ao vértice (in-degree). Para o grafo a seguir: 0 1 4 3 2 5 Tem-se: Vértice 0 1 2 3 4 5 out-degree 1, 3 = 2 2, 3 = 2 0 = 1 nenhum 4 = 1 in-degree 3 = 1 0 = 1 1, 2 = 2 0, 1, 2 = 3 5 = 1 nenhum Grau do 2 + 1 = 2 + 2 = 1 + 3 = 0 + 1 = 1 + 0 = Vértice 3 3 4 4 1 1

Grau de um Vértice (2/2) Em um grafo não direcionado, o grau de um

Grau de um Vértice (2/2) Em um grafo não direcionado, o grau de um vértice corresponde ao número de arestas que incidem nele. Para o grafo a seguir: 0 1 4 3 2 5 Tem-se: Vértice 0 1 2 3 arestas (0, 1), (0, 2) (1, 0), (1, 2) (2, 0), (2, 1) nenhuma 4 5 (4, 5) (5, 4) Grau do Vértice 2 2 2 0 (isolado ou não conectado) 1 1

Tipo Abstrato de Dados Grafo (1/7) Estrutura de dados mais um conjunto de operações

Tipo Abstrato de Dados Grafo (1/7) Estrutura de dados mais um conjunto de operações associados. Implementação por meio de “Listas de Adjacências” usando apontadores: Lista de vértices Grafo 0 5 2 4 tipo. Grafo 7 3 vertice distancia proxima. Aresta primeiro. Vertice 2 3 1 2 Lista de adjacências (arestas) tipo. Vertice 5 5 vertice grau primeira. Aresta 1 3 2 5 1 3 0 3 3 7 2 2 0 5 3 2 3 3 1 7 2 2 4 1 1 2 5 2 3 5 6 1 5 1 0 2 ultima. Aresta 1 6 • conjunto de vértices: V = {0, 1, 2, 3, 4, 5, 6} • conjunto de arestas: A = {(0, 1), (0, 2), (1, 0), (1, 3), (1, 4), (2, 0), (2, 3), (3, 1), (3, 2), (3, 5), (4, 1), (5, 3), (5, 6), (6, 5)} tipo. Aresta proximo. Vertice apontador. Tipo. Vertice ultimo. Vertice apontador. Tipo. Aresta 4 2 5 5

Tipo Abstrato de Dados Grafo Estrutura de dados (tipo. Grafo. h): typedef struct tipo.

Tipo Abstrato de Dados Grafo Estrutura de dados (tipo. Grafo. h): typedef struct tipo. Vertice *apontador. Tipo. Vertice; typedef struct tipo. Aresta *apontador. Tipo. Aresta; struct tipo. Vertice { int vertice; int grau; int visitado; apontador. Tipo. Vertice proximo. Vertice; apontador. Tipo. Aresta primeira. Aresta; apontador. Tipo. Aresta ultima. Aresta; }; struct tipo. Aresta { int vertice; int distancia; apontador. Tipo. Aresta proxima. Aresta; }; struct tipo. Grafo { apontador. Tipo. Vertice primeiro. Vertice; apontador. Tipo. Vertice ultimo. Vertice; }; (2/7)

Tipo Abstrato de Dados Grafo (3/7) Conjunto de operação (oper. Grafo. h): (1/5) //

Tipo Abstrato de Dados Grafo (3/7) Conjunto de operação (oper. Grafo. h): (1/5) // Cria a célula cabeça da lista de vértices: // 1. cria uma célula do tipo. Vertice colocando o endereço no campo primeiro. Vertice do Grafo // 2. incializa os campos do tipo. Vertice: vertice, grau, proximo. Vertice, primeira. Aresta e // ultima. Aresta com valores nulos // 3. o apontador ultimo. Vertice recebe o endereço da célula cabeça void faz. Grafo. Vazio(struct tipo. Grafo *grafo) { grafo->primeiro. Vertice = (apontador. Tipo. Vertice) malloc(sizeof(tipo. Vertice)); grafo->primeiro. Vertice->vertice = -1; grafo->primeiro. Vertice->grau = -1; grafo->primeiro. Vertice->proximo. Vertice = NULL; grafo->primeiro. Vertice->primeira. Aresta = NULL; grafo->primeiro. Vertice->ultima. Aresta = NULL; grafo->ultimo. Vertice = grafo->primeiro. Vertice; } // verifica se o Grafo está vazio (sem nenhum vértice) int grafo. Vazio(struct tipo. Grafo *grafo) { return(grafo->primeiro. Vertice == grafo->ultimo. Vertice); } // retorna o endereço do "vertice" (NULL se o vértice não for localizado) apontador. Tipo. Vertice localiza. Vertice( int vertice, struct tipo. Grafo *grafo) { apontador. Tipo. Vertice p = grafo->primeiro. Vertice->proximo. Vertice; while ((p != NULL) && (p->vertice != vertice)) { p = p->proximo. Vertice; } return(p); }

Tipo Abstrato de Dados Grafo (4/7) Conjunto de operação (oper. Grafo. h): (2/5) //

Tipo Abstrato de Dados Grafo (4/7) Conjunto de operação (oper. Grafo. h): (2/5) // Cria a célula cabeça da lista de adjacências do vértice: // 1. cria uma célula do tipo. Aresta colocando o endereço no campo primeiro. Aresta do vértice // 2. incializa os campos do tipo. Aresta: vertice, distancia e proxima. Aresta com valores nulos // 3. o apontador ultimo. Aresta recebe o endereço da célula cabeça void faz. Lista. Adjacencias. Vazia(apontador. Tipo. Vertice vertice) { vertice->primeira. Aresta = (apontador. Tipo. Aresta) malloc(sizeof(tipo. Aresta)); vertice->primeira. Aresta->vertice = -1; vertice->primeira. Aresta->distancia = -1; vertice->primeira. Aresta->proxima. Aresta = NULL; vertice->ultima. Aresta = vertice->primeira. Aresta; } // verifica se a lista de adjacências do vértice está vazia int lista. Adjacencias. Vazia(apontador. Tipo. Vertice vertice) { return(vertice->primeira. Aresta == vertice->ultima. Aresta); } // retorna o endereço da "aresta" na lista de adjacências do vértice (NULL se a aresta // não for localizada) apontador. Tipo. Aresta localiza. Aresta(apontador. Tipo. Vertice vertice, int aresta) { apontador. Tipo. Aresta p = vertice->primeira. Aresta->proxima. Aresta; while ((p != NULL) && (p->vertice != aresta)) { p = p->proxima. Aresta; } return(p); }

Tipo Abstrato de Dados Grafo (5/7) Conjunto de operação (oper. Grafo. h): (3/5) //

Tipo Abstrato de Dados Grafo (5/7) Conjunto de operação (oper. Grafo. h): (3/5) // Insere um novo vértice no final da lista de vértices: // 1. cria uma nova célula colocando-a no final da lista (campo ultimo. Vertice) // 2. atualiza o apontador ultimo. Vertice // 3. armazena o valor do vértice no campo correspondente // 4. inicializa o grau do vértice com o valor zero // 5. faz a nova célula através do campo proximo. Vertice apontar para o vazio // 6. faz a lista de adjacências do novo vértice vazia void insere. Vertice(int vertice, struct tipo. Grafo *grafo) { grafo->ultimo. Vertice->proximo. Vertice = (apontador. Tipo. Vertice) malloc(sizeof(tipo. Vertice)); grafo->ultimo. Vertice = grafo->ultimo. Vertice->proximo. Vertice; grafo->ultimo. Vertice->vertice = vertice; grafo->ultimo. Vertice->grau = 0; grafo->ultimo. Vertice->proximo. Vertice = NULL; faz. Lista. Adjacencias. Vazia(grafo->ultimo. Vertice); } // Insere uma nova aresta no final da lista de adjacências do vértice de origem: // 1. cria uma nova célula colocando-a no final da lista de adjacências (campo ultima. Aresta) // 2. atualiza o apontador ultima. Aresta // 3. armazena o valor do vértice destino da aresta no campo correspondente // 4. armazena a distância da aresta // 5. faz a nova célula através do campo proximo. Aresta apontar para o vazio // 6. atualiza o grau do vértice de origem void insere. Aresta(apontador. Tipo. Vertice vertice. Origem, int vertice. Destino, int distancia) { vertice. Origem->ultima. Aresta->proxima. Aresta = (apontador. Tipo. Aresta) malloc(sizeof(tipo. Aresta)); vertice. Origem->ultima. Aresta = vertice. Origem->ultima. Aresta->proxima. Aresta; vertice. Origem->ultima. Aresta->vertice = vertice. Destino; vertice. Origem->ultima. Aresta->distancia = distancia; vertice. Origem->ultima. Aresta->proxima. Aresta = NULL; vertice. Origem->grau = vertice. Origem->grau + 1; }

Tipo Abstrato de Dados Grafo (6/7) Conjunto de operação (oper. Grafo. h): (4/5) No

Tipo Abstrato de Dados Grafo (6/7) Conjunto de operação (oper. Grafo. h): (4/5) No processo para remover uma célula em listas encadeadas com apontadores é realizado usando como referência o endereço da célula "anterior" àquela que efetivamente será removida. // retorna o endereço da célula "anterior" a aresta do vértice que será removida apontador. Tipo. Aresta localiza. Aresta. Anterior(apontador. Tipo. Vertice vertice, int aresta) { apontador. Tipo. Aresta p. Ant = vertice->primeira. Aresta; apontador. Tipo. Aresta p = vertice->primeira. Aresta->proxima. Aresta; while ((p != NULL) && (p->vertice != aresta)) { p. Ant = p; p = p->proxima. Aresta; } return(p. Ant); } // remove a "próxima" aresta de "p" da lista de adjacência de "p. Vertice": // 1. "q" recebe o endereço da aresta que será devolvido ao sistema de gerenciamento de memória // 2. a aresta anterior aponta para a próxima aresta àquela que será removida // 3. verifica se a aresta que será removida não é a última aresta da lista de adjacências // 4. atuliza o grau do vértice // 5. libera (free) o endereço da aresta removida void remove. Aresta(apontador. Tipo. Aresta p, apontador. Tipo. Vertice p. Vertice) { apontador. Tipo. Aresta q = p->proxima. Aresta; p->proxima. Aresta = q->proxima. Aresta; se retirando a última aresta da lista de adjacencias if (p->proxima. Aresta == NULL) p. Vertice->ultima. Aresta = p; // Vertice->grau = p. Vertice->grau - 1; free(q); // libera a memória ocupada pela aresta }

Tipo Abstrato de Dados Grafo (6/7) Conjunto de operação (oper. Grafo. h): (5/5) //

Tipo Abstrato de Dados Grafo (6/7) Conjunto de operação (oper. Grafo. h): (5/5) // retorna o endereço da célula "anterior" ao vértice que será removido apontador. Tipo. Vertice localiza. Vertice. Anterior( int vertice, struct tipo. Grafo *grafo) { apontador. Tipo. Vertice p. Ant = grafo->primeiro. Vertice; apontador. Tipo. Vertice p = grafo->primeiro. Vertice->proximo. Vertice; while ((p != NULL) && (p->vertice != vertice)) { p. Ant = p; p = p->proximo. Vertice; } return(p. Ant); } // remove o "próximo" vértice de "p" da lista de vértices do Grafo: void remove. Vertice(apontador. Tipo. Vertice p, struct tipo. Grafo *grafo) { apontador. Tipo. Vertice vertice. Destino, vertice. Origem = p->proximo. Vertice; // processo para remover "todas" as arestas do vértice apontador. Tipo. Aresta aresta. Dois, aresta. Um = vertice. Origem->primeira. Aresta; while (!lista. Adjacencias. Vazia(vertice. Origem)) { vertice. Destino = localiza. Vertice(aresta. Um->proxima. Aresta->vertice, grafo); aresta. Dois = localiza. Aresta. Anterior(vertice. Destino, vertice. Origem->vertice); remove. Aresta(aresta. Um, vertice. Origem); remove. Aresta(aresta. Dois, vertice. Destino); } free(vertice. Origem->primeira. Aresta); // remove a célula cabeça da lista de adjacências p->proximo. Vertice = vertice. Origem->proximo. Vertice; // verifice se o vértice removido é o último da lista de vértices if (p->proximo. Vertice == NULL) grafo->ultimo. Vertice = p; free(vertice. Origem); // libera a memória ocupada pela aresta }

Caminhamento em Grafos Problema: dado um vértice v de um grafo G=(V, A), Objetivo:

Caminhamento em Grafos Problema: dado um vértice v de um grafo G=(V, A), Objetivo: deseja-se caminhar entre “todos” os vértices G que são “alcançáveis” a partir de v. Formas principais de caminhamento: 1) Busca em Profundidade (DFS- Depth-First Search) 2) Busca em Largura (BFS- Breadth-First Search)

DFS- Busca em Profundidade (1/3) Buscar o vértice mais “profundo” sempre que possível. Na

DFS- Busca em Profundidade (1/3) Buscar o vértice mais “profundo” sempre que possível. Na busca em profundidade, as arestas são exploradas a partir do vértice mais recentemente visitado. Algoritmo: 1. visitar v e marcá-lo como “visitado”; 2. percorrer “todas” as arestas v: 2. 1 se o vértice da aresta ainda não foi visitado então deve-se fazer busca em profundidade no vértice da aresta; 2. 2 posicionar na próxima aresta de v. Nota: O algoritmo DFS é recurssivo, ou seja, quando todas as arestas adjacentes tiverem sido exploradas, a busca anda para trás (backtrack) para explorar as outras arestas do vértice de origem.

DFS- Busca em Profundidade (2/3) // percorre a lista de vértice do grafo alterando

DFS- Busca em Profundidade (2/3) // percorre a lista de vértice do grafo alterando o status do campo "visitado" do vértice // para falso (valor inteiro 0) void busca. Em. Profundidade(apontador. Tipo. Vertice p, struct tipo. Grafo *grafo) { apontador. Tipo. Vertice aux = grafo->primeiro. Vertice->proximo. Vertice; while (aux != NULL) { aux->visitado = 0; aux = aux->proximo. Vertice; } DFS(p, grafo); } // DFS- Busca em Profundidade void DFS(apontador. Tipo. Vertice p, struct tipo. Grafo *grafo) { apontador. Tipo. Vertice aux; printf("%d ", p->vertice); p->visitado = 1; // marca o vértice como "visitado" // percorre "todas" as arestas do vértice apontado por "p" apontador. Tipo. Aresta aresta = p->primeira. Aresta->proxima. Aresta; while (aresta != NULL) { // se o vértice ainda não foi visitado aplicar a busca em profundidade no vértice da aresta aux = localiza. Vertice(aresta->vertice, grafo); if (aux->visitado == 0) DFS(aux, grafo); // aplica a DFS no vértice da aresta = aresta->proxima. Aresta; } }

DFS- Busca em Profundidade (3/3) Grafo 0 2 Fim DFS 1 3 5 p

DFS- Busca em Profundidade (3/3) Grafo 0 2 Fim DFS 1 3 5 p 6 DFS a partir do vértice 0: 0 1 3 2 5 6 4 1 3 2 5 6º backtrack pp 4 0 2 1 3 0 3 3 7 2 1º backtrack p 2 2 0 5 3 2 4º backtrack pp 3 3 1 7 2 2 5º backtrack p 4 1 1 2 3º backtrack p 5 2 3 5 2º backtrack p Legenda da visitação: Branco: vértice não visitado Cinza: visitação em andamento Preto: visitação concluída 4 6 1 5 1 6 1 5 5

Referência • Projeto de Algoritmos com implementações em Pascal e C – Nivio Ziviani

Referência • Projeto de Algoritmos com implementações em Pascal e C – Nivio Ziviani – São Paulo: Pioneira Thomson Learning, 2005. – Capítulo 7: Algoritmos em Grafos (pág. 251 -301)