41
Instituto Federal de Educação Ciências e Tecnologia Campus Formiga Bacharelado em Ciência da Computação Atividade: 3ª Trabalho Prático Disciplina: Algoritmos e Estruturas de Dados I Professor: Mário Luiz Rodrigues Oliveira Fabrício Daniel Freitas Matrícula: 002752 Valquíria Soares Silva Matrícula: 002785 Formiga, 23 de Setembro de 2013.

Listas encadeadas, duplamente encadeadas com cabeça

Embed Size (px)

Citation preview

Page 1: Listas encadeadas, duplamente encadeadas com cabeça

Instituto Federal de Educação Ciências e Tecnologia

Campus FormigaBacharelado em Ciência da Computação

Atividade: 3ª Trabalho Prático

Disciplina: Algoritmos e Estruturas de Dados IProfessor: Mário Luiz Rodrigues Oliveira

Fabrício Daniel FreitasMatrícula: 002752

Valquíria Soares SilvaMatrícula: 002785

Formiga, 23 de Setembro de 2013.

Page 2: Listas encadeadas, duplamente encadeadas com cabeça

Sumário:

Sumário...............................................................................................................................2Introdução...........................................................................................................................3Implementação....................................................................................................................7Descrição! 9Estudo da Complexidade! 13Conclusão! 15Bibliografia! 16

Page 3: Listas encadeadas, duplamente encadeadas com cabeça

1 -Introdução!!

! Este trabalho consiste em utilizar os conceitos de listas para a solução de um problema, utilizando prioridades. Fala sobre lista, TAD, alocação de memória e entre outros assuntos.

! - Ambientes de desenvolvimento do código fonte: Xcode, e codblocks.! - Compilador utilizado: gcc version 4.7.1 (GCC).! - Linguagem utilizada: Linguagem C.! - Ambiente de desenvolvimento da documentação: LibreOffice Writter.! - Na análise da ordem de complexidade dos algoritmos será analisado o pior caso, de acordo com a definição assintótica de Big O.

! A Lista Encadeada diz que, cada nó (ou célula) deve conter um item da lista e um “campo extra” para um apontador para o nó seguinte. ! A alocação dinâmica de memória é a técnica utilizada: as posições de memória são alocadas (desalocadas) quando são necessárias (desnecessárias). !! Como visto, C faz a gerência de memória através das declarações malloc e free.!! Existem outras implementações: simplesmente ou duplamente encadeadas e listas encadeadas circulares; com ou sem nós sentinela.!! As desvantagens na utilização desse tipo de lista são:! ! - Acesso indireto aos elementos! ! - Tempo variável para acessar os elementos (depende da posição do elemento) ! ! - Gasto de memória maior pela necessidade de um novo campo para o ponteiro! E possui também as vantagens :! ! - A inserção e remoção de elementos podem ser feitas sem deslocar os itens seguintes da lista! ! - Não há necessidade de previsão do número de elementos da lista; o espaço necessário é alocado em tempo de execução! ! - Facilita o gerenciamento de várias listas (fusão, divisão,...)!! A Lista Simplesmente Encadeada deve existir sempre uma indicação do primeiro nó, a partir dele a lista é percorrida. Então, seria necessário um cuidado especial no tratamento do primeiro nó da lista, tem que ser feito testes em algoritmos de inserção e remoção para verificar se este ponteiro é ou não igual a NULL, uma pequena variação na estrutura, com a adição de um nó chamado nó-cabeça, evita alguns testes com o 1° nó e melhora o desempenho das operações na lista. O nó-cabeça não contém informações relacionadas aos dados da lista e nunca é removido.

Page 4: Listas encadeadas, duplamente encadeadas com cabeça

Figura 1 – Lista Encadeada com nó cabeça.

! As listas duplamente encadeadas são estruturas de dados semelhantes às listas simplesmente encadeadas pois a alocação da memória é feita durante a execução. No entanto, em comparação com as listas simplesmente encadeadas a conexão entre os elementos é feita através de dois ponteiros (um que aponta para o elemento anterior, e o outro, para o seguinte). 

O ponteiro anterior ao primeiro elemento deve apontar para NULL (o início da lista).  O ponteiro seguinte ao último elemento deve apontar para NULL (o fim da lista). Para acessar um elemento, a lista pode ser percorrida pelos dois lados:! *Começando do alto, o ponteiro "seguinte" permite o deslocamento para o elemento seguinte.! *Começando do final, o ponteiro "anterior" permite o deslocamento para o elemento anterior.

! Em suma, o movimento é feito em ambas as direções, do primeiro para o último elemento e/ou do último para o primeiro. 

2 - Implementação! Numa lista encadeada, para cada novo elemento inserido na estrutura, alocamos um espaço de memória para armazená-lo. Desta forma, o espaço total de memória gasto pela estrutura é proporcional ao número de elementos nela armazenado. No entanto, não podemos garantir que os elementos armazenados na lista ocuparão um espaço de memória contíguo, portanto não temos acesso direto aos elementos da lista. Para que seja possível percorrer todos os elementos da lista, devemos explicitamente guardar o encadeamento dos elementos, o que é feito armazenando-se, junto com a informação de cada elemento, um ponteiro para o próximo elemento da lista.• Cabeça de Lista

1.Necessitamos:! ! oUm ponteiro para o primeiro elemento da lista.! ! oUm inteiro para indicar quantos elementos a lista possue.•Pseudo-código:! tipo Lista {     ! Elemento *dados;     ! inteiro tamanho; ! };

•Elemento de Lista1.Necessitamos:

! oUm ponteiro para o próximo elemento da lista.! oUm campo do tipo da informação que vamos armazenar.

•Pseudo-código:  ! tipo Elemento {     ! Elemento *próximo;     ! tipo-que-eu-vou-usar-nesta-aplicação info; ! };

Page 5: Listas encadeadas, duplamente encadeadas com cabeça

! Um TAD define um novo tipo de dado e o conjunto de operações para manipular dados desse tipo, e um TAD facilita a manutenção e a reutilização de código.! A Interface de um TAD define o nome do tipo, e os nomes das funções exportadas, ou seja, os nomes das funções devem ser prefixadas pelo nome do tipo, evitando conflitos quando tipos distintos são usados em conjunto.

! Implementação de um TAD, o arquivo implementado deve incluir o arquivo de interface do TAD, que permite utilizar as definições da interface, que são necessárias na implementação e garante que as funções implementadas correspondem as funções da interface. E deve incluir as variáveis globais e funções auxiliares, que devem ser declaradas como estáticas, e visíveis apenas dentro do arquivo de implementação.

3 – Descrição

Lista dinâmica encadeada com cabeça:! Lista dinâmica é um tipo de lista que define a cada passo um espaço de memória para seu elemento.

! Lista Dinâmica Encadeada (ou Lista Dinâmica simplismente ligada) é um Tipo de “Lista” onde cada elemento aponta para o seu sucessor na “lista” .Usa-se um ponteiro especial para o primeiro elemento da lista (cabeça da lista) e uma indicação de final de lista.

! Como visto na figura acima, tem um ponteiro especial para o primeiro elemento da lista, e cada elemento tem um ponteiro que aponta para um elemento da lista, sendo o ultimo elemento apontando para NULL, ou seja apontando para algo inexistente.

! A estrutura da lista conforme observado, possui um dado, e um ponteiro que aponte para seu próximo elemento.! O dado é a informação que eu quero armazenar dentro da lista e sendo o prox, o elemento que vem depois de mim na lista.

•Considerações:(I)Cada elemento da lista é tratado como um ponteiro que é alocado dinamicamente , a medida que os dados são inseridos.

Page 6: Listas encadeadas, duplamente encadeadas com cabeça

II.Para guardar o primeiro elemento, em uma lista com cabeça, utilizamos um “ponteiro para ponteiro”, pois um “ponteiro para ponteiro” pode guardar o endereço de um “ponteiro”.

!! Na figura acima, eu tenho três elementos na lista, quando aloquei o 33, reservei o espaço de memória para o dado 33, quando aloquei o 23, aloquei o espaço de memória para ele e seu respectivo ponteiro, assim, sucessivamente.

! Caso eu quisesse alocar outro número depois do 16, eu teria que alocar um outro espaço de memória para ele, e em seguida, fazer com que ele apontasse para NULL.!! O primeiro elemento, possui um ponteiro especial, que aponta para o primeiro elemento, o que chamamos de ponteiro para ponteiro, dessa forma eu consigo mudar o início da lista, ou seja, eu posso mudar o conteúdo inicial da lista com cabeça, que também é um ponteiro. Com o “ponteiro para ponteiro”, posso muda o conteúdo do primeiro elemento da lista.

•Vantagens em relação a uma lista sequencial estática:I.Melhor utilização dos recursos de memória, pois é definido o que se quer de memória a cada passo.

II.Não precisa fazer operação de movimentação dos elementos nas operações de remoção e inserção.

! Na figura abaixo um exemplo de inserção na lista dinâmica encadeada, fazendo alterações apenas nos ponteiros.

Page 7: Listas encadeadas, duplamente encadeadas com cabeça

•Desvantagens em relação a lista sequencial estática:I.Acesso indireto aos elementos;II.Necessidade de percorrer a lista para acessar determinado elemento.

! Na lista sequencial estática poderíamos acessar o elemento diretamente pelo índice, porém, na lista sequencial dinâmica, deve-se percorrer toda lista, conforme a figura acima.

•Aplicação:I.Quando não houver necessidade de garantir um espaço mínimo para a execução do aplicativo, ou seja eu não souber quanto de memória eu irei utilizar.

II.Inserção/remoção em lista ordenada são operações mais frequêntes, ou seja quando houver muitos deslocamentos em uma lista.

•Implementação:I.No “ListaDinEncCab.h” foi definido:

Os protótipos das funções;O tipo de dado armazenado na lista;O ponteiro "Lista", pois como iremos trabalhar com lista com cabeça, este

será o “ponteiro para ponteiro para modificar seu primeiro elemento. !

II.No “ListaDinEncCab.c” foi definido:O tipo de dados "Lista";Implementado suas funções, definidas no arquivo.h.

•Inserção em uma lista simplesmente encadeada com cabeça:I.Existem 3 tipos de inserção em uma lista:

! ! -Início;! ! -Meio;! ! -Fim;

Segue-se o exemplo na próxima figura:

Page 8: Listas encadeadas, duplamente encadeadas com cabeça

! Também existe o conceito de inserção em lista vazia, onde a inserção é feita, em uma lista que está vazia.

! Como na figura acima, quando criamos uma lista, ela está vazia e a cabeça aponta para NULL.

! Para inserir o novo nó, ele aponta a ser para o próximo NULL e a cabeça aponta para o novo nó.! Esse é o código básico para todas as inserções.

Page 9: Listas encadeadas, duplamente encadeadas com cabeça

Função que insere no início da lista, esta estando vazia ou não.

Figura exemplificando inserção no final de uma lista

•Inserindo um elemento de forma ordenada ou um elemento qualquer:

! A inserção em uma lista ordenada, pode ser no início no meio ou no final da mesma, sendo assim, deve-se tratar os 3 casos de uma só vez.

Page 10: Listas encadeadas, duplamente encadeadas com cabeça

!! Na figura acima podemos ver no campo “Busca onde inserir”, temos os elementos Cabeça que aponta para o atual, sendo que depois isso se inverte, o elemento cabeça passa a ser o atual e o atual passa a ser o anterior, ate que se chegue ao final da lista, ou encontre o ponto onde deve ser feito a inserção.

! Uma dessas opções será verdadeira em algum momento, ou chego no final da lista, ou encontro o ponto de inserção.

! No segundo caso, “Inserir no início”, estou inserindo um elemento antes do primeiro elemento, um exemplo de lista ordenada.

! Se não for inserido no início, se for falso, eu estarei inserindo no meio da lista ou no final dela.

! Nesse caso, eu terei o nó anterior, que é “inserir depois de ant”, e este apontando para NULL, e o anterior aponta para o atual, sendo que o ant pode ser o último nó “inserir depois de ant”.

•Remoção em uma lista encadeada com cabeça:!! Existem 3 tipos básicos de remoção”, seguindo-se a mesma linha de raciocínio da inserção:! -Início;! -Meio: Este tipo de remoção geralmente é utilizada, quando se quer remover um determinado elemento.! -Fim;

Page 11: Listas encadeadas, duplamente encadeadas com cabeça

•Remoção no início:

De acordo com a figura acima, deve-se desalocar a memória da primeira célula, e informar para onde a cabeça aponta para o novo início da lista.

•Remoção no meio:

! ! No exemplo acima, o 33 apontava para o 12, depois de se remover o 12, o elemento anterior vai apontar para o próximo.

Page 12: Listas encadeadas, duplamente encadeadas com cabeça

•Remoção no fim:

! Observa-se que deve se percorrer toda a lista, até chegar no último elemento dela, então remove-se o último elemento, e o elemento anterior, passa a apontar para o NULL, e o anterior ao último, passa a ser o último elemento da lista.

•Considerações:I.Os 3 tipos de remoção trabalham juntos. A remoção sempre remove um elemento específico da lista, o qual pode estar no início, no meio ou no final da lista, porem, deve-se atentar também aos seguintes cuidados:II.Não se pode remover um elemento de uma lista vazia;III.Removendo o último nó, a lista fica vazia.

! No exemplo acima, não tem como, ou seja, não existe a possibilidade de remover um elemento que não existe, que esteja apontando para NULL.! E se uma lista possui um único nó e eu remover este nó a lista irá se tornar vazia.

Page 13: Listas encadeadas, duplamente encadeadas com cabeça

Exemplo de remoção no início da lista:

! Na figura acima foi representado o primeiro elemento da lista sendo este elemento o elemento nó. A cabeça da lista passa apontar para o elemento seguinte ao elemento no, e em seguida é liberado o nó.

! Também pode-se observar, que no caso de uma lista com um único elemento, ao remover esse elemento, a cabeça da lista passa apontar para vazio, indicando que a lista se torna vazia.

Exemplo de remoção no final da lista:

! Este tipo de remoção é bem parecido com a remoção de ínicio da lista, sendo a principal diferença é que se deve percorrer primeiramente toda a lista.

! Na figura acima, vemos um código que só seria finalizado quando o determinado elemento estiver apontando para NULL, que é o ultimo elemento da lista.

! Também podemos observar se o no é o único elemento da lista, e ele é removido, a cabeça aponta para NULL indicando que esta é uma lista vazia.

Page 14: Listas encadeadas, duplamente encadeadas com cabeça

Exemplo de remoção de um elemento qualquer:

! Na figura acima exemplificando a remoção de um elemento qualquer.!! Primeiramente deve se “Buscar qual remover”;!! Depois de procurar, se o elemento a ser removido for no início, basta mudar o início da lista, ou seja o início passa a apontar para o próximo.

! Se não for o primeiro, o anterior que apontava para o próximo a ele, vai passar a apontar para o no que aponta para o próximo.

! Se for removido o ultimo elemento, o anterior vai passar a apontar para NULL, ou seja, o último elemento da lista, em seguida liberando o nó da memória.

! Se for removido um elemento intermediário, o elemento anterior, vai passar a apontar para onde o no anterior apontava, em seguida deve-se liberar o nó.

Page 15: Listas encadeadas, duplamente encadeadas com cabeça

•Consulta/imprimir elementos de uma lista dinâmica simplesmente encadeada:

! Quando temos uma lista, e queremos retornar determinado(s) elemento(s) desta lista, sendo que basicamente duas maneira de consultar um elemento de uma lista:

- Pela posição;- Pelo conteúdo.

!! Ambos depende de busca (percorrer os elementos) até encontrar o desejado.

! Na figura acima, exemplifica-se a busca por posição e conteúdo.! Na busca por posição eu queria o terceiro elemento da lista, nesse caso deve-se percorrer a lista até achar o elemento que se quer, nesse caso retorna o valor 16.! Já na busca por conteúdo, eu quero o elemento de valor 33, nesse caso a cabeça já esta apontando logo para ele.

Page 16: Listas encadeadas, duplamente encadeadas com cabeça

Lista dinâmica duplamente encadeada com cabeça:

! É um tipo especial da lista encadeada, ou seja, é um tipo de “Lista” onde cada elemento aponta para o seu sucessor e antecessor na “lista”.

! Ela usa um ponteiro especial para o primeiro elemento da lista (cabeça) e uma indicação de final de lista, nos dois sentidos.

!! Como na figura acima, podemos observar os dois ponteiros, em cada nó sendo um apontando para o sucessor e outro apontando para o antecessor do nó na lista.! Todo elemento da lista tem que ter o próximo elemento, o anterior, e o dado.

! Cada elemento é tratado como um ponteiro que é alocado dinamicamente, a medida que os dados são inseridos.! Para guardar o primeiro elemento, utilizamos um “ponteiro para ponteiro”, podendo guardar um endereço de ponteiro, facilitando mudar quem está no ínicio da lista, apenas mudando o conteúdo de “ponteiro para ponteiro”

•Vantagens:! -Melhor utilização dos recursos de memória;! -Não precisa movimentar os elementos nas operações de inserção e remoção.

Page 17: Listas encadeadas, duplamente encadeadas com cabeça

Na figura anterior, para inserir o valor 12, basta modificar os ponteiros nos quais apontam os valores.

•Desvantagens:! -Acesso indireto aos elementos;! -Necessidade de percorrer a lista para acessar um elemento.

! Na figura acima exemplifica o acesso ao elemento 16, sempre partindo do início da lista, tendo que passar por 33, 23 só depois chegar ao 16, sem a possibilidade de acesso direto, percorrendo a lista.

•Utilização da lista duplamente encadeada:! -Quando não houver necessidade de garantir um espaço mínimo de execução do aplicativo;! -Inserir/remover em uma lista ordenada são as operações mais frequentes;! -E tiver a necessidade de acessar informação de um elemento antecessor, neste caso, não sendo possível na lista simplesmente encadeada.

•Criando a lista:

Na figura acima, podemos ver que o conteúdo *li, aponta para o início e não tem nenhum nó dentro da lista, sendo este em si o primeiro e único nó na lista.

Page 18: Listas encadeadas, duplamente encadeadas com cabeça

•Verificar se a lista está vaziaLista vazia:

Podemos ter uma lista vazia ou não, como por exemplo, ao criar uma lista, esta estará vazia, só deixando de ser vazia ao ser inserido elementos.

Na figura acima exemplifica o conteúdo de uma lista apontando para NULL.

•Retornar o tamanho de uma lista duplamente encadeada:

! (I).Na figura acima o *li representa a cabeça da lista, sendo que o conteúdo de li, é o primeiro elemento, sendo que foi declarada a variável cont e o nó recebe o primeiro elemento.! (II). Na segunda parte o valor contou mais um (cont++), e entrei para o próximo elemento.! (III). Na terceira parte, cheguei a um próximo elemento e contei mais um (cont++), o cont passou a valer 2, e foi ao próximo elemento.! (IV). No final chegou ao NULL, indicando o fim da quantidade de elementos da lista.

Page 19: Listas encadeadas, duplamente encadeadas com cabeça

•Inserção na lista duplamente encadeada:! Existem 3 tipos de inserção na lista duplamente encadeada:! ! -Inicio;! ! -No meio;

- Ou no final.

Na figura acima podemos ver o valor 12 sendo inserido inicialmente no inicio, no segundo exemplo, sendo inserido no meio, e no ultimo exemplo no final.

I.Inserindo em uma lista vazia:

! Também existe o caso onde a inserção é feita em uma “lista” que está vazia.! A inserção é parecida com a da “Lista Dinâmica Encadeada”. Deve-se apenas considerar que agora temos dois ponteiros para atualizar: o anterior e o próximo.

Page 20: Listas encadeadas, duplamente encadeadas com cabeça

! No exemplo acima, vemos uma lista que anteriormente a cabeça apontava para NULL, porém agora ela aponta para um novo elemento, o “33”, ou seja, o novo nó recebe os dados, os ponteiro próximos e anteriores recebem NULL, e por fim *li permanece no início da lista, pois esse ponteiro é a cabeça da lista.

II.Inserindo em uma lista vazia:!

! Na figura acima podemos observar que na parte superior, temos uma lista, e na segunda parte a implementação na qual foram copiados os dados e em seguida o prximo aponta para os dados *Li, o próximo do *LI aponta para onde estava apontando anteriormente que era o inicio da lista *Li, e o anterior aponta-se para NULL.! Em seguida foi verificado se a lista estava vazia, caso negativo, *LI apontava para o anterior que apontava para o nó, em seguida mudando o primeiro elemento da lista.

III.Inserindo no final de uma lista:

Page 21: Listas encadeadas, duplamente encadeadas com cabeça

A figura anterior representa a inserção de um elemento no final de uma lista duplamente encadeada com cabeça.

Primeiramente tem-se uma lista, considerando que a lista não esta vazia, em seguida, buscando onde inserir, então uso o while, para procurar até onde está o elemento null, usando o aux para ajudar achar o elemento nulo.

Após achar os elementos, faz-se a inserção copiando os dados, fazendo com que o próximo recebe nulo, o auxiliar aponta para a próxima que recebe o nó, e o nó anterior, aponta para aux.

4 - Estudo da complexidade Análise de Algoritmos é a área da computação que visa determinar a complexidade (custo) de um algoritmo, o que torna possível: • Comparar algoritmos Determinar se um algoritmo é “ótimo”.

Custo de um algoritmo: • Tempo (número de passos) Espaço (memória) A complexidade de um algoritmo é medida segundo um modelo matemático que supõe que este vai trabalhar sobre uma entrada (massa de dados) de tamanho N.

A complexidade pode ser qualificada quanto ao seu comportamento como: .Polinomial : A medida que N aumenta o fator que estiver sendo analisado (tempo ou espaço) aumenta linearmente.

• Exponencial A medida que N aumenta o fator que estiver sendo analisado (tempo ou espaço) aumenta exponencialmente. Algoritmo com complexidade exponencial, não é executável para valores de N muito grandes.

A notação O é utilizada para expressar comparativamente o crescimento assintótico (velocidade com que tende a infinito) de duas funções. Por definição, f = O(g) se existe uma constante c > 0 e um valor n0 tal que n > n0 ⇒ f(n) ≤ c * g(n), ou seja, g atua como limite superior para valores assintóticos da função f.Complexidade exponencial, típicos de algoritmos que fazem busca exaustiva (força bruta) para resolver um problema, não são úteis do ponto de vista prático.

A função que verifica se uma lista é vazia, possui 1 comparação. Então, obtemos a função de complexidade para a função: O(n) = 1 . A ordem de complexidade da função é O (f(n)), ou seja, O(1).

A função que insere lista no inicio, possui 4 comparações e 4 comandos. Obtemos a complexidade para a função, O(n) = 8. Então a ordem de complexidade da função é O (f(n)), ou seja, O(1).

Page 22: Listas encadeadas, duplamente encadeadas com cabeça

A função que insere lista no final, possui 6 comparações e 8 comandos. Obtemos a complexidade para a função, 0(n)=14. Então a ordem de complexidade da função é O (f(n)), ou seja, O(1). A função Insere Lista Ordenada, possui 10 comparações e 11 comandos. Obtemos a complexidade para a função, igual a função anterior. Então a ordem de complexidade da função é O (f(n)), ou seja, O(1).

A função remove lista do inicio, possui no pior caso 3 comparações e 2 comandos. Obtemos então, a função de complexidade para a função: O(n) = 5. Resolvendo obtemos: f(n) = 5, então a ordem de complexidade da função é O (f(n)), ou seja, O(1).

A função remove lista do final, possui 6 comparações e 5 comandos. Obtemos a complexidade para a função, O(n)=11, ou seja, O(f(n)) = O(1).

A função Remove_Lista, possui 8 comparações e 4 comandos, ou seja, O(f(n)) = O(f(12)) = 0(1).

A função remove_listaq, possui no pior caso 5 comparações e 4 comandos. Obtemos então, a função de complexidade para a função: 0(n) = 9. Resolvendo obtemos, 0(f(n)) = 0(f(9)) = 0(1).

A funçao imprimirLista, possui 4 comparações e 3 comandos, ou seja f(n) = 7. Então 0(1).

A funçao consulta_lista_mat, possui no pior caso 3 comparações e 4 comandos. Obtemos então, a função de complexidade para a função: O(n) = 9. Resolvendo obtemos: f(n) = 9, então a ordem de complexidade da função é O (f(n)), ou seja, O(1).

A função lista_vazia, possui no pior caso 2 comparações e 3 comandos. Obtemos então, a função de complexidade para a função: O(n) = 5. Resolvendo obtemos: f(n) = 5, então a ordem de complexidade da função é O (f(n)), ou seja, O(1).

A função tamanho_lista, possui no pior caso 3 comparações e 4 comandos. Obtemos então, a função de complexidade para a função: O(n) = 7. Resolvendo obtemos: f(n) = 7, então a ordem de complexidade da função é O (f(n)), ou seja, O(1).

A função que simula o programa, é f(n) = n, pois é o Max(O(n), O(n), O(n), O(n), ... O(n) ), então a ordem de complexidade é O (f(n)), ou seja, O(n).

Page 23: Listas encadeadas, duplamente encadeadas com cabeça

5 - Conclusão Este trabalho possibilitou um aprofundamento maior sobre a linguagem C, também na revisão e fixação de conceitos sobre listas, ponteiros e alocação de memória.

Ajudou no aprendizado de listas implementadas por encadeamento simples e por encadeamento duplo, fazendo-se o uso de apontadores, TAD e estruturação em C.

As dificuldades encontradas foram pouco tempo para implementação de tal atividade, devido ao número de trabalhos e provas de outras disciplinas, bem como a necessidade de uso de apontadores, sendo que tal utilização ainda não esta bem fixa, mas apesar de ter adquirido conhecimento entre vários tipos de filas, infelizmente o programa não foi concluído com sucesso.

As duvidas foram resolvidas com o auxilio da bibliografia citada e com a ajuda de alguns professores.

Page 24: Listas encadeadas, duplamente encadeadas com cabeça

6 – Bibliografia

[1] N. Ziviani. Projeto de Algoritmos: com implementações em Pascal e C. Cengage Learning (Thomson / Pioneira), São Paulo, 2nd edition, 2004.[2] David Menotti. CIC102 - Algoritmos e Estruturas de Dados 1, Outubro 2009.[3] http://www.univasf.edu.br/~marcelo.linder/arquivos_ed1/aulas/aula15.pdf[4]A. Backes. Linguagem C completa e descomplicada, ELSEVIER.

Page 25: Listas encadeadas, duplamente encadeadas com cabeça

7 – Código Fonte

•Main.c://// main.c// Lista Dinâmica encadeada com Cabeca//// Created by Fabrício Daniel Freitas on 19/09/13.// Copyright (c) 2013 Fabrício Daniel Freitas 0002752 - Valquíria Soares 0002785. All rights reserved.//

#include <stdio.h>#include "ListaDinEncCab.h"#include <stdlib.h>

int main(){ Lista *li; /*Declarei meu ponteiro do tipo Lista, ou seja, o elemento* Lista declarado no meu arquivo.h, já é um ponteiro, ou seja, o *li do tipo Lista, é um ponteiro para ponteiro. \ da struct elemento. Cada um dos nós da minha lista, será um único ponteiro, exceto o inicio da lista que é um ponteiro para ponteiro, por que o ponteiro para ponteiro, consegue guardar nele um endereço de um ponteiro, ou seja, esse sera a cabeça da lista.*/ lista *Li;//ponteiro para ponteiro void menu();//menu de opçoes return 0;}

Page 26: Listas encadeadas, duplamente encadeadas com cabeça

ListaDinEncCab.c://// ListaDinEncCab.c// Lista Dinâmica encadeada com Cabeca//// Created by Fabrício Daniel Freitas on 20/09/13.// Copyright (c) 2013 Fabrício Daniel Freitas 0002752 - Valquíria Soares 0002785. All rights reserved.///*ListaDinEncCab.c: Definir - O tipo de dados "Lista"; - implemetar suas funções */

#include <stdio.h>#include <stdlib.h>//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------

struct elemento{ struct caracter dados; //uma variavel dados do tipo estrutura de dados quaisqer struct elemento *prox; // um ponteiro que aponta para a proxima estrutura};

typedef struct elemento Elem; //facilita na operação//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------

Lista* cria_lista(){ //Função para criar a lista. Lista* li = (Lista*) malloc(sizeof(Lista)); //Malloc para guardar o primeiro nó da lista, ou seja a cabça da lista if (li != NULL) { //Se deu certo a criação do nó cabeça *li = NULL; //Preencheu o conteúdo com NULL, ou seja apontando para nada } return li; //Daí retorna a lista vazia.}

//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int insere_lista_inicio(Lista* li, struct caracter al){ if (li == NULL) return 0; //Primeiro verificar se a lista é váida, ou seja se ela existe, se não existir retorna 0, caso a lista exista Elem* no = (Elem*) malloc (sizeof(Elem));//Eu crio o elemento Elem* no, faço o malloc e o sizeof elemento

Page 27: Listas encadeadas, duplamente encadeadas com cabeça

if (no == NULL) return 0; //Se o nó retornar nulo, quer dizer que não conseguir alocar memória para esse novo nó, porém caso eu consiga alocar espaço de memória no -> dados = al; //Os dados al serão copiados como parâmetro para dentro do nó no -> prox (*li);//Vou informar que o próximo item é a própria lista, isso resolvendo todos os casos se a lista for vazia ou não *li = no; //A cabeça da lista, passa apontar para um novo nó que foi inserido return 1; //retorna 1, indicando que foi inserido o item no inicio da lista}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------

int insere_lista_final (Lista* li, struct caracter al){ if (li == NULL) return 0; //Primeiro verificar se a lista é válida, ou seja se ela existe, se não existir retorna 0, caso a lista exista Elem* no (Elem*) malloc (sizeof(Elem)); //Eu crio o elemento Elem* no, faço o malloc e o sizeof elemento novo nó, porém caso eu consiga alocar espaço de memória if (no == NULL) return 0; //Se o nó retornar nulo, que dizer que não consegui alocar memória para esse novo nó, porém caso eu consiga alocar espaço de memória no-> dados = al; //Os dados al serão copiados como parâmentro para dentro de nó no -> prox = NULL;//Como será inserido no final da lista, eu devo dizer que o próximo elemento após esse nó, é o NULL, pois no final da lista eu só tenho o NULL if ((*li) == NULL) { //Se eu estiver inserindo em uma lista vazia, neste caso, eu digo que o inicio da lista *li = no;//é o novo nó }else{ //senão, eu percorro toda a lista Elem *aux = *li; //crio um ponteiro auxiliar, dizendo que ele tem o valor do ínicio da lista, pois não se deve percorrer a lista com a própria cabeça e sim com um auxiliar while (aux -> prox != NULL) {// enquanto o próximo elemento for diferente do ultimo valor, ou seja NULL aux = aux -> prox;//O auxiliar vai receber o nó seguinte a ele } //até achar o último nó aux -> prox = no; //Achou o ultimo nó, ele vai receber o elemento a ser inserido } return 1; //retorna 1 indicando que a inserção no final foi feita corretamente}

//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int insere_lista_ordenada(Lista* li, struct caracter al){

Page 28: Listas encadeadas, duplamente encadeadas com cabeça

if (li == NULL) return 0;//Verifico se a lista é valida, e crio um elemento Elem *no = (Elem*) malloc(sizeof(Elem));//Aloco memória para o elemento criado if (no == NULL) return 0;//Verifico se foi alocado o elemento no -> dados = al;//verifico se a lista é vazia, copio os dados para dentro dele if (lista_vazia(li)) { //insere no inicio da lista no -> prox (*li); //Se a lista for vazia, o nó aponta para o próximo que recebe o li return 1;//retorna 1 finalizando a função dizendo que deu certo } else{//procura onde inserir o elemento caso a lista não seja vazia Elem *ant, *atual = *li;//crio dois ponteiros auxiliares o atual apontando para a cabeça da lista e um anterior while(atual != NULL && atual -> dados.dado_caracter != 'q'){//enquanto o atual for diferente de nulo, e enquanto o elemento estiver entre atual e outro ant = atual; //anterior passa valer o atual e atual = atual -> prox;//o atual passa a valer o próximo elemento } if (atual == *li){//se o elemento atual for no início da lista no -> prox = (*li);//se for, quer dizer que ele não andou em nenhum lugar, e deve ser inserido no início da lista, o nó prox, aponta para o ínicio da lista *li = no; //ínicio da lista aponta para o próximo nó }else{ no -> prox = ant -> prox; //Nó prox, aponta para onde o nó anterior estava apontando ant->prox = no; //o anterior aponta para o nó, sendo que este antes, apontava para NULL } } return 1;}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------

int remove_lista_inicio(Lista* li){ //função para remover no inicio if (li == NULL) //verifico se a lista existe return 0;//caso a lista não exista, retorna falso, ou seja 0 if ((*li) == NULL) //verifica se a lista é vazia, se o conteudo inicial da lista é NULL return 0; //retorna falso Elem *no = *li; //Se for uma lista válida, ou seja passou pelos testes acima, inicia criando uma auxiliar com o nome *no, que recebe o valor da cabeça da lista *li = no->prox; free(no); return 1;

Page 29: Listas encadeadas, duplamente encadeadas com cabeça

}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int remove_lista_final(Lista* li){ if (li == NULL) //verifico se a lista existe return 0;//caso a lista não exista, retorna falso, ou seja 0 if ((*li) == NULL) //verifica se a lista é vazia, se o conteudo inicial da lista é NULL return 0; //retorna falso Elem *ant, *no = *li; //percorrer a lista até o final dela, crio duas auxiliares, ant e no while (no -> prox != NULL){ //enquanto o no que aponta para o proximo for diferente de vazio ant = no;//o anterior recebe o no no = no->prox; //no recebe o elemento seguinte a ele } if (no == (*li))// depois de percorrer ve se o nó é igual o inicio da lista , caso afirmativo remover primeiro elemento? ou seja a lista tem somente 1 elemento *li = no->prox; //entao o inicio da lista aponta para o proximo, se ele for o unico elemento da lista ele vai apontar para nulo else //caso não seja o primeiro da lista, ant->prox = no->prox; //o anterior vai apontar para onde aponta o no free(no);//libera o no return 1;//retorna 1 indicando que tudo ocorreu certo}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int remove_lista(Lista* li, char carac){ //remove um elemento qualquer da lista if (li == NULL) //verifico se minha lista é valida return 0;//se não for valida retorna falso (0) Elem *ant, *no = li; //crio duas auxiliares, sendo que nó recebe a copia da cabeça li while (no != NULL && no -> dados.dado_caracter){ //percorro toda a lista procurando o elemento encontrado ant = no; //anterior passa a ser o nó no = no-> prox;//nó passa a ser o elemento seguinte a ele }//a lista só vai parar quando chegar no final da lista ou quando encontrar o elemento pedido if (no == NULL) //testa para ver se a cabeça é igual a null, se for, ou tinha um vazio desde o começo ou percorri toda a lista e não encontrei o elemento return 0; // retorna não encontrado ou lista vazia if(no == *li) //se passou a condição anterior, tem que se achar on o elemento esta que pode ser no inicio da lista, se for remover o primeiro elemento *li = no->prox; //basta mudar a cabeça da lista else//se for um elemento intermediario ou final

Page 30: Listas encadeadas, duplamente encadeadas com cabeça

ant -> prox = no -> prox; // anterior que aponta para o proximo, vai receber o nó que aponta para o próximo free (no); //liberar o espaço de memoria do nó return 1; //retornar 1 indicando que tudo ocorreu certo}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int remove_listaq(Lista* li, char carac){ //remove um elemento qualquer da lista if (li == NULL) //verifico se minha lista é valida return 0;//se não for valida retorna falso (0) Elem *ant, *no = li; //crio duas auxiliares, sendo que nó recebe a copia da cabeça li while (no != NULL && no -> 'q'){ //percorro toda a lista procurando o elemento encontrado ant = no; //anterior passa a ser o nó no = no-> prox;//nó passa a ser o elemento seguinte a ele }//a lista só vai parar quando chegar no final da lista ou quando encontrar o elemento pedido if (no == NULL) //testa para ver se a cabeça é igual a null, se for, ou tinha um vazio desde o começo ou percorri toda a lista e não encontrei o elemento return 0; // retorna não encontrado ou lista vazia if(no == *li) //se passou a condição anterior, tem que se achar on o elemento esta que pode ser no inicio da lista, se for remover o primeiro elemento *li = no->prox; //basta mudar a cabeça da lista else//se for um elemento intermediario ou final ant -> prox = no -> prox; // anterior que aponta para o proximo, vai receber o nó que aponta para o próximo free (no); //liberar o espaço de memoria do nó return 1; //retornar 1 indicando que tudo ocorreu certo}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int imprimirLista(Lista* li, struct caracter *al){ f (li == NULL || pos <= 0) //se a lista é valida return 0; Elem *no = *li;//Ponteiro auxiliar apontado para inicio da lista while (no != NULL){//enquanto o no for diferente de NULL char algo = 'no'; //algum elemento printf("%c",algo); no = no->prox;//entao vou para o proximo elemento } return 1; //retorna 1 dizendo que deu certo a tarefa de busca na lista}//--------------------------------------//--------------------------------------//-------------------//-------

Page 31: Listas encadeadas, duplamente encadeadas com cabeça

//--------------------------------------//--------------------------------------//-------------------//-------int consulta_lista_mat(Lista* li, char carac, struct caracter *al){ if (li == NULL || pos <= 0) //se a lista é valida return 0; Elem *no = *li;//Ponteiro auxiliar apontado para inicio da lista while (no != NULL && no->dados.dado_caracter != mat){//enquanto o no for diferente de NULL e tambem for diferente do procurado no = no->prox;//entao vou para o proximo elemento } if (no == NULL) //se a lista é vazia ou percorri toda a lista e nao encotrei o dado return 0;// ja retorna o elemento 0 dizendo que nao tem como achar o elemento else{ *al = no -> dados;//seanao o conteudo do ponteiro recebera o valor dos dados em no return 1; //retorna 1 dizendo que deu certo a tarefa de busca na lista }}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int lista_vazia (Lista *li){ //Implementação da lista vazia if (li == NULL) //Se retornar NULL quer dizer que ela ta vazia return 1; //Então é verdadeiro retorna 1 if (*li == NULL)//Se a cabeça da lista aponta direto para NULL return 1; //Não tem conteúdo dentro da lista ou seja, ela está vazia return 0;//Se eu passei por todos esses if's e não retornou 1 ainda, então retorna 0, indicando que a lista não está vazia.}//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------int tamanho_lista(Lista* li){ //Retorna o tamanho da lista em inteiro if (li == NULL) return 0; // Verifica se a lista é válida, caso não seja, retorna 0 ou falso int cont = 0; //Cria uma variavel contadora Elem* no = li; //Cria um nó auxiliar que recebe o primeiro elemento da lista while (no != NULL) { //Enquanto o nó for diferente de NULL vai apontar para o próximo cont ++; //Vai contar no = no-> prox; // E apontar para o próximo } // O nó auxiliar é criado, pois não se pode andar com a cabeça da lista return cont; //Retornar o tamanho da lista em inteiro}//--------------------------------------//--------------------------------------//-------------------//-------

Page 32: Listas encadeadas, duplamente encadeadas com cabeça

//--------------------------------------//--------------------------------------//-------------------//-------void menu_simplismente_encadeada(){ int escolha; printf"Escolha uma das seguintes opções abaixo: \n" printf"1- Criar lista\n" printf"2- Inserir no inicio da lista\n" printf"3- Inserir no final da lista\n" //printf"4- Inserir um nó com conteúdo item entre a posição apontada por q e a posicao seguinte\n" printf"5- Remover o elemento do inicio da lista\n" printf"6- Remover o elemento final da lista\n" printf"7- Remover o no com conteudo item\n" printf"8- Remover o no seguinte a um no apontado por q\n" printf"9- Imprimir a lista\n" printf"10- Verificar se um determinado item pertence a lista\n" printf"11- Verificar se a lista esta vazia\n" printf"12 - Retornar o tamanho da lista\n" scanf("%d",&escolha); //escolhe a função switch (escolha){//Se ele escolher criar lista case 1: li = cria_lista (); //chamar a função para criar a lista break; case 2: int x = insere_lista_inicio(li, dados); //retonra se deu certo ou não a inserção printf("\n%d\n",x); break; case 3: int x = insere_lista_final (li, dados); //retorna se deu certo ou não a inserção no final da lista printf("\n%d\n",x); break; /*case 4: int x = insere_lista_ordenada(li, dados); //Retorna se deu certo ou não a inserção na lista break;*/ case 5: int x = remove_lista_inicio(li); //chama a função de remover no inicio da lista printf("\n%d\n",x); break; case 6: int x = remove_lista_final(li); //chama a funcao para remover no final da lista printf("\n%d\n",x); break; case 7:

Page 33: Listas encadeadas, duplamente encadeadas com cabeça

int x = remove_lista(li, item); //chama a funcao que remove um elemento qualquer printf("\n%d\n",x); break; /*case 8: remove o q int x = remove_listaq(li, item); printf("\n%d\n",x); break;*/ case 9: int x = imprimirLista(li, &dados);//imprimir a lista printf("\n%d\n",x); break; case 10: int x = consulta_lista_mat(li, posicao, &dados);//consultar determinado dado na lista, sendo dado caracter o valor que quero saber se consta na lista printf("\n%d\n",x); break; case 11: int x = lista_vazia(Lista* li); //chama a função lista vazia printf("\n%d\n",x); break; case 12: int x = tamanho_lista(li); //chama o tamanho da lista printf("\n%d\n",x); break; default: printf("\nOpcao invalida!\n"); break; } }//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------

void menu(){ //menu de opçoes 1 int escolha; printf("Escolha 1 para Lista Simplismente encadeada ou 2 para duplamente encadeada: /n"); scanf("%d",&escolha); switch (escolha) { case 1: void menu_simplismente_encadeada();

Page 34: Listas encadeadas, duplamente encadeadas com cabeça

break; case 2: void menu_duplamente_encadeada(); default: printf("\nOpção inválida!\n") break; }}

//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------void menu_duplamente_encadeada(){ int escolha; printf"Escolha uma das seguintes opções abaixo: \n" printf"1- Criar lista\n" printf"2- Inserir um elemento no inicio da lista: \n printf"12- Saber se a lista está vazia\n"" switch (escolha) { case 1: Lista* cria_lista(); //cria lista duplamente encadeada break; case 2: int x = Insere_lista_inicio(Li, dados); //inserir inicio da lista, printf("/n%d/n",x); break; case 3: int x = Insere_lista_final(Li, dados);//inserir no final da lista duplamente encadeada printf("/n%d/n",x); break; /*case 4: int x = Insere_lista_ordenadaq(Li, dados);//inserir o q printf("/n%d/n",x); break; case 5: int x = Insere_lista_ordenadap(Li, dados);//inserir o p printf("/n%d/n",x); break;*/ case 12: int x= Lista_vazia(lista* Li); printf("\n%d\n",x); break;

Page 35: Listas encadeadas, duplamente encadeadas com cabeça

case 13: int x = Tamanho_lista(Li); printf("\n%d",x); default: break; }}

//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------struct Elemento{ //tipo de lista duplamente encadeada struct elemento *ant; //ponteiro para anterior struct aluno dados;//campo de dados struct elemento *prox;//ponteiro para o proximo};

typedef struct Elemento elem;//Redefinicao da struct elemento

Lista* cria_lista(){ lista* Li = (lista*) malloc(sizeof(lista));//reserva espaço de memória para o tipo lista e retornar o ponteiro Li if (Li != NULL) { //se não for vazio, se a alocacao deu certo *LI = NULL;//a cabeça aponta para nulo, sendo que Li é ponteiro para ponteiro } return Li;}

int Lista_vazia(lista* Li){;//protótipo da lista duplamente encadeada, a Lista_vazia recebe como parametro lista e retorna um inteiro if (Li == NULL) //quer dizer que não tenho uma lista return 1;//ou seja se a lista é vazia, entao é verdadeiro if (*Li == NULL)//Se o conteudo (a cabeça da lista) for igual a NULL não tenho nenhum elemento ainda cadastrado em minha lista return 1; //informando novamente que a lista esta vazia return 0;//se as duas condições acima são falsas, retorna 0, indicando que a lista não está vazia, sendo que a lista é valida e não estava vazia}

int Tamanho_lista(lista *Li){ //vai retornar um valor inteiro que recebe a lista como parâmetro if (Li == NULL) //se a lista for nula, quer dizer que não tenho uma lista return 0;//vai retornar zero, pois não tem tamanho int cont = 0; //inicialização de um contador para retornar um tamanho elem* No = *Li; //variavel auxiliar para recever a cabeça da lista

Page 36: Listas encadeadas, duplamente encadeadas com cabeça

while (No != NULL) { //vai percorrer a lista, contando quantos nós existem, enquanto o nó não chegar no final, ou seja, for diferente de NULL cont ++;//eu conto mais 1 No = No -> prox;//e ando o próximo elemento } return cont;//quando chegar no final da lista eu retotno o tamanho}

int Insere_lista_inicioLista(lista* Li, struct Caracter Al){//inserir elemento no inicio da lista duplamente encadeada, retorna 0 se deu erro ou 1 se deu certo if (Li == NULL)//primeiramente verifico se minha lista é válida return 0;//se for zero, eu não tenho uma lista elem* No = (elem*) malloc(sizeof(elem));// se eu tiver a lista, devo alocar espaço para um novo no if (no == NULL)//se der erro na alocacao do elemento return 0;//retorna 0, caso não tenha nenhum erro e a lista é válida No->Dados = AL;//copio os dados para o nome que criei No->prox = (*Li);//digo que o próximo nó vai apontar para o conteúdo Li No->ant = NULL;//o anterior passa a ser nulo //lista não vazia: apontar para o anterior if(*Li != NULL)//se o primeiro item da lsta for diferente de nulo (*Li)->ant = No;//entao eu não tinha uma lista vazia e o primeiro no da lista vai apontar para o anterior dele, para o No que foi inserido *Li = No;//no final faz-se a substituicao, que *li, vai receber o nó return 1;//terminou tudo isso retorna 1 para indicar que deu tudo certo }

int Insere_lista_final(lista* Li, struct Caracter Al){//recebe os parâmetros if(Li == NULL)//verifico se a lista esta vazia return 0;//se tiver indico elem *No = (elem*) malloc(sizeof(elem));//aloco meu novo nó if (No == NULL)//verifico se a alocacao funcionou return 0;//se nao funcionou retorno erro No->dados = Al;//copio os novos dados do novo no criado No->prox = NULL;//como foi inserido no final, foi alocado no final delel if ((*Li)==NULL) {//e a lista vazia devo tratar fazendo com que No->ant = NULL;//o anterior aponta para o nulo *Li = No;//e a cabeça da lista passa a ser o elemento no }else{//caso a lista não estivesse vazia elem *aux = *Li;//tenho que chegar ate o final da lista criando um ponteiro auxiliar que recebe a cabeça da lista while (aux->prox != NULL) {//enquanto ele aponta ao proximo for diferente de nulo, ou seja, não tiver chegado ao final da lista aux = aux-> prox;//sendo qe o auxiliar sempre recebe o proximo elemento

Page 37: Listas encadeadas, duplamente encadeadas com cabeça

aux->prox = No;//ao chegar no final da fila aux->prox = No;//auxiliar vai apontar para o próximo elemento que sera o no No-> ant = aux;//o novo elemento vai apontar para o ultimo elemento } return 1;//retorna 1 indicando que deu certo a inserçao } }

int Insere_lista_ordenadaq(lista* Li, struct caracter Al){ //inserir lista duplamente encadeada if (li == NULL) return 0;//Verifico se a lista é valida, e crio um elemento elem *No = (elem*) malloc(sizeof(elem));//Aloco memória para o elemento criado if (no == NULL) return 0;//Verifico se foi alocado o elemento No -> dados = Al;//verifico se a lista é vazia, copio os dados para dentro dele if (lista_vazia(Li)) { //insere no inicio da lista No -> prox = NULL; //Se a lista for vazia, o prox vai apontar para null No->ant = NULL;// e tamber o anterior tera que estar apontando para null return 1;//retorna 1 finalizando a função dizendo que deu certo } else{//procura onde inserir o elemento caso a lista não seja vazia elem *ant, *atual = *Li;//crio dois ponteiros auxiliares o atual apontando para a cabeça da lista e um anterior while(atual != NULL && atual -> dados.dado_caracter != 'q'){//enquanto o atual for diferente de nulo, e enquanto o elemento estiver entre atual e outro, ant = atual; //anterior passa valer o atual e atual = atual -> prox;//o atual passa a valer o próximo elemento } if (atual == *Li){//se o elemento atual for no início da lista no -> ant = NULL;//No apontando para anterior que aponta para nulo (*Li)->ant = No;//Ponteiro anterior aponta para no No->prox =(*Li);//proximo vai apontar para a lista *Li = No;//Lista passaa a valer no }else{//se nao for no inicio da lista entao a insercao é no meio ou no final No -> prox = ant -> prox; //Nó prox, aponta para onde o nó anterior estava apontando No->ant = ant;//no anterior, recebe o valor anterior do anterior ant->prox = No;//No ant passa apontar para o no anterior if (atual != NULL) //agora verificar se o atual for igual a nulo, eu estou no fim da lista atual->ant = No; //se for diferente, o atual recebe o anterior }

Page 38: Listas encadeadas, duplamente encadeadas com cabeça

} return 1;}

int Insere_lista_ordenadap(lista* Li, struct caracter Al){ //inserir lista duplamente encadeada if (li == NULL) return 0;//Verifico se a lista é valida, e crio um elemento elem *No = (elem*) malloc(sizeof(elem));//Aloco memória para o elemento criado if (no == NULL) return 0;//Verifico se foi alocado o elemento No -> dados = Al;//verifico se a lista é vazia, copio os dados para dentro dele if (lista_vazia(Li)) { //insere no inicio da lista No -> prox = NULL; //Se a lista for vazia, o prox vai apontar para null No->ant = NULL;// e tamber o anterior tera que estar apontando para null return 1;//retorna 1 finalizando a função dizendo que deu certo } else{//procura onde inserir o elemento caso a lista não seja vazia elem *ant, *atual = *Li;//crio dois ponteiros auxiliares o atual apontando para a cabeça da lista e um anterior while(atual != NULL && atual -> dados.dado_caracter != 'p'){//enquanto o atual for diferente de nulo, e enquanto o elemento estiver entre atual e outro, ant = atual; //anterior passa valer o atual e atual = atual -> prox;//o atual passa a valer o próximo elemento } if (atual == *Li){//se o elemento atual for no início da lista no -> ant = NULL;//No apontando para anterior que aponta para nulo (*Li)->ant = No;//Ponteiro anterior aponta para no No->prox =(*Li);//proximo vai apontar para a lista *Li = No;//Lista passaa a valer no }else{//se nao for no inicio da lista entao a insercao é no meio ou no final No -> prox = ant -> prox; //Nó prox, aponta para onde o nó anterior estava apontando No->ant = ant;//no anterior, recebe o valor anterior do anterior ant->prox = No;//No ant passa apontar para o no anterior if (atual != NULL) //agora verificar se o atual for igual a nulo, eu estou no fim da lista atual->ant = No; //se for diferente, o atual recebe o anterior } } return 1;}

Page 39: Listas encadeadas, duplamente encadeadas com cabeça

ListaDinEncCab.h:

//// ListaDinEncCab.h// Lista Dinâmica encadeada com Cabeca//// Created by Fabrício Daniel Freitas on 19/09/13.// Copyright (c) 2013 Fabrício Daniel Freitas 0002752 - Valquíria Soares 0002785. All rights reserved.///* ListaDinEncCab.h: Definir - Os protótipos das funções; - O tipo de dado armazenado na lista; - O ponteiro "Lista". */

struct caracter{ //strutura para armazenar um dado qualquer, englobando int, char e float char dado_caracter[30];};

typedef struct celula* Lista; //ponteiro definido no arquivo.ctypedef struct elemento* lista;//ponteiro definido no arquivo c

Lista* cria_lista(); //Função para criar lista, retornando Lista*

int insere_lista_inicio(Lista* li, struct caracter al);// vai conter os dados que será inserido dentro da lista

int insere_lista_final (Lista* li, struct caracter al);//vai conter os dados que serão inseridos no final da lista

int insere_lista_ordenada(Lista* li, struct caracter al); //insere elemento na lista

int remove_lista_inicio(Lista* li); //protótipo da função de remover no inicio

int remove_lista_final(Lista* li); //protótipo da função para remover no final

int remove_lista(Lista* li, char carac); //protótipo da função para remover qualquer elemento da lista

int remove_listaq(Lista* li, char carac); //remove qualquer elemento da lista no caso quereremos o relacionado a q

int imprimirLista(Lista* li, struct caracter *al);// imprimi a lista

int consulta_lista_mat(Lista* li, char carac, struct caracter *al); //prototipo da funcao que encontra determinado dado na lista

int lista_vazia (Lista *li); //Verificar se a lista está vazia

Page 40: Listas encadeadas, duplamente encadeadas com cabeça

int tamanho_lista(Lista* li); //Protótipo para retornar o tamanho da lista

void menu(); //menu para escolher o arquivo

void menu_simplismente_encadeada();//submenu de lista simplismente encadeadasvoid menu_duplamente_encadeada();//submenu da lista duplamente encadeada

//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------//--------------------------------------//--------------------------------------//-------------------//-------struct aluno{//strutura para armazenar um dado qualquer, englobando int, char e float da duplamente encadeada char dado_caracter[30];};

Lista* cria_lista();//Cria a lista duplamente encadeada

int Lista_vazia(lista* Li);//protótipo da lista duplamente encadeada, a Lista_vazia recebe como parametro lista e retorna um inteiro

int Tamanho_lista(lista *Li); //retorna o tamanho da lista duplamente encadeada

int Insere_lista_inicioLista(lista* Li, struct caracter Al);//inserir elemento no inicio da lista duplamente encadeada, retorna 0 se deu erro ou 1 se deu certo

int Insere_lista_final(lista* Li, struct caracter Al);//protótipo da função de inserir no final da lista duplamente encadeada

int Insere_lista_ordenadaq(lista* Li, struct caracter Al);//exercicio de q

int Insere_lista_ordenadap(lista* Li, struct caracter Al);//exercicio de inserir o p

Page 41: Listas encadeadas, duplamente encadeadas com cabeça