Singly Linked Lists
What is a singly linked list?
How does it differ from arrays?
Defining
SinglyLinkedList
class with insertion, removal, traversal methods
Definition
A linked list stores any data types in an order just like an array.
However, arrays are indexed with a number, and you can select elements at specific indices.
In contrast, linked lists just consist of nodes that each contain a value and a pointer to the next node (or null for the last node).
Additionally, linked lists keep track of the head node, the tail node, and the length of the list.
Now a singly linked list means that each node is only connected one-directionally to the next node. (A doubly linked list is connected both ways.)
Comparison to Arrays
Because arrays have indices, it's easy to perform random access: to request the 4th element, we just go arr[3]
. With singly linked lists, there is no random access; we have to start at the head and follow the pointers to the 4th element.
However, one thing that singly linked lists are good at is insertion and deletion. For example, arrays have a hard time adding to the beginning because everything has to be re-indexed. With singly linked lists, you simply define a new node as the head and set the pointer to the first element.
Singly Linked List Class
Basic constructors
To start, we want to define a Node
with a value and a reference to the next node.
Push
The basic requirements of a push are that you
Set the new node to both head and tail if it's the first element.
Otherwise, set the current tail's
next
to the new node and then repoint the tail to the new node.Finally, increment the
length
.
Pop
With a pop, your goal is to
Store the current tail in a variable.
Change the second-last element to the new tail and set its
next
to null.Decrement the
length
.Return the stored variable.
Note: In order to get to the secon-last element, you actually have to traverse from the head, making pop O(n)
.
Shift
Shift is simple. All you have to do is
Store the current head in a variable.
Set the head's
next
as the new head.Set the saved old head's
next
to null.Decrement the
length
.Return the stored variable.
Unshift
Unshift is also very simple. You just
Point the new node to the current head.
Set the new node as the new head.
Increment the
length
.
Get
The basic idea of get is to
Accept an index.
If the index is out of bounds, return
undefined
.Otherwise, loop through the list until you reach the index.
Set
Set is super simple in that you can just use get
and re-set the val
property of the node it returns.
Insert
Insert accepts an index
and val
and adds a node at that index, pointing the node before it to the new node and pointing the new node to what the node before it was previously pointing at.
What makes insert unique is that you can leverage other methods!
If the index is less than zero or greater than the length, return false.
If the index is equal to the length, use
push
.If the index is 0, use
unshift
.Otherwise, use
get
to find the element atindex - 1
.Then create a new node and set its pointer to the previous element's pointer.
Finally, set the previous element's pointer to the new node.
Increment
length
and return true.
Remove
Remove works just like insert but takes away an element:
If the index is less than zero or greater than or equal to the length, return undefined.
If the index is equal to
length - 1
, use and returnpop
.If the index is equal to zero, use and return
shift
.Otherwise, find the node at
index - 1
.Store that node's pointer in a variable. That's your target node.
Set that node's pointer to its
next.next
.Decrement the
length
and return the target node.
Reverse
The basic idea behind reverse is to loop through your linked list and keep track of 3 nodes: the current node, the one before it, and the one after. The goal is to set the current node's pointer to the node before it. Then you shift the 3 nodes being tracked to the right.
Note: Before starting this loop though, you want to swap the head and tail.
Think of reverse as looping through the list and flipping the direction of the pointer as it goes:
The pointers are now correctly reversed. You then just flip the head and tail properties for sake of record.
Big O
Insertion (push and unshift) is O(1)
because all you're doing is re-setting pointers and redefining the head or tail. In contrast, arrays are only O(1)
for push. Unshift requires re-indexing.
Removal is O(1)
(for shift) or O(n)
(for pop). That's because pop requires following the pointers to the second-last node.
Search is O(n)
. In contrast, search in arrays vary from O(n)
(e.g. linear search) to O(log n)
(e.g. binary search).
Random access is O(n)
via get
because you have to follow the pointers to reach your desired node. In contrast, arrays are always O(1)
because indexing makes access super quick.
TLDR: Singly linked lists are best when you're realy concerned insertion or removal times but don't really care about random access. This is especially true when you care about insertion at the beginning.
Last updated