1
resposta

referência e próximo objeto da lista

Eu vi vários exemplos de lista ligada em outras linguagens para tentar entender o conceito e fiquei com uma dúvida, quando você usa o próximo, fica celula.proximo, isso é uma propriedade? O próximo tá armazenando o valor que eu inseri? Sempre ouço falar de referência nesse caso tbm é assim, se sim, quando a referência é utilizada?

Comecei a pensar nisso por causa desse exemplo em js:

// create the first node
const head = new LinkedListNode(12);

// add a second node
head.next = new LinkedListNode(99);

// add a third node
head.next.next = new LinkedListNode(37);

Se eu usar celula.proximo.proximo, eu vou para o terceiro item da lista? Se sim, porque acontece isso? Qual a diferença do python para o C que usa ponteiros nesse caso?

void push(node_t ** head, int val) {
    node_t * new_node;
    new_node = (node_t *) malloc(sizeof(node_t));

    new_node->val = val;
    new_node->next = *head;
    *head = new_node;
}

Outra coisa, porque no final o novo nó aponta para o head? Tá atribuindo o valor do novo item para o head? Isso que eu não consigo entender.

1 resposta

Olá Gisele, tudo bem com você?

quando você usa o próximo, fica celula.proximo, isso é uma propriedade?

Sim!, veja que no código de criação da classe Celula temos:

def __init__(self):
    self.conteudo = conteudo
    self.proximo = None

O próximo tá armazenando o valor que eu inseri? Sempre ouço falar de referência nesse caso tbm é assim, se sim, quando a referência é utilizada?

Então hahahah, é justamente ai que está a referência!

O proximo não armazena o valor que você inseriu, e sim uma referência para a pŕoxima célula

Vou te explicar com base nos seus exemplos:

  • Javascript

Esse primeiro de javascript eu procurei a classe:

class LinkedListNode {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}

Veja que tanto nosso em python quanto em javascript temos o this.next que será utilizado para armazenar uma referência, que por padrão começa como null ou None

Temos a primeira inserção:

const head = new LinkedListNode(12);

Então temos um objeto assim:

Endereço na memoria: 100

head: {
    data: 12,
    next: null,
}

Após isso temos a segunda inserção:

head.next = new LinkedListNode(99);

Ou seja vamos criar um novo objeto com:

Endereco na Memoria: 500      
{ 
    data: 99,
    next: null
} 

E como estamos atribuindo esse objeto a head.next, teremos agora:

Endereço na memoria: 100

head: {
    data: 12,
    next: 500,
}

Então provavelmente aqui que há a confusão, estamos sempre armazenando uma referência na memória de onde está o próximo objeto, e por fim a ultima inserção:

head.next.next = new LinkedListNode(37);

Podemos ler isso como: Vá para o objeto que head.nextreferencia, ou seja, pegue o endereço de memória que está armazenado em nexte vá para lá, chegando lá teremos um novo objeto, pegue o valor de next e coloque o endereço de um novo elemento que iremos criar:

Endereco na memoria: 600

{
    data: 37,
    next: null, 
}

Como head.next aponta para o endereço de memória 500, e sabemos que la temos o nosso (99) :

Endereco na Memoria: 500      
{ 
    data: 99,
    next: 600
} 

Agora temos algo parecido com isso:

    100                       500                                600
[ Valor: 12] ----------->  [ Valor: 99]  ---------->         [Valor: 37]
[Prox: 500]                | Prox: 600 |                    | Prox: null |

Se eu usar celula.proximo.proximo, eu vou para o terceiro item da lista? Se sim, porque acontece isso? Qual a diferença do python para o C que usa ponteiros nesse caso?

Sim, você irá porque o fluxo será o seguinte:

  • Celula é a raiz da nossa lista ligada (ex: 12)
  • Celula.proximo é uma referência para o endereço de memória 500 que é a segunda Celula ( 99)
  • Celula.proximo.proximo é ir para o endereço de memória 500 e de lá pega ro proximo que também é uma referência na memóra ( Prox: 600) que nos leva a um objeto do tipo Celula com valor 37

Em relação a diferença de Python para C, com a estrutura de dados não muda nada, a única coisa é o processo de codificar, onde temos que identificar que estamos passando um endereço na memória, ou que estamos querendo acessar um endereço de memória

Com python e muitas linguagens orientadas a objetos esse processo de referenciar que é um endereço de memória é automático, quando dizemos que celula.proximo = new Celula(valor), para nós não pode ser tão claro quanto em C mas de fato o que estamos colocando em celula.proximo é um endereço da memória que aponta para o objeto que criamos :)

Outra coisa, porque no final o novo nó aponta para o head? Tá atribuindo o valor do novo item para o head? Isso que eu não consigo entender.

Vamos pegar aquele exemplo que trabalhei acima, e supor que temos uma função adicionaNoComeco, igual na aula :)

Veja que nesses exemplos esse head indica a cabeça da lista, ou seja, o primeiro elemento, e vou inserir agora mais um:

adicionaNoComeco( new LinkedListNode(50))

A primeira coisa que temos é que o nosso headda lista Ligada, que no exemplo da aula é o inicio, vale o endereço de memória 100 que é o objeto com valor 12

Então o primeiro passo é fazer é criar o novo objeto:

endereço na memoria: 850

{
    data: 50,
    prox: null,
} 

Entretanto dizemos que esse objeto aponta para o valor de head, então:

endereço na memoria: 850

{
    data: 50,
    prox: 100,
} 

E agora já que meu objeto novo está referenciando o que era o primeiro elemento da lista, eu posso trocar o valor de head, e atribuir para ele o endereço da memória dessa nova célula :)

{ 
    head: 850
} 

E agora temos corretamente:

head: 850

[ End: 850 | Valor: 50 | Prox: 100 ] -> [ End: 100 | Valor: 12 | Prox: 500] -> [End: 100 | Valor: 99 | Prox: 600] -> [End: 600 | Valor: 37 | Prox: Nulll]

Conseguiu compreender? Qualquer coisa estou a disposição :)

Abraços e Bons Estudos!