Linked lists in Ruby, part 2
In my previous article, we implemented a linked list data structure with some very basic operations. While those operations are useful in many cases, they won’t take us very far. That’s is why in this (and subsequent) post we’ll talk about more advanced actions we can perform on our linked lists.
Reverse a linked list
Let’s say we have a linked list with elements 1,2,3,4. As you remember, each element points to the next one while the last one points to nil.
Now, how can we reverse a linked list? Let’s take a look at one of the iterative solutions:
Iterative reversal
We have lots of pointers and temporary variables pointing back and forth! Let’s figure out what happens on each iteration.
Iteration 1:
We are not at the end of our list (node is [1, 2, 3, 4]). Continue!
Iteration 2
We are not at the end of our list (node is [2, 3, 4]). Continue!
Iteration 3
We are not at the end of our list (node is [3, 4]). Continue!
Iteration 4
We are not at the end of our list (node is [4]). Continue!
Iteration 5
We are at the end of our list (node is nil). Break!
We then point head
toprevious
(which we used to store our reversed list) and it’s done!
Recursive reversal
So now you understand the iterative approach to this problem. How about a recursive one?
It is useful to understand how the call stack works. Please take a look at the following article for details.
In this example, let’s assume that head
points to the linked list with values 1, 2, 3, 4.
Again, let’s decipher what happens on each recursive call to reverse
method. To make it more transparent, I’ll show the arguments of each new recursive call.
Call 1
We are not at the end of our list (node is [1, 2, 3, 4]). Continue!
Since our previous
node is set to nil by default, we cut off everything AFTER our list’s first element => [1].
Call 2
We are not at the end of our list (node is [2, 3, 4]). Continue!
Inside this call, we slice (shift one step to the right) our list and assign it to next_node
. Then we take our initial (for this call) list ([2, 3, 4]) and set its next
element to previous
([1]). As a result, the second argument for the next recursive call becomes [2, 1]
Call 3
We are not at the end of our list (node is [3, 4]). Continue!
The description is the same:
Call 4
We are not at the end of our list (node is [4]). Continue!
The description is the same:
Call 5
We are at th end of our list (node is nil). Break!
Since our current node is now nil, we’ll set the head of our initial linked list to previous
, which is our list in reverse!