I have a question about linked lists

In this code:

def remove_node(self, value_to_remove):
    current_node = self.head_node
    if current_node.get_value() == value_to_remove:
      self.head_node = current_node.get_next_node()
    else:
      while current_node:
        next_node = current_node.get_next_node()
        if next_node.get_value() == value_to_remove:
          current_node.next_node = next_node.get_next_node()
          current_node = None
        else:
          current_node = next_node

Why did they set the

current_node = None

This makes no sense to me. The only reason I can think of is to create a break in the loop, but wouldn’t it be better to just use break? If you set current to None you are messing up the whole list right?

Thanks

No. It just allows the while condition to evaluate to False and exit the loop.
It looks like you could simply use break if you prefer. Perhaps @appylpye could weigh in, and shed some additional light on this.

Ok but doesnt it also set the node to be None which would not make sense then…@appylpye what do you think? Or maybe you @midlindner know…

Similar to the following:

a = 5
b = a
b = None
print(a) #5

current_node is a variable assigned to the value which happens to be a node, but reassigning current_node to None doesn’t remove the node. We remove the node by not having any other node point to it.

Perhaps this example will be more illustrative:

class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        return f"My name is {self.name}, the cat.\nI'm {self.age} years old."


felix = Cat('Felix', '5')
morris = Cat('Morris', '3')
garfield = Cat('Garfield', '7')

for cat in [felix, morris, garfield]:
    print(cat)

b = morris
b.age = 6 #we change morris' age
b = None #Oh, no. Did we kill morris?
print(morris) #??
Output:

My name is Felix, the cat.
I’m 5 years old.
My name is Morris, the cat.
I’m 3 years old.
My name is Garfield, the cat.
I’m 7 years old.
My name is Morris, the cat. (Morris lives!!)
I’m 6 years old.

1 Like

It doesn’t mess up the LinkedList, because it merely sets the local current_node variable to None.

As suggested, you could use a break statement instead.

1 Like

Then why are we setting

current_node.next_node = next_node.get_next_node()

If current_node is not the original and is going to get erased?

If

current_node.next_node = next_node.get_next_node()

Affects the original then why does-int setting it to None?

What is the point of setting Current Node next node if you are just going to erase current node?

Does changing current nodes next node also change the original nodes next node? If so then how since when you reassign a variable it does not affect the original.

Then why are we setting

current_node.next_node = next_node.get_next_node()

If current_node is not the original and is going to get erased?

If

current_node.next_node = next_node.get_next_node()

Affects the original then why does-int setting it to None?

What is the point of setting Current Node next node if you are just going to erase current node?

Does changing current nodes next node also change the original nodes next node? If so then how since when you reassign a variable it does not affect the original.

Hi @dev6112515278,

This statement within the remove_node method of Linked_List does not destroy the Node to which current_node pointed prior to that assignment:

          current_node = None

It leaves the Node to which it previously pointed intact.

Let’s assume that we are on the exercise Linked List Implementation III.

We have this definition of the Node class:

class Node:
  def __init__(self, value, next_node=None):
    self.value = value
    self.next_node = next_node
    
  def get_value(self):
    return self.value
  
  def get_next_node(self):
    return self.next_node
  
  def set_next_node(self, next_node):
    self.next_node = next_node

As an experiment, let’s create two Node instances, independent of a LinkedList:

node_b = Node("Hippo")
node_a = Node("Giraffe", node_b)

We now have two Nodes, with node_a having a pointer to node_b.

Let’s display some stuff:

print(node_a.get_value()) # This accesses node_a
print(node_b.get_value()) # This accesses node_b
print(node_a.get_next_node().get_value()) # This also accesses node_b

Output:

Giraffe
Hippo
Hippo

Above, we successfully used two techniques to get access to node_b.

Now let’s assign None to node_b:

node_b = None

Again, let’s display some stuff:

print(node_b)
print(node_a.get_next_node().get_value())

Output:

None
Hippo

As expected, the variable named node_b now has the value None. However, node_a still has a pointer to the Node that had previously been pointed to by node_b. That Node was not destroyed. We merely lost the ability to access it via the variable node_b, while retaining the ability to access it via the pointer from node_a.

But wasnt node_a pointing to node_b and not to Node("Hippo"). It was not directly pointing to Node("Hippo") but indirectly? So I dont see why node_a.next_node does not end up being None when you set node_b to be none, since you never directly pointed node_a to the original instance of the node

Second, here:

current_node = self.head_node
current_node.next_node = next_node.get_next_node()
          current_node = None

I skipped some code, but I wanted to ask are we setting self.head_node.next_node or current_node.next_node? They are not the same right? I am going to keep reading over what you just wrote but its hard for me to wrap my mind around. What points to what and what it means.

node_a = Node("Giraffe", node_b)

node_a points to node_b so why when you set node_b to None does node_a not point to None? You never directly connected node_a to the original node that node_b pointed to…

What the above means is that the object referred to by node_a contains a pointer to node_b.

Here is the __init__ method of Node:

  def __init__(self, value, next_node=None):
    self.value = value
    self.next_node = next_node

Note this line:

    self.next_node = next_node

When we did this, that line set up the pointer from node_a to node_b:

node_a = Node("Giraffe", node_b)

So thereafter, if we make the variable node_b refer to anything else, we still have access to the Node that was originally referred to by node_b.

Yes but I am confused how this is possible since, doesnt node_a strictly point to node_b? If we wanted it to point to the original node wouldnt we have to say node_a = Node("Giraffe", Node("Hippo")?

Thats like saying

a = 5
b = a

so when you are setting a variable equal to another variable you are really setting that variable equal to what that other variable is pointing to?

Yes, that is exactly what happens.

So, if we continue with what you were doing above, as follows …

a = None

… we have not destroyed the 5 that we originally assigned to a. We can still do this:

print(b)

here

def remove_node(self, value_to_remove):
    current_node = self.head_node
    if current_node.get_value() == value_to_remove:
      self.head_node = current_node.get_next_node()
    else:
      while current_node:
        next_node = current_node.get_next_node()
        if next_node.get_value() == value_to_remove:
          current_node.next_node = next_node.get_next_node()
          current_node = None
        else:
          current_node = next_node

specifically here:

self.head_node = current_node.get_next_node()

could we say

current_node = current_node.get_next_node()

Since current_node is pointing to self.head_node

And since here:

 current_node.next_node = next_node.get_next_node()

We are using current node to set the next node

I dont know why this is so hard for me to wrap my head around. I feel like it should be simple

I think I understand it better once I drew it out

Here …

      self.head_node = current_node.get_next_node()

… we are changing the head_node instance variable to refer to a different Node.

If, instead, we did this …

      current_node = current_node.get_next_node()

… we would only be reassigning the local current_node variable to refer to a different Node. That would leave the head_node instance variable unchanged.

Yes I think I understand it. Thanks alot the help is invaluable. I just would never think of doing it this way. I would have used break instead of setting current_node = None. I think that they wrote this remove node function different then before also.

I also would have preferred that. It is more explicit, and is therefore easier to understand.

See the second line of PEP 20 – The Zen of Python.