FACULDADE CAMPO LIMPO PAULISTA MESTRADO EM CIÊNCIA DA COMPUTAÇÃO Complexidade de Algoritmos Lista de Exercícios 4 Prof. Osvaldo. 1. Desenvolva algoritmos para as operações abaixo e calcule a complexidade de tempo (pior caso) para os mesmos. Use um vetor como estrutura de dados. a) inserir um elemento em uma dada posição de um vetor. b) inserir n elementos em um vetor (por exemplo, lendo um a um a partir de uma entrada padrão). c) inserir um o elemento x em um vetor ordenado, mantendo a ordenação. Isto é eficiente? 2. Uma pilha é uma estrutura de dados que que admite operações de inserção e remoção segundo uma disciplina LIFO (last input, first output), i.e., o último elemento a entrar na estrutura é o primeiro a sair. Explique como se pode implementar duas pilhas em um vetor [1 .. n] de tal modo que nenhuma pilha “transborde” a menos que o número total de elementos nas pilhas seja igual a n. As operações de inserção e remoção devem ter complexidade de tempo (pior caso) igual a O (1). 3. Mostre como implementar uma fila usando duas pilhas. Analise a complexidade de tempo (pior caso) das operações de inserção e remoção na fila implementada desta forma. 4. Calcule a complexidade de tempo (pior caso) para as seguintes operações realizadas sobre uma lista ligada simples: a) inserir um elemento na última posição da lista. b) remover o elemento da primeira posição da lista. c) remover o elemento de valor igual a x da lista. 5. Uma lista ligada circular é uma lista ligada em que os elementos podem ser percorridos circularmente. Desenvolva algoritmos para as operações de inserção, remoção e busca de um elemento em uma lista circular. Quais são as complexidades de tempo (pior caso) destes algoritmos? 6. Escreva um algoritmo para inverter a ordem dos elementos de uma lista ligada simples. Calcule a complexidade de tempo (pior caso) do seu algoritmo. 7. Uma palavra é um palíndromo se a seqüência de letras que a forma é a mesma, quer seja lida da esquerda para a direita ou da direita para a esquerda (como exemplos: ovo, raiar e osso). Desenvolva uma estrutura de dados baseada em listas e escreva um algoritmo O (n) para resolver o problema de testar se uma palavra é um palíndromo. 1 8. Projete uma estrutura de dados dinâmica (listas) para representar Pilhas. A sua estrutura de dados deve permitir implementar em O (1) os algoritmos para empilhar e desempilhar. 9. Projete uma estrutura de dados dinâmica (listas) para representar Filas. A sua estrutura de dados deve permitir implementar em O (1) os algoritmos para inserir e retirar da fila. 10. Seja A uma matriz esparsa n x m, isto é, boa parte dos seus elementos são nulos ou irrelevantes. Projete uma estrutura de dados que represente A e cujo espaço total seja O (k) em vez de O (n m), onde k é o número total de elementos não irrelevantes de A. 11. Desenvolva um algoritmo para localizar um elemento aij da matriz A, armazenada segundo a estrutura de dados obtida na solução do exercício anterior. Determine a sua complexidade. Um pool é uma estrutura de dados que aceita as seguintes operações: - Inserção (x): insere um elemento x na estrutura de dados; - Remoção: remove algum elemento da estrutura de dados. Os três exercícios a seguir referem-se à estrutura de dados pool. 12. Projete os algoritmos que implementam a estrutura de dados pool de tal forma que as operações de inserção e remoção tenham complexidade de tempo (pior caso) igual a O (1). Admita que a estrutura de dados pode conter elementos duplicados. 13. Modifique a implementação da estrutura de dados pool da seguinte forma: assuma que todo elemento pode aparecer no máximo uma vez na estrutura de dados. A operação de inserção deve agora verificar a existência de duplicatas. Havendo uma duplicata, o elemento não deve ser inserido. Qual a complexidade de tempo (pior caso) de cada operação? 14. Uma outra variante da implementação da estrutura de dados pool é a seguinte: assuma que todos os elementos são identificados por inteiros no intervalo de 1 a n, e n é pequeno o suficiente para se poder alocar memória de tamanho O (n). Cada elemento pode aparecer no máximo uma vez. Projete algoritmos para as operações de inserção e remoção que trabalhem com complexidade de tempo (pior caso) igual a O (1). 15. Uma operação de união toma dois conjuntos disjuntos S1 e S2 como entrada e retorna um conjunto S = S1 ∪ S2. Os conjuntos S1 e S2 são usualmente destruídos na operação. Mostre como a operação de união pode ser desenvolvida tendo uma complexidade de tempo igual a O (1). 2 16. Desenhe árvores binárias de busca inserindo na ordem em que aparecem os seguintes elementos: a) 30 15 40 7 8 38 45 18. b) 7 8 15 18 30 38 40 45. 17. O percurso de uma árvore em preorder resultou na impressão da seqüência A B C F H D L M P N E G I, e o percurso da mesma árvore em inorder resultou em F C H B D L P M N A I G E. Construa uma árvore que satisfaça esses percursos. Ela é única? 18. (ENADE 2011): Suponha que se queira pesquisar a chave 287 em uma árvore binária de busca com chaves entre 1 e 1 000. Durante uma pesquisa como essa, uma seqüência de chaves é examinada. Cada seqüência abaixo é uma suposta seqüência de chaves examinadas em uma busca da chave 287. I. 7, 342, 199, 201, 310, 258, 287 II. 110, 132, 133, 156, 289, 288, 287 III. 252, 266, 271, 294, 295, 289, 287 IV. 715, 112, 530, 249, 406, 234, 287 É válido apenas o que se apresenta em A) I. B) III. C) I e II. D) II e IV. E) III e IV. 19. Desenvolver e calcular as complexidades dos seguintes algoritmos sobre árvores binárias de busca: a) Mínimo: retorna um ponteiro para o menor elemento da árvore. b) Máximo: retorna um ponteiro para o menor elemento da árvore. c) Sucessor: retorna um ponteiro para o sucessor de um nó apontado por um ponteiro, digamos p. 20. Escreva um algoritmo para retornar a soma dos elementos de uma árvore binária. Qual a complexidade do seu algoritmo? 21. (ENADE 2011) Listas ordenadas implementadas com vetores são estruturas de dados adequadas para a busca binária, mas possuem o inconveniente de exigirem custo computacional de ordem linear para a inserção de novos elementos. Se as operações de inserção ou remoção de elementos forem freqüentes, uma alternativa é transformar a lista em uma árvore binária de busca balanceada, que permitirá a 3 execução dessas operações com custo logarítmico. Considerando essas informações, escreva um algoritmo recursivo que construa uma árvore binária de busca completa, implementada por apontadores, a partir de um vetor ordenado, V, de n inteiros, em que n = 2m - 1, m > 0. O algoritmo deve construir a árvore em tempo linear, sem precisar fazer qualquer comparação entre os elementos do vetor, uma vez que este já está ordenado. Para isso, a) descreva a estrutura de dados utilizada para a implementação da árvore. b) escreva o algoritmo para a construção da árvore. A chamada principal à função recursiva deve passar, como parâmetros, o vetor, índice do primeiro e último elementos, retornando a referência ou apontador para a raiz da árvore criada. 22. Mostre que se um nó em uma árvore binária de busca tem dois filhos, então o seu sucessor não possui filho à esquerda e o seu predecessor não possui filho à direita. 23. Escreva um algoritmo que receba como entrada um ponteiro y para um nó e retorne um ponteiro para o pai deste nó em uma árvore binária de busca. O seu algoritmo deve ter complexidade de tempo igual a O (h), onde h é altura da árvore. 24. Árvores binárias podem ser utilizadas para representar expressões aritméticas identificando, sem ambigüidade, a ordem em que as sub-expressões devem ser avaliadas. Por exemplo, as árvores das figuras abaixo representam as expressões: a) 2 + 3*4; b) (2 + 3)*4 e c) (2 + 3*4)*2 + 3*4. Desenvolva um algoritmo que receba como entrada um ponteiro para a raiz de uma árvore binária, como descrevemos, e retorne a avaliação da expressão, isto é, o algoritmo deverá retornar os valores 14, 20 e 40 para as árvores das figuras (a), (b) e (c), respectivamente. Admita que as expressões conterão somente os operadores de adição e multiplicação. Calcule a complexidade do seu algoritmo. 4 25. Ilustre (desenho) a operação Inserir (A, 12, 3) em um heap A = [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1]. Represente o heap na sua ilustração como uma árvore com ponteiros. 26. - Ilustre (desenho) a operação Remover (A, 12) em um heap A = [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1]. Represente o heap na sua ilustração como uma árvore com ponteiros. 27. Qual a diferença entre as propriedades de um heap e de uma árvore binária de busca? É possível imprimir em ordem crescente todos os nós de um heap com complexidade de tempo igual a O (n)? 28. Mostre como um heap pode ser utilizado para criar um algoritmo de ordenação O (n log n). 29. Descreva como implementar uma fila com disciplina FIFO (first input, first output) por meio de um heap. Qual a complexidade das operações de inserir e retirar da fila neste caso? 30. Ilustre a inserção dos elementos {5, 28, 19, 15, 20, 33, 12, 17, 10} em uma tabela de hashing com colisões resolvidas por encadeamento. Considere uma tabela com 9 slots, numerados de 0 (zero) a 8, e tome h(x) = x mod 9 como função de hashing. 31. Desenvolva um algoritmo de complexidade O (n log k) para intercalar k listas ordenadas em uma única lista, onde n é o número total de elementos em todas as listas de entrada. (Sugestão: use um heap para ajudar na tarefa de intercalar). 32. Projete (apenas diga como será, não é necessário escrever os algoritmos) uma estrutura de dados que suporte as seguintes operações em tempo, no pior caso, igual a O (log n), onde n é o número de elementos na estrutura de dados: - Inserir (x): insere o elemento x na estrutura de dados somente se ele não existir. - Remover (x): remove o elemento de valor igual a x da estrutura de dados (se ele existir). - Sucessor (x): retornar o menor valor na estrutura de dados que é maior do que x. Argumente por que a sua estrutura de dados resolve o problema. Observação: nos exercícios a seguir considere, no resultado da complexidade de algum algoritmo sobre um grafo G = (V, E), que n = |V| e que e = |E|. 33. Escreva os vértices visitados por um percurso em profundidade no grafo mostrado na figura 1, começando pelo vértice indicado. Mostre a árvore geradora obtida para cada caso. Considere que os vértices estão colocados em ordem alfabética nas listas 5 de adjacências. Assim, por exemplo, a lista de adjacências do vértice “c” tem os nós inseridos na ordem “a”, “b”, “e” e “f”. a) a b) c c) d d) g e) e f) h Figura 1 34. Escreva os vértices visitados por um percurso em largura no grafo mostrado na figura 1, começando pelo vértice indicado. Mostre a árvore geradora obtida em cada caso. a) a b) c c) d d) g e) e f) h 35. Idem exercício 1, só que para o grafo da figura 2 e para os vértices indicados: a) a b) g c) f Figura 2 36. Idem exercício 2, só que para o grafo da figura 2 e para os vértices indicados: a) a b) g c) f 6 37. Desenvolva um algoritmo que receba como entrada um grafo G = (V, E), não orientado, e dê como saída um vetor com o grau de cada um dos vértices de G. Qual a complexidade do seu algoritmo? 38. Desenvolva um algoritmo que receba como entrada um grafo G = (V, E), orientado, e dê como saída dois vetores, um deles com o grau de entrada e o outro com o grau de saída de cada um do vértices de G. Qual a complexidade do seu algoritmo? 39. Modifique o algoritmo de percurso em profundidade para classificar cada uma das arestas de um grafo orientado G = (V, E) como: (1) aresta de árvore; (2) aresta contrária; (3) aresta direta; (4) aresta enviesada. Dê a sua resposta como uma matriz M de |V| linhas e |V| colunas na qual cada célula M [i, j] contenha um número de 1 até 4 conforme o tipo da aresta (i, j). Qual a complexidade do seu algoritmo? 40. Escreva um algoritmo para determinar se um grafo G = (V, E) não orientado é conexo. Qual a complexidade do seu algoritmo? 41. Um grafo G = (V, E) orientado é conexo se a sua forma não orientada for conexa. Escreva um algoritmo para determinar se um grafo G = (V, E) orientado é conexo. Qual a complexidade do seu algoritmo? 42. O problema agora tem como entrada um grafo não orientado G = (V, E), uma árvore geradora T de G e um vértice v. Desenvolva um algoritmo para determinar se T é uma árvore geradora em percurso em profundidade válida de G a partir de v. Em outras palavras, determine se T pode ser uma saída de algum percurso em profundidade a partir de v. A complexidade de tempo do seu algoritmo deve ser O (n + e). 43. Modifique o algoritmo de ordenação topológica dado em sala da seguinte forma. Assuma que você não saiba se o grafo dado como entrada é acíclico. Obviamente, se o grafo for cíclico, uma ordenação topológica é impossível. Desenvolva um algoritmo que irá ter como saída a ordenação topológica do grafo se o grafo for acíclico e irá ter como saída um ciclo, caso contrário. A complexidade de tempo do seu algoritmo deve ser O (n + e). 44. Considere o algoritmo dado em sala que calcula o menor caminho de um vértice v para todos os outros vértices de um grafo orientado. Prove que o subgrafo consistindo de todas as arestas pertencentes ao menor caminho a partir de v, achado durante a execução do algoritmo, é uma árvore enraizada a partir de v. 45. Seja G = (V, E) um grafo não orientado com pesos nas arestas, e seja T a árvore de caminho mais curto a partir de v (exercício anterior). Suponhamos que todos os pesos em G foram incrementados por uma constante c. A árvore T ainda continua sendo a árvore de menor caminho a partir de v? 46. Um grafo euleriano é um grafo G = (V, E) conexo e tal que todos os seus vértices tem grau par. Estes grafos têm o seu nome devido a um conhecido quebra cabeças 7 resolvido pelo matemático suíço Leonhard Euler em 1736: o problema das pontes de Königsberg (Figura 3). Euler provou que é impossível visitar os pontos A, B, C, e D passando exatamente uma vez em cada uma das pontes que ligam estes pontos porque o grafo correspondente a este problema possui vértices com grau impar. Dado um grafo euleriano, desenvolva um algoritmo eficiente para achar, em tempo linear, uma rota P tal que cada aresta de E apareça em P exatamente uma vez. Figura 3 47. Dado um grafo G = (V, E) e um inteiro k, achar o subgrafo induzido H de G máximo tal que cada vértice em H tenha grau maior que k, ou determinar que ele não existe. Um subgrafo induzido de um grafo G = (V, E) é um grafo H = (U, F) tal que U ⊆ V e F inclui todas as arestas em E para as quais os seus vértices estejam em U. O seu algoritmo deve rodar em tempo linear. 48. Seja G = (V, E) um grafo não orientado tal que cada vértice tem um grau par. Desenvolva um algoritmo linear para orientar as arestas de G de tal modo que cada vértice tenha o seu grau de entrada igual ao seu grau de saída. 49. Desenvolva um algoritmo para achar um vértice em um grafo conexo não orientado para o qual a remoção não desconecta o grafo. O algoritmo deve rodar em tempo linear. Como conseqüência, prove que todo grafo conexo contém um vértice em tal condição. 50. Um grafo G = (V, E) não orientado admite uma k-coloração se todos os vértices de G podem ser coloridos com k diferentes cores tal que dois vértices adjacentes não podem ser coloridos com a mesma cor. Desenvolva um algoritmo linear para colorir o grafo com duas cores ou determinar que duas cores não são suficientes. 8