×
Ir ao conteúdo
  • Cadastre-se

C Lista em C, não estou conseguindo fazer essas listas em C.


Ir à solução Resolvido por arfneto,

Posts recomendados

@patopaloro Você tem uma lista simplesmente encadeada, então pode considerar algo assim:

struct node {
    char str[101]; // dado
    struct listNode* nextP; // ponteiro para o proximo no
};

typedef struct node Node; // encurtar o nome

Você só vai ter um ponteiro em cada nó, que aponta para o próximo nó.

O ponteiro para o começo da lista então seria:

Node* startP = NULL;

O protótipo da função de inserção poderia ser algo assim:

int insert(Node** startP, char* str);
  • Curtir 2
Link para o comentário
Compartilhar em outros sites

struct node {
    char str[101]; // dado
    struct node* nextP; // ponteiro para o proximo no
};

typedef struct node Node; // encurtar o nome

Só corrigindo minha bobagem aí.

Depois de 1h o fórum não deixa mais editar a postagem 🤪

Tem alguma ideia de como seguir com o programa? O ponteiro para o começo da lista você declara na main.

Consegue fazer a função de inserção? Tem alguma ideia de como fazer?

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@patopaloro Lista não é algo que se aprende no começo, então você já deve ter uma noção de estruturas, ponteiros e alocação dinâmica, isso é a base.

Aloque um ponteiro para Node do tamanho de Node mesmo, sendo o campo str a string do nó e nextP o ponteiro para o próximo nó, você pode inicializar com NULL.

Node* novoP = malloc(sizeof(Node));

Você vai precisar de um ponteiro para o nó anterior para fazer ele apontar para o novo nó, e um para o nó atual, inicialmente apontado para onde o ponteiro do início da lista aponta:

Node* anteriorP = NULL;
Node* atualP = *startP;

Assim, basta um loop percorrendo a lista a partir de atualP, verificando o critério de colocação do nó e atualizando anteriorP.

Já que anteriorP aponta para NULL inicialmente, se ele continuar apontando para NULL, você vai ter que fazer a inserção no início: o campo nextP de novoP aponta para onde o ponteiro do início da lista aponta, e o ponteiro do início da lista aponta para esse novoP.

Caso contrário,  o campo nextP de anteriorP aponta para novoP e o campo nextP de novoP aponta para atualP.

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

  • Solução

Os 3 exercícios são a mesma coisa, desde que perceba isso antes e escreva em torno dos dados

 

image.png.816b37386a00cb1bdb817de534c49728.png

 

Nesse primeiro caso são 2 listas, duas vezes a mesma coisa. Os demais tem uma lista só.


No desenho tem até as linhas em azul mostrando os tais ponteiros da lista.image.png.3e104624f15d9619b0d212d94f84e727.png

 

 

Acho que já sabe que a lista é uma coleção de coisas onde cada coisa aponta para a coisa seguinte. Se não soubesse saberia depois de ver esse desenho aí.

 

Na hora de colocar algo na lista é claro que tem que ter um critério, e a realidade é simples: ou insere no início, ou no final ou por alguma ordem, como a ordem alfabética, a ordem de CPF, o total da compra, seja lá o que for.

 

Nesse exemplo acima parece óbvio que é melhor inserir no final, como a noção que a gente tem de fila, porque assim fica na simples ordem de entrada e é mais fácil combinar a bebida com o acompanhamento.

 

Escrevendo em torno dos dados

 

Está claro que cada exercício vai ter uma lista e está claro que você deve escrever uma vez só esse lance de lista porque é para isso que serve uma lista em um programa de computador. Você escreve algo que possa usar sempre, ou não vale muito a pena escrever afinal.

 

Uma lista tem algo dentro: nós.

 

Veja isso
 

typedef struct
{
    int    N; // tamanho
    Node*  inicio; // o primeiro

} Lista;

 

E já está bom. A Lista é de nós, cada um é chamado Node, o nome comum nos livros. E o total deles é N.

 

image.png.8a14a0f6b4f9d7adf3e51658624174e0.pngUm node é como esses do desenho, mas só o esqueleto, sem o conteúdo. E dentro dele tem um ponteiro para o dado, ou mesmo o dado, dependendo de como preferir escrever.

 

Uma tentativa para Node
 

typedef struct st_no
{
    struct st_no* prox;
    Dado*         dado;

} Node;

 

Então dado aponta para o dado, que pode ser qualquer coisa, e prox aponta para o próximo Node, igualzinho no desenho. Se prox for NULL acabou a Lista. Como no desenho.

 

O tal st_no na declaração está lá porque tem um ponteiro dentro da estrutura para outra do mesmo tipo, e o nome Node só aparece na linha de baixo e assim não dá para escrever Node* dado antes de definir o que é Node, certo? E você não quer ficar repetido struct XXX pelo programa afora.

 

Uma tentativa para Dado
 

typedef struct
{
    char item[20];

} Dado;

 

E já está bom: tem a lista de nós e cada nó aponta para um dado.

 

Como criar uma lista? Como apagar uma lista?

 

Para uma vida longa e tranquila, uma função pode criar a lista, outra pode apagar a lista e assim se funcionar está ok e se der m. já se sabe onde procurar. Se fizer diferente vai encher seu programa com ponteiros para início de uma e outra lista e ponteiros auxiliares e tal. Um inferno. 

 

Claro, estamos programando estruturas de dados (listas) com tamanho dinâmico em C e tem uns ponteiros óbvios desde o enunciado... As linhas com a flecha...➡️ É muito mais simples ter ponteiros para frente e para trás na lista. Ponteiros só para um lado são um pesadelo porque... não dá para ir para o outro lado.

 

E assim criar e apagar lista podem ser tão simples como
 

    Lista* criar();
    Lista* apagar(Lista*);

 

Acho que dá pra entender não? Como criar e apagar uma lista em seguida?

 

int main(void)
{
    Lista* minha_lista = criar();
    apagar(minha_lista);
    return 0;
}

 

 

Mas depois de apagar a lista minha_lista se o programa continuar o ponteiro vai ficar apontando para a lista que já foi apagada e se você acha que isso é um problema pode apostar que é. Então por isso é esperto depois de apagar a lista zerar o ponteiro, antes de usar por engano e cancelar o programa.

 

int main(void)
{
    Lista* minha_lista = criar();
    apagar(minha_lista);
    minha_lista = NULL; // apaga a referencia
    return 0;
}

 

Mas isso é um porre e é mais uma coisa para lembrar então seria legal se apagar() retornasse o NULL para invalidar a referência para minha_lista na mesma linha...

 

int main(void)
{
    Lista* minha_lista = criar();
    minha_lista = apagar(minha_lista);
    return 0;
}

 

Aí sim. Em outras linguagens se pode até exigir que o cara use o valor que a função retorna. Em C ele pode ser ignorado e se pode escrever como na primeira versão.

 

E como criar uma lista afinal?

 

Lista* criar()
{
        Lista* L = (Lista*)malloc(sizeof(Lista));
        if (L == NULL) return NULL;
        L->inicio = NULL;
        L->N = 0;
        return L;
};

 

Não é só isso? aloca uma lista com tamanho zero e apontando para NULL como no desenho...

image.png.562eba25ecc4f4fb8f0297f280634a17.png

O simples. Dentro da lista está o tamanho da lista, dentro da lista tem o endereço de início da lista. Como eu sugeri antes seria claro melhor ter também o ponteiro para o último nó porque assim facilita muito percorrer a lista.

 

E como seria apagar uma lista? 

 

Não seria equivalente a apagar todos os nós e depois apagar a própria lista?  Sim. É só isso.

 

Lista* apagar(Lista* uma)
{
    if (uma->N == 0)
    {  // esta vazia
        free(uma);
        return NULL;
    };
    // a lista tem pelo menos 1 cara
    // entao p aponta paa o segundo
    Node* p = uma->inicio->next;
    for (int i = 0; i < uma->N; i += 1)
    {
        free(uma->inicio);
        uma->inicio = p;  // o proximo
    }
    free(uma);  // apaga a lista em si
    return NULL;
};

 

Se a lista está vazia apaga a lista em si e retorna, porque não tem dado nenhum. Se tem algum apaga os N dados. Antes de apagar o nó salva o endereço do próximo, porque depois já era. Antes de travar a porta confirme que tem a chave... ;) 

 

13 horas atrás, Lucca Rodrigues disse:

O protótipo da função de inserção poderia ser algo assim:

int insert(Node** startP, char* str);

 

Sim, é verdade, @Lucca Rodriguesmas não é muito bom assim. Node é algo interno a Lista. O que se quer é inserir um Dado na Lista. Prefira sempre

 

    int inserir(Dado* d, Lista* l)

 

Porque pode usar isso para qualquer dado. Só aqui tem 3 exercícios por exemplo.

 

@patopaloro sugiro que escreva nessa linha. É a maneira comum de fazer isso em qualquer linguagem. E é comum por alguma razão. 

 

Veja no caso do exercício 1: você vai em um loop inserir as bebidas chamando inserir() em uma Lista de bebidas e outra Lista de acompanhamentos. Depois é só listar os N caras das duas listas que claro vão ter o mesmo tamanho.

 

Um "programa" até aqui, juntando o que está acima...

 

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

typedef struct
{
    char item[20];

} Dado;

typedef struct st_no
{
    struct st_no* next;
    Dado*         dado;

} Node;

typedef struct
{
    int    N; // tamanho
    Node*  inicio;

} Lista;

Lista* criar();
Lista* apagar(Lista*);
int    inserir(Dado*,Lista*);

int main(void)
{
    Lista* bebidas = criar();
    Lista* comidas = criar();
    apagar(bebidas);
    apagar(comidas);
    return 0;
}

Lista* apagar(Lista* uma)
{
    if (uma->N == 0)
    {  // esta vazia
        free(uma);
        return NULL;
    };
    // a lista tem pelo menos 1 cara
    // entao p aponta paa o segundo
    Node* p = uma->inicio->next;
    for (int i = 0; i < uma->N; i += 1)
    {
        free(uma->inicio);
        uma->inicio = p;  // o proximo
    }
    free(uma);  // apaga a lista em si
    return NULL;
};

Lista* criar()
{
    Lista* L = (Lista*)malloc(sizeof(Lista));
    if (L == NULL) return NULL;
    L->inicio = NULL;
    L->N      = 0;
    return L;
};

int inserir(Dado* d, Lista* L)
{
    return 0;
}

 

É claro que vai usar o mesmo código nos 3 programas. Só muda a definição de dado. Por isso é importante deixar os dados separados da estrutura. No fundo se cria um arquivo .h com as funções de lista e usa um #include em todos os programas. Mas deixa a definição de Dado de fora para poder trocar em cada programa ;) 

 

 

 

  • Curtir 1
  • Obrigado 2
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

Aprenda a ler resistores e capacitores

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!