Pilhas, Filas e Listas 1. Explique como implementar duas pillhas em um único vetor de tamanho n, de tal modo que nenhuma das pilhas sofra estouro positivo (rejeite uma nova inserção por estar lotada), a menos que o número total de elementos em ambas as pilhas juntas seja n. As operações PUSH e POP devem ser executadas no tempo O(1). 2. Enquanto uma pilha permite a inserção e a eliminação de elementos em apenas uma extremidade e um fila permite a inserção em uma extremidade e a eliminação na outra extremidade, uma deque (double-ended queue, ou fila de extremidade dupla) permite a inserção e a eliminação em ambas as extremidades. Escreva quatro procedimentos de tempo O(1) para inserir elementos e eliminar elementos de ambas as extremidades de uma deque construída a partir de um vetor. 3. Mostre como implementar uma fila usando duas pilhas. Analise o tempo de execução das operações sobre as filas. 4. Mostre como implementar uma pilha usando duas filas. Analise o tempo de execução das operações sobre pilhas. 5. Implemente uma pilha usando uma lista simplesmente encadeada. As operações PUSH e POP devem demorar o tempo O(1). 6. Implemente uma fila usando uma lista simplesmente encadeada. As operações ENQUEUE e DEQUEUE devem demorar o tempo O(1). 7. Forneça um procedimento não recursivo de tempo Θ(݊) que inverta uma lista simplesmente encadeada de n elementos. Qualquer consumo de memória além do espaço de armazenamento necessário para a própria lista deve ter tamanho O(1). 8. Explique como implementar listas duplamente encadeadas usando apenas um valor de ponteiro np[x] por item, em lugar dos dois valores usuais (próximo e anterior). Suponha que todos os valores de ponteiros possam ser interpretados como inteiros de k bits e defina np[x] como np[x] = próximo[x] XOR anterior[x], o “ou exclusivo” de k bits de próximo [x] e anterior[x].(O valor NULL é representado por 0). Certifique-se de descrever as informações necessárias para obter acesso ao início da lista. Mostre como implementar operações para inserir, remover e recuperar elementos nesta lista. Mostre também como inverter a ordem dos elementos nesta lista em tempo O(1). 9. Considere uma lista seqüencial definida pelas estruturas de dados abaixo: typedef struct { char nome[41]; char matricula[11]; float cr; } ALUNO; // registro de um aluno // nome do aluno // matricula do aluno // C.R. do aluno typedef struct // lista de alunos { ALUNO aluno[200]; // registros dos alunos int numAlunos; // numero de alunos na lista } LISTA_ALUNOS; a. Calcule o tamanho (em bytes) dos tipos de dados ALUNO e LISTA_ALUNOS, considerando que o tipo char tem tamanho de 1 byte e os tipos int e float têm tamanho de 4 bytes. b. Construa uma função que retorne a quantidade de alunos com coeficiente de rendimento maior ou igual a 7. Protótipo: int numAlunosCrAlto(LISTA_ALUNOS *pLista) c. Construa uma função que receba a matrícula de um aluno e exclua este aluno da lista. A função deve retornar o valor 0 caso a exclusão seja bemsucedida, ou o valor 1 caso o aluno não seja encontrado na lista. Protótipo: int excluirAluno(LISTA_ALUNOS *pLista, char *matricula) 10. Considere uma lista ordenada de itens disponíveis no almoxarifado de uma fábrica, sendo que cada item é identificado por um código numérico exclusivo: typedef struct { int codItem; char descricao[21]; int qtdEstoque; } ITEM; typedef struct { ITEM item[5000]; int numItens; } LISTA_ITENS; // item do almoxarifado // código de identificação do item // texto descritivo do item // quantidade disponível em estoque // lista de itens // registros dos itens // numero de itens na lista a. Supondo que a lista não esteja ordenada, construa uma função que receba um código de identificação de item e retorne a quantidade disponível em estoque para o item correspondente (caso o item não exista na lista, deve ser retornado o valor 0). Protótipo: int pesquisar(LISTA_ITENS *pLista, int cod) b. Supondo agora que a lista esteja ordenada, construa uma nova versão da mesma função construída no item (a) utilizando pesquisa binária. 11. Nesta questão, você implementará uma pilha de caracteres e a utilizará em uma aplicação. a) Defina um tipo de dado denominado PILHA que represente uma pilha capaz de armazenar até 200 elementos do tipo primitivo char e construa as funções empilhar e desempilhar para esta estrutura. Protótipos: int empilhar(PILHA *pPilha, char c int desempilhar(PILHA *pPilha, char *c) b) Construa uma função que verifique se uma palavra é palíndroma, ou seja, se a palavra se mantém igual quando lida de trás para frente (exemplos: "radar", "arara", etc.). A função deve declarar uma variável do tipo PILHA e utilizar as funções empilhar e desempilhar construídas no item (a). Deve ser retornado o valor inteiro 0 quando a palavra for palíndroma, 1 quando não for palíndroma e 2 quando ocorrer estouro de pilha. Protótipo: int palindroma(char *palavra) 12. Uma empresa de manutenção de equipamentos está implementando um sistema de controle de atendimento a chamados de clientes. O sistema deve ser capaz de cadastrar os chamados solicitados pelos clientes e deve garantir que os chamados sejam atendidos na mesma ordem em que tiverem sido cadastrados. Supondo que já exista um tipo de dados definido como CHAMADO que contém todas as informações necessárias para o cadastramento e atendimento de um chamado, implemente o que se pede abaixo: a. Defina um tipo de dados denominado FILA_CHAMADOS, que implemente uma fila circular de registros do tipo CHAMADO, com capacidade máxima de 500 chamados simultâneos. b. Construa uma função para cadastramento de chamados, cuja finalidade seja enfileirar um novo registro do tipo CHAMADO em uma fila circular do tipo FILA_CHAMADOS. cadastrarChamado(FILA_CHAMADOS *pFila, Protótipo: int CHAMADO *pChamado) c. Construa uma função para atendimento de chamados, cuja finalidade seja desenfileirar um registro do tipo CHAMADO de uma fila circular do tipo FILA_CHAMADOS. Protótipo: int atenderChamado(FILA_CHAMADOS *pFila, CHAMADO *pChamado) obs: as funções para cadastrar e atender chamados devem retornar o valor 0 quando a operação for bem-sucedida, ou um valor diferente de 0, quando ocorrer algum erro que impossibilite a operação. 13. Considere uma lista dos clientes de uma empresa, definida pelas seguintes estruturas de dados: typedef struct { char nome[41]; int idade; float rendaMensal; } CLIENTE; // dados do cliente // // // nome do cliente idade do cliente renda mensal do cliente typedef struct // lista de clientes { CLIENTE cliente[5000]; // registros dos clientes int numClientes ; // número de clientes armazenados } LISTA_CLIENTES; Escreva funções em linguagem C para ordenar uma lista do tipo LISTA_CLIENTES, utilizando algoritmo de ordenação de sua livre escolha, conforme com os seguintes critérios: a. Pelo campo “nome” (ordem lexicográfica crescente) Protótipo: ordenarNome(LISTA_CLIENTES *pLista) b. Pelos campos “idade” (ordem crescente) + “rendaMensal” (ordem decrescente) Protótipo: ordenarIdadeRenda(LISTA_CLIENTES *pLista) 14. Considere agora uma versão encadeada da lista de clientes descrita na questão anterior, definida pelas seguintes estruturas de dados: typedef struct { CLIENTE cliente; void * pProximo; } ELEMENTO; // Estrutura de um elemento da lista // dados do cliente // ponteiro para o próximo elemento typedef struct // Estrutura da lista encadeada { CLIENTE * pPrimeiro; // ponteiro para o primeiro elemento } LISTA_ENCADEADA; Escreva funções em linguagem C para efetuar as operações solicitadas em cada item a seguir: a. Retornar o número de clientes com renda mensal igual ou superior a 1000. Protótipo: int numClientesRendaAlta(LISTA_ENCADEADA *pLista) b. Retornar ponteiro para o cliente mais velho da lista (retornar NULL em caso de lista vazia). Protótipo: CLIENTE *clienteMaisVelho(LISTA_ENCADEADA *pLista) 15. Escreva funções em linguagem C para enfileirar e desenfileirar pacientes de uma fila circular do tipo FILA_PACIENTES, conforme os protótipos abaixo: int enfileirar(FILA_PACIENTES *pFila, PACIENTE *pPaciente) int desenfileirar(FILA_PACIENTES*pFila,PACIENTE *pPaciente) O valor de retorno deve ser igual a 0 quando a operação for bem sucedida, ou igual a 1 quando ocorrer alguma situação em que a operação não possa ser efetuada. 16. Nesta questão, você implementará uma pilha de caracteres e a utilizará em uma aplicação. a. Defina um tipo de dado que represente uma pilha capaz de armazenar até 200 elementos do tipo primitivo char e construa as funções empilhar e desempilhar para esta estrutura. b. Construa uma função que receba como parâmetro uma string contendo uma instrução em linguagem C e verifique se os parênteses e colchetes estão consistentes, utilizando a pilha que você definiu (exemplo: "x=2*(a[5*(2+b[i])]-c[j]);" é uma expressão consistente, mas "x=2*(a[5*(2+b[i])-c[j]);" e "x=2*a[5*(2+b[i])]-c[j]);" não são). 17. Considere uma lista simplesmente encadeada cujos elementos possuam a estrutura abaixo, onde o tipo ALUNO foi definido na questão 1: typedef struct { ALUNO aluno; // dados do aluno void * pProximoAluno; // ponteiro para próximo aluno } ELEMENTO; A lista deve ser representada por um ponteiro do tipo ELEMENTO * apontando para um "nó-cabeça" cujo campo pProximoAluno aponta para o primeiro aluno da lista. Escreva os algoritmos necessários para efetuar cada as operações especificadas abaixo: a. Retornar a quantidade de alunos com coeficiente de rendimento maior ou igual a 7. → int numAlunosCrAlto(ELEMENTO *pLista) b. Excluir da lista todos os alunos com coeficiente de rendimento menor que 5. → void excluirAlunosCrBaixo(ELEMENTO *pLista) c. Retornar o nome do aluno com o maior coeficiente de rendimento da lista. → char * nomeAlunoMaiorCr(ELEMENTO *pLista) 18. Nesta questão, você trabalhará com filas circulares e construirá um sistema de gerenciamento de filas de impressão (spooler). Um spooler é utilizado quando uma impressora de rede é compartilhada por diversos usuários. Ao se inserir um arquivo no spooler, especifica-se um nível de prioridade para aquele arquivo. Arquivos com mesmo nível de prioridade são processados em ordem de chegada, mas arquivos com nível de prioridade mais alto são sempre processados antes dos arquivos com prioridade mais baixa (mesmo tendo chegado depois). Considere um spooler composto por 3 filas, conforme a estrutura abaixo, onde o tipo de dado FILA é uma implementação padrão de fila circular: typedef { FILA FilaAlta; FILA FilaMedia; FILA FilaBaixa; } SPOOLER; // Fila de prioridade alta // Fila de prioridade média // Fila de prioridade baixa a. Defina o tipo de dado FILA para uma fila circular capaz de armazenar até 50 elementos do tipo FILE * e construa as funções enfileirar e desenfileirar para esta estrutura. Estas funções devem retornar um código indicando se foi possível realizar a operação. b. Construa a função inserirSpooler(SPOOLER *,FILE *,char), que enfileira o ponteiro de arquivo recebido na fila com a prioridade especificada ('A'=alta; 'M'=média; 'B'=baixa), e a função retirarSpooler(SPOOLER *,FILE *), que desenfileira o ponteiro de arquivo que estiver no início da fila de mais alta prioridade que não esteja vazia. 19. Considere uma versão duplamente encadeada (com "nó-cabeça") da lista descrita na questão anterior, cujos elementos possuam a estrutura typedef struct { DADOS_CLIENTE dados; // dados do cliente void * pProximoCliente; // ponteiro para próximo cliente void * pClienteAnterior; // ponteiro para cliente anterior } ELEMENTO; Escreva os algoritmos necessários para efetuar cada as operações definidas abaixo: a. Trocar um elemento de posição com o seu sucessor → void trocarPosicao(ELEMENTO *pElemento) b. Ordenar os elementos da lista em ordem crescente de idade, utilizando bubblesort. → void ordenarBolha(ELEMENTO *pLista) 20. Considere uma lista encadeada que armazena os clientes de uma empresa, definida pelas seguintes estruturas de dados: typedef struct { char nome[41]; char telefone[15]; int idade; } DADOS_CLIENTE; // Dados de um cliente da empresa // // // nome do cliente telefone do cliente idade do cliente typedef struct // Nó da lista encadeada { DADOS_CLIENTE dados; // dados do cliente void * pProximoCliente; // ponteiro para próximo cliente } CLIENTE; typedef struct // Lista encadeada de clientes { CLIENTE * pPrimeiroCliente; // ponteiro p/ primeiro cliente } LISTA_CLIENTES; Escreva funções em linguagem C para efetuar as operações solicitadas em cada item a seguir: a. Retornar o número total de clientes armazenados na lista. → int numElementos(LISTA_CLIENTES *pLista) b. Retornar os dados do cliente mais velho da lista. → DADOS_CLIENTE *dadosClienteMaisVelho(LISTA_CLIENTES *pLista) c. Excluir da lista todos os clientes com idade maior ou igual a 65 anos. → void excluirIdosos(LISTA_CLIENTES *pLista) Árvores 1. Forneça um algoritmo não recursivo que execute um percurso de árvore em ordem. (Sugestão: Existe uma solução fácil que usa uma pilha como uma estrutura de dados auxiliar e uma solução mais complicada, embora elegante, que não emprega nenhuma pilha mas pressupõe que é possível testar a igualdade entre dois ponteiros.) 2. Forneça algoritmos recursivos que executem caminhos de árvores de pré-ordem e pós-ordem no tempo Θ(݊) em uma árvore de n nós. 3. Mostre que, se um nó em uma árvore de pesquisa binária tem dois filhos, então seu sucessor não tem nenhum filho da esquerda e seu predecessor não tem nenhum filho da direita. 4. Um percurso de árvore em ordem de uma árvore de pesquisa binária de n nós pode ser implementado encontrando-se o elemento mínimo na árvore com TREEMINIMUM e, em seguida, fazendo-se n-1 chamadas a TREE-SUCESSOR. Prove que este algoritmo é executado em tempo Θ(݊). 5. Podemos classificar um dado conjunto de n números construindo primeiro uma árvore de pesquisa binária contendo esses números ( usando TREE-INSERT repetidamente para inserir os números um a um), e depois imprimindo os números por meio de um percurso de árvore em ordem. Quais são os tempos de execução no pior caso e no melhor caso para esse algoritmo de ordenação? 6. Dadas duas cadeias ܽ = ܽ ܽଵ … ܽ ݁ ܾ = ܾ ܾଵ … ܾ onde cada ܽ e cada ܾ pertencem a algum conjunto ordenado de caracteres, dizemos que a cadeia a é lexicograficamente menor que a cadeia b se 1- Existe um inteiro j, onde 0 ≤ ݆ ≤ min (, )ݍ, tal que ܽ = ܾ para todo ݅ = 0, 1, … ݆ − 1 ݁ ܽ < ܾ , ou 2- ܽ ݁ ݍ < = ܾ para todo ݅ = 0, 1, … , . Por exemplo, se a e b são cadeias de bits, então 10100 < 10110 pela regra 1 (fazendo-se j = 3) e 10100 < 101000 pela regra 2. Isso é semelhante à ordenação utilizada nos dicionários de idiomas. A estrutura de dados raiz de árvore mostrada na figura abaixo armazena as cadeias de bits 1011, 10, 011, 100 e 0. Quando procuramos por uma chave ܽ = ܽ ܽଵ … ܽ , vamos para a esquerda em um nó de profundidade i se ܽ = 0 e para a direita se ܽ = 1. Seja S um conjunto de cadeias binárias distintas cujos comprimentos produzem a soma n. Mostre como usar uma raiz de árvore para ordenar lexicograficamente o conjunto S no tempo Θ(݊). No caso do exemplo da Figura, a saída da ordenação deve ser a sequência 0, 011, 10, 100, 1011. 7. Considere a árvore binária de busca representada no diagrama abaixo, no qual são exibidos os valores das chaves de ordenação numéricas correspondentes a cada nó: a. Indique a ordem em que os nós da árvore acima seriam visitados em um percurso pré-ordem e em um percurso pós-ordem. b. Mostre que é árvore acima é AVL, indicando, para cada nó, as alturas das subárvores esquerda e direita e o fator de balanço correspondente. c. Redesenhe a árvore após a inserção de um novo elemento cuja chave de ordenação tenha valor 60 e demonstre que a árvore deixará de ser AVL, indicando os nós que ficarão desregulados e seus respectivos balanços. d. Indique qual o tipo de rotação que deve ser aplicada para que a árvore volte a ser AVL após a inclusão do novo elemento e redesenhe a árvore após a aplicação dessa rotação. 8. Considere uma lista telefônica armazenada na forma de uma árvore binária de busca, representada pelas estruturas de dados definidas abaixo: typedef struct {char nome[41]; char telefone[11];} ASSINANTE; typedef struct {ASSINANTE a; void *pDir; void *pEsq;} NO; a. Construa uma função recursiva que receba o nome de um assinante e retorne o seu telefone (a função deve retornar NULL caso o assinante não seja encontrado na lista). Protótipo: char * pesquisarTelefone(NO * pNoRaiz, char *nomeAssinante) b. Construa uma função recursiva que exiba a lista de assinantes em ordem alfabética (dica: faça um percurso em ordem simétrica, imprimindo nome e telefone para cada nó visitado). Protótipo: void exibirLista(NO *pNoRaiz) c. Construa uma função recursiva que retorne a altura da árvore. Protótipo: int determinarAltura(NO *pNoRaiz) 9. Considere um cadastro de alunos armazenado em uma árvore binária de busca representada pelas estruturas de dados abaixo, utilizando o campo nome como chave de ordenação: typedef struct { char nome[51]; int idade; } ALUNO; typedef struct { ALUNO raiz; void *pSubArvDir; void *pSubArvEsq; } ARVORE; a. Construa uma função recursiva que receba o nome de um aluno e retorne a sua idade (a função deve retornar NULL caso o aluno não seja encontrado na árvore). Protótipo: int pesquisarIdadeAluno(ARVORE *pArvore, char *nomeAluno) b. Construa uma função recursiva que imprima os nomes de todos os alunos cadastrados na árvore em ordem alfabética (dica: implemente um percurso em ordem simétrica). Protótipo: void imprimirNomes(ARVORE *pArvore) c. Construa uma função recursiva que receba um ponteiro para uma estrutura do tipo ARVORE e execute uma rotação dupla à direita na raiz da árvore AVL representada nesta estrutura. Protótipo: void rotacaoDuplaDireita(ARVORE *pArvore) 10. Considere um cadastro de funcionários armazenado na forma de uma árvore binária de busca representada pelas estruturas de dados definidas abaixo: typedef struct { int matricula; char nome[41]; float salario; } FUNCIONARIO; // DADOS DO FUNCIONARIO // matricula do funcionario // nome do funcionario // salario do funcionario typedef struct { FUNCIONARIO raiz; void *pSubArvoreEsq; void *pSubArvoreDir; } ARVORE; // ARVORE BINARIA DE BUSCA // dados do funcionario // ponteiro para subarvore esquerda // ponteiro para subarvore direita Construa uma função recursiva que receba a matrícula de um funcionário e retorne o seu salário. Protótipo: float salarioFuncionario(ARVORE *pArvore, int matricula)