Salvou de mais, vi outros vídeos até parar no seu (os outros não desmerecendo) deixavam o assunto mais complicado... Mas você? Nossa fez o bicho papão virar borboleta
"fez o bicho papão virar borboleta" 🤣 🤣 🤣 🤣 🤣 Vou emoldurar essa comparação kkkkkk. Obrigado pelo feedback Ruan. Fico feliz em saber que ajudou em vez de complicar rsrs. Abraços e bons estudos.
Galera, segue meu código completo e comentado baseado no que o professor ensinou. Espero que ajude. Uma dica de ouro: só consegui entender essa matéria quando comecei a literalmente desenhar o que estava acontecendo no papel, os nós e como eles se relacionam na lista nos processos de inserir e excluir, acreditem, ajuda muito. Para quem quiser ter essa ideia visual de como representar no papel, deem uma olhada nos vídeos do Pietro, vai ajudar ainda mais. 👇👇👇
#include #include //Estrutura principal do programa, é o nosso nó contendo o dado e o ponteiro para o próximo nó typedef struct no{ int conteudo; struct no *proximo; }no; //Lista (conjunto de nós) typedef struct Lista{ no *inicio; int tamanho; }Lista; //Inicializa os parâmetros da lista void inicializaLista(Lista *lista){ lista->inicio = NULL; lista->tamanho = 0; } void inserirInicio(int elemento, Lista *lista){ //Aloca dinamicamente memória para o novo nó no *novo = (no*)malloc(sizeof(no)); //O conteudo do novo nó recebe o elemento a ser adicionado novo->conteudo = elemento; //novo->proximo aponta para o mesmo local que lista->inicio aponta. No primeiro caso, é NULL, //depois o novo->proximo passa a apontar sempre para onde lista->inicio aponta (que será o nó antigo) //ou seja, o novo nó passa a apontar para o seu anterior, dessa forma, nós concatenamos os 2 nós, //mas sempre colocando o novo nó criado antes na lista, ou seja, no início novo->proximo = lista->inicio; //O início da lista, que apontava para o nó mais recente (que está no início) passa a apontar para o novo nó //formalizando a entrada do novo nó, de fato, no início da lista. Observe que lista->inicio recebe o endereço //do novo nó, passando a apontar para o novo nó que agora é o primeiro da lista, já que estamos inserindo no início lista->inicio = novo; //O tamanho da lista é incrementado lista->tamanho++; //OBS: note que tanto inserindo no inicío da lista quanto no final, o último nó sempre apontará para NULL } void inserirFim(int elemento, Lista *lista){ //Aloca dinamicamente memória para o nó e inicializa os parâmetros dele no *novo = (no*)malloc(sizeof(no)); //O conteudo do novo nó recebe o elemento a ser adicionado novo->conteudo = elemento; //Se a lista estiver vazia, lista->inicio já recebe o novo nó e já fazemos o primeiro, e por ora, último nó //apontar para NULL if(lista->inicio == NULL){ lista->inicio = novo; novo->proximo = NULL; //Se já houverem elementos na lista, criamos um ponteiro do tipo nó que recebe o início da lista //e andamos até o final da lista com o while através do ponteiro criado, quando iterador->proximo for //NULL, ou seja, ele aponta para o mesmo local que o ponteiro do último nó aponta, significa que chegamos //ao final da lista e o iterador->proximo pode receber o endereço do novo nó criado } else { no *iterador = lista->inicio; while(iterador->proximo != NULL){ iterador = iterador->proximo; } //Observe que esse ponteiro consegue alterar os ponteiros dos nós na lista. Aqui ele faz com que o ponteiro //do último nó passe a apontar para o endereço do novo nó criado, concatenando os 2. E o ponteiro do //último nó, aponta para NULL, como sempre deve acontecer. iterador->proximo = novo; novo->proximo = NULL; } //O tamanho da lista é incrementado lista->tamanho++; //OBS: note que tanto inserindo no inicío da lista quanto no final, o último nó sempre apontará para NULL } void excluir(int elemento, Lista *lista){ //Lista vazia já tratada na main() //É o caso do primeiro elemento da lista ser o que queremos remover if(lista->inicio->conteudo == elemento){ //noARemover aponta para o mesmo local que lista->inicio, ou seja, o primeiro nó no *noARemover = lista->inicio; //lista->inicio agora aponta para o mesmo local que o primeiro nó. Se a lista tiver 2 ou mais //nós, lista->inicio aponta para o segundo nó, se a lista só tiver um nó, lista->inicio //volta a apontar para NULL lista->inicio = noARemover->proximo; //O nó a ser removido passa a apontar para NULL e é completamente desligado da lista noARemover->proximo = NULL; free(noARemover); //O tamanho da lista é decrementado lista->tamanho--; printf(" %d foi removido! ", elemento); //Se não for o primeiro elemento que queremos remover, vamos para o else }else { //Criamos um nó de iteração no loop começando do início da lista no *iterador = lista->inicio; //O programa só sairá do loop se o conteudo do iterador->proximo for o mesmo que o elemento //ou se o iterador->proximo apontar para NULL, o que significa que o usuário provavelmente tentou //remover um elemento que não existe (ou não está cadastrado) na lista while(iterador->proximo != NULL && iterador->proximo->conteudo != elemento){ //Se o elemento não for o que estamos procurando no nó, andamos com o interador na lista //e continuamos as comparações. Note que o iterador sempre estará apontando para o nó //anterior ao que queremos remover iterador = iterador->proximo; } //Se esse if é verdadeiro, significa que o iterador está apontando para o último nó e o elemento //digitado não existe na lista if(iterador->proximo == NULL){ printf("Provavalmente, voce digitou um numero que nao existe na lista. Tente novamente. "); }else { //Criamos um outro nó que, além de apontar para o nó a ser removido, irá funcionar como Backup //do *proximo do nó anterior que iremos remover porque o objetivo é ligar o *proximo do nó anterior //com o *proximo do nó a ser removido, ou seja, o nó anterior ao que será removido passará a apontar //para o mesmo local que o que o nó a ser removido aponta, ligando o nó anterior com o próximo nó da lista. //Depois disso, o nó a ser removido passará a apontar para NULL e será desligado //Com essa instrução, noARemover aponta para o nó que será removido já que achamos o elemento do nó //a ser removido no while (iterador->proximo->conteudo foi igual ao elemento). Mas lembre que iremos //remover o nó como um todo, não apenas o elemento. no *noARemover = iterador->proximo; //Só conseguimos fazer o nó anterior ao nó que será removido apontar para o nó seguinte ao nó que será //removido com essa instrução. Essa é a linha chave da remoção que liga os 2 nós, isolando o nó que será //removido. iterador->proximo = noARemover->proximo; //O nó a ser removido passa a apontar para NULL e é completamente desligado da lista noARemover->proximo = NULL; free(noARemover); //O tamanho da lista é decrementado lista->tamanho--; printf(" %d foi removido! ", elemento); } //free(iterador); //Perceba que noARemover funciona como um intermediário e facilitador para trabalhar com os ponteiros } } void imprimir(Lista *lista){ //Criamos um ponteiro do tipo nó que receberá o início da lista para podermos //trabalhar com o while imprimindo os elementos e andando na lista no *iterador = lista->inicio; //Enquanto houverem elementos na lista, os exibiremos na tela e andaremos na lista //através do ponteiro criado printf(" Tamanho da lista: %d ", lista->tamanho); while(iterador != NULL){ printf("%d ", iterador->conteudo); iterador = iterador->proximo; //O último nó sempre aponta para NULL. Logo, quando o iterador chegar no último nó, ele irá //receber iterador->proximo normalmente, mas dessa vez iterador->proximo passa a apontar //para o mesmo local que o último nó aponta, ou seja, NULL. Logo, sairemos do loop, fim da lista } printf(" "); } int main(){ //Criando a lista e alocando memória dinamicamente para ela Lista *lista = (Lista*)malloc(sizeof(Lista)); if (lista == NULL) { printf("Erro de alocacao da lista! "); exit(0); } else { int quantidade, opcao, valor; inicializaLista(lista); do { printf(" [1] insere o elemento no inicio [2] insere o elemento no fim [3] exibir dados [4] remover elemento [5] sair "); printf(">>>Sua opcao: "); scanf("%d", &opcao); switch(opcao){ //Insere o elemento no inicio case 1: printf("Digite o valor: "); scanf("%d", &valor); inserirInicio(valor, lista); break; //Insere o elemento no fim case 2: printf("Digite o valor: "); scanf("%d", &valor); inserirFim(valor, lista); break; //Exibe os dados da lista case 3: imprimir(lista); break; //Remove algum elemento específico verificando se a lista está ou não vazia case 4: if(lista->inicio == NULL){ printf("Lista vazia! "); }else{ printf("Digite o valor: "); scanf("%d", &valor); excluir(valor, lista); } break; //Sai do programa liberando a memória alocada para a lista case 5: free(lista); printf("Voce optou por sair... "); break; //Opções inválidas default: printf("Escolha uma opcao valida! "); } } while (opcao != 5); } return 0; }
PS: por algum motivo a função excluir não funciona completamente no codeblocks, mas no VSCODE sim. Por completamente quero dizer se você tentar remover um elemento que não existe na lista, o codeblocks simplesmente para, ao menos pra mim. No VS, não aconteceu nenhum problema se o programa for compilado tranquilamente.
Olá Camila. Produzi aqui pro canal um curso completo de C, então regravei estas aulas sobre listas encadeadas, elas estão nas playlist sobre estruturas de dados em C a partir da aula 242: th-cam.com/play/PLqJK4Oyr5WSjQ584hwqaHJYDpDcYqS-HK.html
Salvando a jornada, parabéns pela didática. Obrigado,
Cara tu manda muito! Não sei como não tem mais visualizações, sua didática é impecável!
Né. Também fico me perguntando isso 😅 😅 😅
Que bom que ajudou João. Obrigado pelo feedback.
Salvou de mais, vi outros vídeos até parar no seu (os outros não desmerecendo) deixavam o assunto mais complicado... Mas você? Nossa fez o bicho papão virar borboleta
"fez o bicho papão virar borboleta" 🤣 🤣 🤣 🤣 🤣
Vou emoldurar essa comparação kkkkkk.
Obrigado pelo feedback Ruan. Fico feliz em saber que ajudou em vez de complicar rsrs.
Abraços e bons estudos.
Didática nota 1000!!!!!!!!!!
Valeu 😁 😁 👍 👍
Cara sensacional seu vídeo, muito didático mesmo, ajudando muito
Obrigado Elvis pelo feedback 🤩.
Fico feliz em saber que está ajudando.
Galera, segue meu código completo e comentado baseado no que o professor ensinou. Espero que ajude. Uma dica de ouro: só consegui entender essa matéria quando comecei a literalmente desenhar o que estava acontecendo no papel, os nós e como eles se relacionam na lista nos processos de inserir e excluir, acreditem, ajuda muito. Para quem quiser ter essa ideia visual de como representar no papel, deem uma olhada nos vídeos do Pietro, vai ajudar ainda mais. 👇👇👇
#include
#include
//Estrutura principal do programa, é o nosso nó contendo o dado e o ponteiro para o próximo nó
typedef struct no{
int conteudo;
struct no *proximo;
}no;
//Lista (conjunto de nós)
typedef struct Lista{
no *inicio;
int tamanho;
}Lista;
//Inicializa os parâmetros da lista
void inicializaLista(Lista *lista){
lista->inicio = NULL;
lista->tamanho = 0;
}
void inserirInicio(int elemento, Lista *lista){
//Aloca dinamicamente memória para o novo nó
no *novo = (no*)malloc(sizeof(no));
//O conteudo do novo nó recebe o elemento a ser adicionado
novo->conteudo = elemento;
//novo->proximo aponta para o mesmo local que lista->inicio aponta. No primeiro caso, é NULL,
//depois o novo->proximo passa a apontar sempre para onde lista->inicio aponta (que será o nó antigo)
//ou seja, o novo nó passa a apontar para o seu anterior, dessa forma, nós concatenamos os 2 nós,
//mas sempre colocando o novo nó criado antes na lista, ou seja, no início
novo->proximo = lista->inicio;
//O início da lista, que apontava para o nó mais recente (que está no início) passa a apontar para o novo nó
//formalizando a entrada do novo nó, de fato, no início da lista. Observe que lista->inicio recebe o endereço
//do novo nó, passando a apontar para o novo nó que agora é o primeiro da lista, já que estamos inserindo no início
lista->inicio = novo;
//O tamanho da lista é incrementado
lista->tamanho++;
//OBS: note que tanto inserindo no inicío da lista quanto no final, o último nó sempre apontará para NULL
}
void inserirFim(int elemento, Lista *lista){
//Aloca dinamicamente memória para o nó e inicializa os parâmetros dele
no *novo = (no*)malloc(sizeof(no));
//O conteudo do novo nó recebe o elemento a ser adicionado
novo->conteudo = elemento;
//Se a lista estiver vazia, lista->inicio já recebe o novo nó e já fazemos o primeiro, e por ora, último nó
//apontar para NULL
if(lista->inicio == NULL){
lista->inicio = novo;
novo->proximo = NULL;
//Se já houverem elementos na lista, criamos um ponteiro do tipo nó que recebe o início da lista
//e andamos até o final da lista com o while através do ponteiro criado, quando iterador->proximo for
//NULL, ou seja, ele aponta para o mesmo local que o ponteiro do último nó aponta, significa que chegamos
//ao final da lista e o iterador->proximo pode receber o endereço do novo nó criado
} else {
no *iterador = lista->inicio;
while(iterador->proximo != NULL){
iterador = iterador->proximo;
}
//Observe que esse ponteiro consegue alterar os ponteiros dos nós na lista. Aqui ele faz com que o ponteiro
//do último nó passe a apontar para o endereço do novo nó criado, concatenando os 2. E o ponteiro do
//último nó, aponta para NULL, como sempre deve acontecer.
iterador->proximo = novo;
novo->proximo = NULL;
}
//O tamanho da lista é incrementado
lista->tamanho++;
//OBS: note que tanto inserindo no inicío da lista quanto no final, o último nó sempre apontará para NULL
}
void excluir(int elemento, Lista *lista){
//Lista vazia já tratada na main()
//É o caso do primeiro elemento da lista ser o que queremos remover
if(lista->inicio->conteudo == elemento){
//noARemover aponta para o mesmo local que lista->inicio, ou seja, o primeiro nó
no *noARemover = lista->inicio;
//lista->inicio agora aponta para o mesmo local que o primeiro nó. Se a lista tiver 2 ou mais
//nós, lista->inicio aponta para o segundo nó, se a lista só tiver um nó, lista->inicio
//volta a apontar para NULL
lista->inicio = noARemover->proximo;
//O nó a ser removido passa a apontar para NULL e é completamente desligado da lista
noARemover->proximo = NULL;
free(noARemover);
//O tamanho da lista é decrementado
lista->tamanho--;
printf("
%d foi removido!
", elemento);
//Se não for o primeiro elemento que queremos remover, vamos para o else
}else {
//Criamos um nó de iteração no loop começando do início da lista
no *iterador = lista->inicio;
//O programa só sairá do loop se o conteudo do iterador->proximo for o mesmo que o elemento
//ou se o iterador->proximo apontar para NULL, o que significa que o usuário provavelmente tentou
//remover um elemento que não existe (ou não está cadastrado) na lista
while(iterador->proximo != NULL && iterador->proximo->conteudo != elemento){
//Se o elemento não for o que estamos procurando no nó, andamos com o interador na lista
//e continuamos as comparações. Note que o iterador sempre estará apontando para o nó
//anterior ao que queremos remover
iterador = iterador->proximo;
}
//Se esse if é verdadeiro, significa que o iterador está apontando para o último nó e o elemento
//digitado não existe na lista
if(iterador->proximo == NULL){
printf("Provavalmente, voce digitou um numero que nao existe na lista. Tente novamente.
");
}else {
//Criamos um outro nó que, além de apontar para o nó a ser removido, irá funcionar como Backup
//do *proximo do nó anterior que iremos remover porque o objetivo é ligar o *proximo do nó anterior
//com o *proximo do nó a ser removido, ou seja, o nó anterior ao que será removido passará a apontar
//para o mesmo local que o que o nó a ser removido aponta, ligando o nó anterior com o próximo nó da lista.
//Depois disso, o nó a ser removido passará a apontar para NULL e será desligado
//Com essa instrução, noARemover aponta para o nó que será removido já que achamos o elemento do nó
//a ser removido no while (iterador->proximo->conteudo foi igual ao elemento). Mas lembre que iremos
//remover o nó como um todo, não apenas o elemento.
no *noARemover = iterador->proximo;
//Só conseguimos fazer o nó anterior ao nó que será removido apontar para o nó seguinte ao nó que será
//removido com essa instrução. Essa é a linha chave da remoção que liga os 2 nós, isolando o nó que será
//removido.
iterador->proximo = noARemover->proximo;
//O nó a ser removido passa a apontar para NULL e é completamente desligado da lista
noARemover->proximo = NULL;
free(noARemover);
//O tamanho da lista é decrementado
lista->tamanho--;
printf("
%d foi removido!
", elemento);
}
//free(iterador);
//Perceba que noARemover funciona como um intermediário e facilitador para trabalhar com os ponteiros
}
}
void imprimir(Lista *lista){
//Criamos um ponteiro do tipo nó que receberá o início da lista para podermos
//trabalhar com o while imprimindo os elementos e andando na lista
no *iterador = lista->inicio;
//Enquanto houverem elementos na lista, os exibiremos na tela e andaremos na lista
//através do ponteiro criado
printf("
Tamanho da lista: %d
", lista->tamanho);
while(iterador != NULL){
printf("%d
", iterador->conteudo);
iterador = iterador->proximo;
//O último nó sempre aponta para NULL. Logo, quando o iterador chegar no último nó, ele irá
//receber iterador->proximo normalmente, mas dessa vez iterador->proximo passa a apontar
//para o mesmo local que o último nó aponta, ou seja, NULL. Logo, sairemos do loop, fim da lista
}
printf("
");
}
int main(){
//Criando a lista e alocando memória dinamicamente para ela
Lista *lista = (Lista*)malloc(sizeof(Lista));
if (lista == NULL) {
printf("Erro de alocacao da lista!
");
exit(0);
} else {
int quantidade, opcao, valor;
inicializaLista(lista);
do {
printf("
[1] insere o elemento no inicio
[2] insere o elemento no fim
[3] exibir dados
[4] remover elemento
[5] sair
");
printf(">>>Sua opcao: ");
scanf("%d", &opcao);
switch(opcao){
//Insere o elemento no inicio
case 1:
printf("Digite o valor: ");
scanf("%d", &valor);
inserirInicio(valor, lista);
break;
//Insere o elemento no fim
case 2:
printf("Digite o valor: ");
scanf("%d", &valor);
inserirFim(valor, lista);
break;
//Exibe os dados da lista
case 3:
imprimir(lista);
break;
//Remove algum elemento específico verificando se a lista está ou não vazia
case 4:
if(lista->inicio == NULL){
printf("Lista vazia!
");
}else{
printf("Digite o valor: ");
scanf("%d", &valor);
excluir(valor, lista);
}
break;
//Sai do programa liberando a memória alocada para a lista
case 5:
free(lista);
printf("Voce optou por sair...
");
break;
//Opções inválidas
default:
printf("Escolha uma opcao valida!
");
}
} while (opcao != 5);
}
return 0;
}
PS: por algum motivo a função excluir não funciona completamente no codeblocks, mas no VSCODE sim. Por completamente quero dizer se você tentar remover um elemento que não existe na lista, o codeblocks simplesmente para, ao menos pra mim. No VS, não aconteceu nenhum problema se o programa for compilado tranquilamente.
@@JoaoPaulo-zv8ry Cara, sem palavras para te agradecer. Seu código está me ajudando muito! Muito obrigado
Você explica muito bem! Conteúdo sensacional!
Obrigado Bard 😁
ameii, vai mostrar como inserir no meio?
Olá Camila.
Produzi aqui pro canal um curso completo de C, então regravei estas aulas sobre listas encadeadas, elas estão nas playlist sobre estruturas de dados em C a partir da aula 242: th-cam.com/play/PLqJK4Oyr5WSjQ584hwqaHJYDpDcYqS-HK.html