For me one of the biggest things about programming is being able to visualize what I’m trying to do and translate that easily into code. It’s like building muscle memory when you’re dancing or snowboarding, or doing beginner’s scales on the piano, or figure drawing.
(You go by the motions because if you practice long and hard enough you can get to the point where it becomes second nature, and more improvisation and complex dialogues are possible, and then you may one day be playing jazz in the perfect state of flow.)
The Humble Node
The Linked List Node and the Linked List are the the basic building blocks of algorithms, and what makes it hard for a beginner is that you don’t usually manipulate these data structures much in everyday programming.
But these little exercises can teach so much! They’re quite magical. They teach you how to loop through data, walking and then running to the end, and then hopping back and forth across more and more intricate fabrications.
Ah, first there is the node. Such a basic unit, but it is found everywhere. Here it is in Swift:
public class ListNode {
public var val: Int
public swvar next: ListNode?
public init(_ val: Int) {
self.val = val
self.next = nil
}
}
In python:
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.nextval = None
And in C:
typedef struct node {
int val;
struct node * next;
} node_t;swiftThe LinkedList
And then put the nodes together into a Linked List, and you start to understand how memory works on your computer:
public class LinkedList {
public var head: ListNode?
public var tail: ListNode?
public init() {}
public var isEmpty: Bool {
return head == nil
}
func push(_ val: Int) {
let newHead = ListNode(val)
newHead.next = head
head = newHead
if tail == nil {
tail = head
}
}
public func append(_ val: Int) {
guard !isEmpty else {
push(val)
return
}
guard let tail = tail else { return }
tail.next = ListNode(val)
}
}
The next nodes point to other nodes all over the place, not necessarily sitting next to each other as do arrays.
What's at the End?
One day you speak to a runner and ask her, “I want to know what’s at the end of this list.”
“How long is this list?” she asks.
“I don’t know, it could go on for thousands and thousands of nodes.”
“Then how do I get to the end?
let head = ListNode(1)
head.next = ListNode(5)
head.next?.next = ListNode(42)
var runner = head
while let nextNode = runner.next {
runner = nextNode
}
“Well, let’s start at the beginning.”
var runner = head
“And then let’s keep going until there is no next. Then you’ll know you’re at the end.”
while let nextNode = runner.next {
runner = nextNode
}
“Is there a next now?”
“Yes!”
“Ill keep going!” she says.
And on, and on…
“Oh wait! There is no next! I’ve found the end.”
“And what’s there?”
“Strange… just the number 42. Must be important,” she says.
“It’s the answer to everything.”
print(runner)
// 42
Tortoise and the Hare
But what if there is no next, there is no end? What if the list links back to itself and is a cycle?
Our poor runner could just go on running forever in circles!
var head = ListNode(5)
head.next = ListNode(7)
let secondNode = head.next
head.next?.next = ListNode(8)
head.next?.next?.next = ListNode(12)
head.next?.next?.next?.next = secondNode
Then you need not one helper, but two, a tortoise and a hare.
In this case the hare will go two ahead at a time, twice as fast as the tortoise. We all know that rabbits are faster than turtles, right?
(Although in faraway lands, tortoises have been known to invent strange machines and eventually outrun the hare… but let’s not get ahead of ourselves.)
For this case we also assume that no nodes have the same value in the list. Maybe they are each unique, like values in a Bitcoin Merkel Tree. Maybe they are unique numbers showing your DNA, which belongs to you and only you.
We let both our hare and our tortoise start at the beginning of the list.
var tortoise = head
var hare = head
The hare goes two ahead at a time:
while let hareNext = hare.next?.next,
let tortoiseNext = tortoiseNext.next
If either hareNext or tortoiseNext is nil, then we know we are at the end. We’ll break out of the while loop.
But since this is a cycle, that will not happen! They will just keep going on forever and ever, unless the rabbit catches up with the hare… which it does!
if hare.val == tortoise.val {
return true
}
func hasCycle(head: ListNode) -> Bool {
while let hareNext = hare.next?.next, let tortoiseNext = tortoise.next {
hare = hareNext
tortoise = tortoiseNext
if hare.val == tortoise.val {
return true
}
}
return false
}
Quite clever, isn’t it? It’s moments like these that open the door to more and more clever tricks, and starting from here algorithms become less painful and start to get very fun!
But before you can start to compose your own music, and before you can meet other musicians and jam, sometimes you have to practice your scales first and learn your lessons. You’ll get there!
More
These places led the way for me:
RayWenderlich Swift Algorithm Club they have a cool video series now too! And they have a book and video series too.
Going to a meetup with like-minded people at the Noisebridge Hackerspace and Women Who Code SF — preferably near walkable public transportation
Hanging out with crazy elements, like mathematicians at SudoRoom whiteboarding nights
And it’s nice to watch videos but it’s best to do code itself, so I’ve been having fun with LeetCode and Hackerrank