Introduction to Linked Lists

Marco Sanchez-Ayala
The Startup
Published in
6 min readMay 23, 2020

Implementation in Python

Photo by JJ Ying on Unsplash

When I started this blog, I was a data science boot camp student determined to pursue a career in data analysis. My posts early on were often focused on techniques related to data analysis and visualization. Recently in my journey into tech, however, I’ve found a much deeper appreciation for coding and computer science than I have for purely data-focused applications of programming.

A few weeks ago I began shifting my focus towards better learning object-oriented programming, as you might tell from my most recent posts. This has opened many doors for me including one leading into the world of data structures, since their implementation is most easily achieved with OOP.

Data Structures

Data structures in computing refer to particular ways of organizing data for efficient use. Python users should be familiar with some of these simple data structures inherent in the language:

  • Lists
[1, 2, 3]
  • Tuples
(1, 2, 3)
  • Sets
{1, 2, 3}
  • Dictionaries
{'first': 1, 'second': 2, 'third': 3}

We have different data structures because each one can offer unique advantages over others in terms of different tasks like inserting elements, removing elements, searching, sorting, etc.

The data structure I’d like to discuss today is one that does not come standard in Python, the linked list. Why it’s standard in some other languages but not Python is a completely different discussion.

Linked Lists

A linked list is a linear collection of data elements. Each element points to the next, defining their ordering in the sequence. They can be used for many things like implementing stacks and queues, performing arithmetic operations on long integers, etc. See here for more examples!

There are many flavors of linked lists, but I’m going to introduce just the singly linked list. Let’s see an example and introduce some terminology.

1 -> 5 -> -2 -> None

We have three elements in the linked list above. Each element is called a node. Each node has two properties: a value and a reference to the next node in the sequence. By “reference” I mean the arrow pointing to the next value. You can think of the above linked list as the following with each box representing a node.

+------+ +------+ +-------+ 
| 1 -> | | 5 -> | | -2 -> | None
+------+ +------+ +-------+

I’m going to use the notation from the former example above, but just know that each node encompasses both a value and a reference to another node.

Notice how the third node references None. This is how we know we’ve found the end of the linked list because we eventually come to a point where there are no more nodes! Unlike Python lists, there are no indexes with which to reference each element in the linked list.

The first node of the linked list is called the head. We always start with the head when building our linked lists. In the example above, our head is a node with value 1 and pointing to a node with value 5.

So what? Let’s look at some things we can do with linked lists.

Insertion

We can insert elements at the beginning, middle, or end of our linked list. All we have to do is adjust how the nodes reference each other. Let’s add a node with value 0 into multiple places as an example.

At the beginning:

0 -> 1 -> 5 -> -2 -> None

This is the easiest and fastest. We just assign a new head of our list and make sure it references the old head.

In the middle after node with value 5:

1 -> 5 -> 0 -> -2 -> None

This is slower because the computer has no idea where the node with value 5 is until we iterate through to find it. Thus, we look at the head. Is that value 5? No, so we look at the next node whose value is conveniently 5. Now we just insert a new node and make sure it continues the chain of references

At the end:

1 -> 5 -> -2 -> 0 -> None

Another simple case, but takes the longest because we have to iterate through the entire list to find the last node and then add a new node at the end. Again, we make sure this continues our chain of references.

The important thing in all of these examples is that not only did we add a value, but we added a reference to the next node.

Removal

Conversely, we can remove elements from different positions in our linked list. Again, all we have to do is adjust how the nodes reference each other. We start again with the list:

1 -> 5 -> -2 -> None

At the beginning:

5 -> -2 -> None

The fastest since we just take out the first node and do nothing else.

In the middle (remove node with value 5):

1 -> -2 -> None

Slower than removal at the beginning because we have to find node with value 5 to remove it.

At the end:

1 -> 5 -> None

The slowest because we have to find the last node to remove it.

Implementation in Python

Now that we have an idea of the basic building blocks of a singly linked list, let’s look at a basic implementation of this in Python.

We require two classes in order to do this: a Node class and LinkedList class that keeps track of our head and has methods to insert/remove nodes.

Node

Recall that a node keeps track of just two things: a value and a reference to another node. Thus, our __init__() method takes care of these two things for us. We assign the reference self.next later.

LinkedList

The absolute simplest implementation of this class just keeps track of the head.

We can then construct and see our linked list above with the following assignments:

The output of the while loop is

1
5
-2

Great! Let’s look at how we might insert elements.

Insertion: Beginning

Rememeber there are three ways we can insert. Let’s start at the beginning of the linked list.

Note the self parameter due to the fact that this is actually a method within LinkedList.

We follow the same logic as explained in the visual example above. Thus, if we have

llist: 1 -> 5 -> -2

and we run

llist.push(0)

we will get the structure

0 -> 1 -> 5 -> -2 -> None

Insertion: Middle

This method may be fewer steps, but you do have to already have prev_node to insert the new node. In llist_02.py we created llist, second, and third. We could insert a node with value 0 into our linked list by calling

llist.insert_after(second, 0)

giving us

llist: 1 -> 5 -> 0 -> -2 -> None

Insertion: End

To use this in practice all we do is take our linked list and append!

llist.append(0)

Gives

llist: 1 -> 5 -> -2 -> 0 -> None

Removal

This follows the same intuition as described above and will be a nice exercise for anyone following along to try themselves. If not, I’ll have code up on my GitHub with the solutions and more.

Final Thoughts

We can use insertion and removal methods to carry out all sorts of algorithms on this data structure. The repo I linked to in the previous paragraph will ultimately be updated with some examples of this as I work through more problems.

Also, as I mentioned before, there are many flavors of linked lists, not just singly linked lists! We also have doubly linked lists, circular linked lists, etc. I’d highly recommend looking more into these if you’re interested.

--

--