Category: Programming
This article is a work in progress — I’m making it available to readers as I write it!
On Wednesday July 27, 2022, 13 people boarded a bus at The Sail on the Riverwalk in downtown Tampa bound for Austin, Texas to participate in a contest unlike any other: StartupBus 2022. I was one of those 13 people, and this is what happened on (and off) that bus.
StartupBus is the Mother of All Hackathons. The first part of the event is a three-day bus ride where buspreneurs (contestants), with help from conductors (coaches), conceive a technology startup, its software, and marketing and business plans. There are a number of buses that start in different places — in 2022, the buses left from California, Mexico City, Cincinnati, and Tampa — and they spend three days making their toward Austin, where their buspreneurs present their startups at the qualifying, semi-final, and final rounds of judging. It’s a road trip, entrepreneurship crash course, competition, and adventure all in one.
Day 1: On the bus from Tampa to Gainesville and Tallahassee
Boarding the bus
At 6:00 a.m., I arrived at The Sail, the designated pickup loacation. It’s a pavilion located downtown, on the Tampa Riverwalk, just a stone’s throw away from the Tampa Convention Center. The buspreneurs were told that the bus would depart at 7, so I expected to be the first one there. Instead, Mandy was there, and so were a handful of buspreneurs. This was a good sign.
The bus should’ve been there too, but it wasn’t. None of our bus contacts were responding to messages or Mandy’s phone calls.
“Let’s just chalk this up to Murphy’s Law and declare 6:45 as ‘panic o’clock,’” I suggested.
Fortunately, she made contact with the bus people at around panic o’clock, and they told us that they were on their way. That gave us a little more time to chat and get to know each other a little more:
The slight delay gave us a chance to load up on coffee and a little breakfast food. We started boarding the bus soon afterward:
Here’s a shot showing Josh’s photobombing prowess:
…and shortly after 7:30, our bus started making its way toward the highway.
The secret route
While the buspreneurs knew that the bus would start in Tampa on Wednesday morning and arrive in Austin sometime on Friday evening, they didn’t know what route we’d take or what stops we’d make.
The simplest route from Tampa to Austin takes I-75 north to I-10, and then takes I-10 west, a route 1,200 miles (a little over 1900 km) long. If you were to drive that distance at a consistent 70 miles an hour with no stops at all, you could make the trip in a little over 17 hours. Add stops for activities (more about these later), meals, sleep (at hotels or Airbnbs — we weren’t going to sleep on the bus), and bio breaks, and the trip easily expands to fill three days. At least one of the buspreneurs did some map consulting and guessed our route and where we might end up stopping.
Here’s a map of the route we took:
Opening ceremonies
Shortly after everyone had settled in on the bus, it was time to get started with the opening ceremonies. The buspreneurs were already familiar with us conductors, so we got on with the task of having the mentors say something to inspire them. First Cary…
…then Josh:
With the introductory speeches out of the way, the next step was to have the buspreneurs introduce themselves and propose a startup idea.
The buspreneurs got to refine their startup pitches in an online meetup with one of Tampa Bay’s Toastmasters groups, who listened and provided valuable feedback.
After the meetup, the buspreneurs started talking amongst themselves to figure out which startups they should create. Remember, they had only three days to create them!
In the meantime, I got into an extensive conversation with Cary about his life and work, and we discovered that we had both lived in Toronto. Small world!
University of Florida Innovation Hub
At about 10:00 a.m., we arrived in Gainesville, where we paid a visit to the University of Florida’s business incubator, UF Innovate | Accelerate @ The Hub.
Back on the bus
Domi Station, Tallahassee
Day 2: New Orleans
The wiper hack
Stayges
Next Responder
Afterparty on the roof
A night out in New Orleans
Day 3: Austin
Making our way to Texas
Buc-ee’s!
Home stretch
Arrival at the “TikTok Mansion”
Day 4: Qualifying round
Breakfast and getting ready
Capital Factory and the qualifying rounds
Brent Britton and CoreX
Day 2: Semifinals and finals
…because this could happen:
Getting my (rubber) ducks in a row
In programming, there’s a term called rubber duck debugging. It’s a problem-solving technique where you describe the problem you’re facing in as simple a way as possible to an inanimate object, such as a rubber duck.
It’s turned out to be a good way to solve many programming problems. When you describe how something should work and then look at the system and see how it isn’t working, it often becomes easy to spot where the issue is.
You could perform the exact same thing with a person — in fact, it’s one of the things that pair programmers are supposed to do — but when another person isn’t available, a rubber duck can come in pretty handy. So I’ve started a collection.
The newest addition is the Einstein duck, located below the big monitor on the left. I supposed I could’ve ordered it from Amazon, but I bought it at the gift shop the History of Science Museum when I visited Oxford last month.
See also…
The linked list exercises continue! In case you missed them, be sure to check out part 1 and part 2 of this linked list series before moving forward…
Adding an item to the start of a linked list
In this article, the first new feature we’ll implement is the ability to add new items to the start of the list. It’s similar to adding an item to the end of a list, which is why I reviewed it above.
The diagram below shows the first two nodes of a linked list. Recall that a linked list’s head
points to its first node and that every node points to the next one:
Here’s how you add a new node to the start of the list. The new node becomes the first node, and the former first node becomes the second node:
Here are the steps:
- Create a new node. In our implementation, newly-created nodes have a next property that points to
null
,nil
, orNone
by default. - If the list is empty (i.e. head points to
null
,nil
, orNone
), adding it to the list is the same thing as adding it to the start of the list. Pointhead
to the new node and you’re done. - If the list is not empty, you need to do just a little more work.
- Point the new node’s
next
property tohead
. This makes the current first node the one that comes after the new node. - Point
head
at the new node. This makes the new node the first one in the list.
- Point the new node’s
Here’s how I implemented this in Python…
# Python
def add_first(self, value):
new_node = Node(value)
# If the list is empty,
# point `head` to the newly-added node
# and exit.
if self.head is None:
self.head = new_node
return
# If the list isn’t empty,
# make the current first node the second node
# and make the new node the first node.
new_node.next = self.head
self.head = new_node
…and here’s my JavaScript implementation:
# JavaScript
addFirst(value) {
let newNode = new Node(value)
// If the list is empty,
// point `head` to the newly-added node
// and exit.
if (this.head === null) {
this.head = newNode
return
}
// If the list isn’t empty,
// make the current first node the second node
// and make the new node the first node.
newNode = this.head
this.head = newNode
}
Inserting a new item at a specified position in the list
So far, we’ve implemented methods to:
- Add a new item to the start of the list, and
- Add a new item to the end of the list.
Let’s do something a little more complicated: adding a new item at a specified position in the list. The position is specified using a number, where 0 denotes the first position in the list.
Here’s how you add a new node at a specified position in the list.
Here are the steps:
- The function has two parameters:
index
: The index where a new node should be inserted into the list, with0
denoting the first item.value
: The value that should be stored in the new node.
- Create a new node,
new_node
, and putvalue
into it. - Use a variable,
current_index
, to keep track of the index of the node we’re currently looking at as we traverse the list. It should initially be set to0
. - Use another variable,
current_node
, which will hold a reference to the node we’re currently looking at. It should initially be set tohead
, which means that we’ll start at the first item in the list. - Repeat the following as long as
current_node
is not referencingnull
,nil
, orNone
:- We’re looking for the node in the position immediately before the position where we want to insert the new node. If
current_index
is one less thanindex
:- Point the new node at the current node’s next node.
- Point the current node at the new node.
- Return
true
, meaning that we successfully inserted a new item into the list
- If we’re here, it means that we haven’t yet reached the node immediately before the insertion point. Move to the next node and increment
current_index
.
- We’re looking for the node in the position immediately before the position where we want to insert the new node. If
- If we’re here, it means that we’ve gone past the last item in the list.
false
, meaning we didn’t insert a new item into the list.
Here’s how I implemented this in Python…
# Python
def insert_element_at(self, index, value):
new_node = Node(value)
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index - 1:
# We’re at the node before the insertion point!
# Make the new node point to the next node
# and the current node point to the new node.
new_node.next = current_node.next
current_node.next = new_node
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
…and here’s my JavaScript implementation:
// JavaScript
insertElementAt(index, value) {
let newNode = new Node(value)
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index - 1) {
// We’re at the node before the insertion point!
// Make the new node point to the next node
// and the current node point to the new node.
newNode.next = currentNode.next
currentNode.next = newNode
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
Deleting an item from a specified position in the list
Now that we can add a new item at a specified position in the list, let’s implement its opposite: deleting an item from a specified position in the list. Once again, the position is specified using a number, where 0 denotes the first position in the list.
Here’s how you delete a new node from a specified position in the list:
Here are the steps:
- The function has a single parameter,
index
: the index of the node to be deleted, with0
denoting the first item in the list. - Use a variable,
current_index
, to keep track of the index of the node we’re currently looking at as we traverse the list. It should initially be set to0
. - Use another variable,
current_node
, which will hold a reference to the node we’re currently looking at. It should initially be set tohead
, which means that we’ll start at the first item in the list. - Repeat the following as long as
current_node
is not referencingnull
,nil
, orNone
:- We’re looking for the node in the position immediately before the position of the node we want to delete. If
current_index
is one less thanindex
:- Point the current node to the next node’s next node, which removes the next node from the list.
- Set both the next node’s
data
andnext
properties tonull
,nil
, orNone
. At this point, this node is no longer in the list and is an “island” — no object points to it, and it doesn’t point to any objects. It will eventually be garbage collected. - Return
true
, meaning that we successfully deleted the item from the list
- If we’re here, it means that we haven’t yet reached the node immediately before the deletion point. Move to the next node and increment
current_index
.
- We’re looking for the node in the position immediately before the position of the node we want to delete. If
- If we’re here, it means that we’ve gone past the last item in the list.
false
, meaning we didn’t delete the item from the list.
Here’s how I implemented this in Python…
# Python
def delete_element_at(self, index):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index - 1:
# We’re at the node before the node to be deleted!
# Make the current node point to the next node’s next node
# and set the next node’s `data` and `next` properties
# to `None`.
delete_node = current_node.next
current_node.next = delete_node.next
delete_node.data = None
delete_node.next = None
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
…and here’s my JavaScript implementation:
// JavaScript
deleteElementAt(index) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index - 1) {
// We’re at the node before the node to be deleted!
// Make the current node point to the next node’s next node
// and set the next node’s `data` and `next` properties
// to `null`.
const deleteNode = currentNode.next
currentNode.next = deleteNode.next
deleteNode.data = null
deleteNode.next = null
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
The classes so far
Let’s take a look at the complete Node
and LinkedList
classes so far, in both Python and JavaScript.
Python
# Python
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __str__(self):
return f"{self.data}"
class LinkedList:
def __init__(self):
self.head = None
def __str__(self):
if self.head is None:
return('Empty list.')
result = ""
current_node = self.head
while current_node:
result += f'{current_node}\n'
current_node = current_node.next
return result.strip()
def add_last(self, value):
new_node = Node(value)
# If the list is empty,
# point `head` to the newly-added node
# and exit.
if self.head is None:
self.head = new_node
return
# If the list isn’t empty,
# traverse the list by going to each node’s
# `next` node until there isn’t a `next` node...
current_node = self.head
while current_node.next:
current_node = current_node.next
# If you’re here, `current_node` is
# the last node in the list.
# Point `current_node` at
# the newly-added node.
current_node.next = new_node
def __len__(self):
count = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve gone past the last node.
while current_node:
current_node = current_node.next
count += 1
return count
def get_element_at(self, index):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index:
# We’re at the node at the given index!
return current_node.data
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return None
def set_element_at(self, index, value):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index:
# We’re at the node at the given index!
current_node.data = value
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
def add_first(self, value):
new_node = Node(value)
# If the list is empty,
# point `head` to the newly-added node
# and exit.
if self.head is None:
self.head = new_node
return
# If the list isn’t empty,
# make the current first node the second node
# and make the new node the first node.
new_node.next = self.head
self.head = new_node
def insert_element_at(self, index, value):
new_node = Node(value)
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index - 1:
# We’re at the node before the insertion point!
# Make the new node point to the next node
# and the current node point to the new node.
new_node.next = current_node.next
current_node.next = new_node
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
def delete_element_at(self, index):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index - 1:
# We’re at the node before the node to be deleted!
# Make the current node point to the next node’s next node
# and set the next node’s `data` and `next` properties
# to `None`.
delete_node = current_node.next
current_node.next = delete_node.next
delete_node.data = None
delete_node.next = None
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
JavaScript
// JavaScript
class Node {
constructor(data) {
this.data = data
this.next = null
}
toString() {
return this.data.toString()
}
}
class LinkedList {
constructor() {
this.head = null
}
toString() {
if (!this.head) {
return('Empty list.')
}
let output = ""
let currentNode = this.head
while (currentNode) {
output += `${currentNode.data.toString()}\n`
currentNode = currentNode.next
}
return output.trim()
}
addLast(value) {
let newNode = new Node(value)
// If the list is empty,
// point `head` to the newly-added node
// and exit.
if (this.head === null) {
this.head = newNode
return
}
// If the list isn’t empty,
// traverse the list by going to each node’s
// `next` node until there isn’t a `next` node...
let currentNode = this.head
while (currentNode.next) {
currentNode = currentNode.next
}
// If you’re here, `currentNode` is
// the last node in the list.
// Point `currentNode` at
// the newly-added node.
currentNode.next = newNode
}
getCount() {
let count = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve gone past the last node.
while (currentNode) {
currentNode = currentNode.next
count++
}
return count
}
getElementAt(index) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index) {
// We’re at the node at the given index!
return currentNode.data
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return null
}
setElementAt(index, value) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index) {
// We’re at the node at the given index!
currentNode.data = value
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
addFirst(value) {
let newNode = new Node(value)
// If the list is empty,
// point `head` to the newly-added node
// and exit.
if (this.head === null) {
this.head = newNode
return
}
// If the list isn’t empty,
// make the current first node the second node
// and make the new node the first node.
newNode = this.head
this.head = newNode
}
insertElementAt(index, value) {
let newNode = new Node(value)
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index - 1) {
// We’re at the node before the insertion point!
// Make the new node point to the next node
// and the current node point to the new node.
newNode.next = currentNode.next
currentNode.next = newNode
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
deleteElementAt(index) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index - 1) {
// We’re at the node before the node to be deleted!
// Make the current node point to the next node’s next node
// and set the next node’s `data` and `next` properties
// to `null`.
const deleteNode = currentNode.next
currentNode.next = deleteNode.next
deleteNode.data = null
deleteNode.next = null
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
}
Coming up next
It’s time to reverse that list!
Previously in this series
- How to solve coding interview questions: Linked lists, part 2
- How to solve coding interview questions: Linked lists, part 1
- How to solve coding interview questions: Finding the first NON-recurring character in a string in Swift
- How to solve coding interview questions: Looking deeper into finding the first NON-recurring character in a string
- How to solve coding interview questions: The first NON-recurring character in a string
- Coding interview questions: “First recurring character” in Swift
- How to solve coding interview questions: The first recurring character in a string
- I Has the Dumb (or: How I Embarrassed Myself in My Interview with Google) — from way back in 2013
First the advice, then the backstory
Unlike those recipe sites where you have to scroll past lots of backstory and unrelated personal trivia before you get to the actual recipe, I’m going to give you the advice first.
It’s just this: in a hackathon, simple and working beats complex and non-functional.
The demo you build should be all about showing your main idea in action. The user should be able to go to your site or launch the application, use it to perform the intended task or achieve the intended result, and there should be a clear sign that the user succeeded at the end. That’s it. Anything else is gold-plating, and you don’t have time for that in a hackathon, whether you’re allotted an afternoon or, as in the case of StartupBus, three days. On a bus. With lots of interruptions.
Once again, I repeat my best hackathon advice: simple and working beats complex and non-functional.
Want to join StartupBus Florida?
It’s not too late to register to register for StartupBus Florida, which departs Tampa on the morning of Wednesday, July 27 and arrives in Austin, Texas on Friday, July 29 with surprises aplenty in between.
While on the road for three days, you’ll build a startup and its supporting application. Then on Saturday, July 30 and Sunday, July 31 in Austin, you’ll present your startup and application to judges in the semifinals (Saturday) and finals (Sunday).
Need more details? Email me to find out more!
Want to register? Go to StartupBus.com’s Apply page, select Florida, and use the invite code JOEY22.
And now, the backstory…
Here’s a story from a hackathon where I applied this principle and impressed the judges enough for them to make up a new prize category on the spot.
In 2017, GM (yes, the auto manufacturer) held “Makers Hustle Harder” hackathons in a handful of cities to see what people could build on their Next Generation Infotainment (NGI) SDK for in-car console systems.
They held one of these hackathons in Tampa at Tampa Hackerspace. and I offered to help Chris Woodard work on his app idea. I did that for most of the day, and with a couple of hours left, I came up with a goofy idea that I could whip up in very little time.
A little technical background
The NGI SDK made it possible for developers to write apps for the in-car infotainment consoles located in many GM vehicle center dashboards, like the one pictured above. The SDK gives you access to:
- An 800-pixel high by 390-pixel wide touchscreen to receive input from and display information to the user
- The voice system to respond to user commands and provide spoken responses to the user
- Data from nearly 400 sensors ranging from the state of controls (buttons and the big dial) to instrumentation (such as speed, trip odometer, orientation) to car status information (Are there passengers in the car? Are the windows open or closed?) and more.
- The navigation system to get and set navigation directions
- The media system to play or stream audio files
- The file system to create, edit, and delete files on the system
- An inter-app communication system so that apps can send messages to each other
With the SDK, developers could build and test apps for GM cars on your their own computers. It came with an emulator that lets you see your apps as they would appear on the car’s display, simulate sensor readings, and debug your app with a specialized console.
The hackathon
I arrived at Tampa Hackerspace that morning, and it was already abuzz with activity:
Outside in the parking lot were 3 NGI-equipped GM vehicles provided by Crown, a local auto dealer. Two of them were Buick Lacrosse sedans…
…and one was a GM Sierra truck:
The NGI team were there to answer our questions and help us install our apps onto the in-car console to give them some non-emulator, on-the-real-thing testing.
I performed a “smoke test” on my test app, Shotgun (an app that takes a list of names and randomly decides which one gets to “ride shotgun”) early in the morning on the Sierra’s console…
…and I have to say that there’s nothing like the feeling when your code runs for the first time on a completely new-to-you platform.
My main reason for being there was to help out Chris Woodard (whom I knew from his Cocoa / iOS programming Meetup group) on WeatherEye, his app that provides live weather reports for your planned route as you drove. When we completed it early in the afternoon, I ran a smoke test on it, and it worked as well.
With a couple of hours of “hacking time” left, I came up with a silly idea and coded it up: a timer for the game classically known as the “Chinese Fire Drill”. Here’s how it worked:
- Four people get in the car, close the doors, and someone starts the app. They’ll see this screen:
- When everyone’s ready, someone in the front presses the start button.
- If any of the doors are open when the start button is pressed, the players will be told to close all the doors first:
- If all the doors are closed when the start button is pressed, the game begins. The screen looks like this:
- Players exit the car, run around it once, return to their original seat, and close their doors.
- The game ends when all four doors are closed, at which point the time it took them to complete the drill is displayed:
(If you’d like to see the code, I’ve put it in a repo on my GitHub account.)
The app wasn’t pretty, but that’s not what hackathons are about — they’re about getting your idea to work in the time allotted. Remember: simple and working beats complex and non-functional.
I found an available test vehicle, three other people to playtest the game with me, and two camera operators to record video of a test runs. We played the game twice, and we were giggling all the time. Fitness Fire Drill (I decided to not call it Chinese Fire Drill, as the term comes from an era when “Chinese” was used as a pejorative adjective to mean sloppy, sub-par, or amateurish) was a success!
Everyone who built a project presented it at the end of the day to the panel of judges, and the organizers saved Fitness Fire Drill for the very end — it got a lot of laughs.
In the end…
My wife Anitra was flying out early the following morning on business, so rather than stay for the hackathon dinner and judges’ results, I high-tailed it home to have dinner with her. Before going to bed, I noticed that Chris had sent me an email telling me that Fitness Fire Drill won the “Judges’ Fetish” prize (a category they’d made up just for my submission), something I wasn’t expecting!
From that outcome, I learned what I now call the First Rule of Hackathons: simple and working beats complex and non-functional.
In case you missed part 1…
Before you continue reading, be sure to check out part 1! It’s where I explain what a linked list is, and provide a couple of basic classes that we’ll build on in this article.
A quick recap
A linked list is a data structure that holds data as a list of items in a specific order. Each item in the list is represented by a node, which “knows” two things:
- Its data
- Where the next node is
Here’s a picture of a node’s structure:
Linked lists are created by connecting nodes together, as shown in the diagram below for a list containing the characters a
, b
, c
, and d
in that order:
The diagram above features the following:
- Four nodes, each one for a letter in the list.
- A variable called
head
, which is a pointer or reference to the first item in the list. It’s the entry point to the list, and it’s a necessary part of the list — you can’t access a linked list if you can’t access its head. - An optional variable called
tail
, which is a pointer or reference to the last item in the list. It’s not absolutely necessary, but it’s convenient if you use the list like a queue, where the last element in the list is also the last item that was added to the list.
The JavaScript version of the previous article’s code
In the previous article, I provided code for the Node
and LinkedList
classes in Python. As promised, here are those classes implemented in JavaScript.
// JavaScript
class Node {
constructor(data) {
this.data = data
this.next = null
}
toString() {
return this.data.toString()
}
}
class LinkedList {
constructor() {
this.head = null
}
toString() {
let output = ""
let currentNode = this.head
while (currentNode) {
output += `${currentNode.data.toString()}\n`
currentNode = currentNode.next
}
toString() {
if (!this.head) {
return('Empty list.')
}
let output = ""
let currentNode = this.head
while (currentNode) {
output += `${currentNode.data.toString()}\n`
currentNode = currentNode.next
}
return output.trim()
}
addLast(data) {
let newNode = new Node(data)
// If the list is empty,
// point `head` to the newly-added node
// and exit.
if (this.head === null) {
this.head = newNode
return
}
// If the list isn’t empty,
// traverse the list by going to each node’s
// `next` node until there isn’t a `next` node...
let currentNode = this.head
while (currentNode.next) {
currentNode = currentNode.next
}
// If you’re here, `currentNode` is
// the last node in the list.
// Point `currentNode` at
// the newly-added node.
currentNode.next = newNode
}
}
…where each item in a list is represented by the node. A node knows only about the data it contains and where the next node in the list is.
The JavaScript version of Node
and LinkedList
uses the same algorithms as the Python version. The Python version follows the snake_case
naming convention, while the JavaScript version follows the camelCase
convention.
The JavaScript LinkedList class has the following members:
head
: A property containing a pointer or reference to the first item in the list, ornull
if the list is empty. This property has the same name in the Python version.constructor()
: The class constructor, which initializeshead
tonull
, meaning that any newly createdLinkedList
instance is an empty list. In the Python version, the__init__()
method has this role.toString()
: Returns a string representation of the contents of the linked list for debugging purposes. If the list contains at least one item, it returns the contents of the list. If the list is empty, it returns the stringEmpty list
. In the Python version, the__str__()
method has this role.addLast()
: Given a value, it creates a newNode
containing that value and adds thatNode
to the end of the list. In the Python version, theadd_last()
method has this role.
Adding more functionality to the list
In this article, we’re going to add some much-needed additional functionality to the LinkedList class, namely:
- Reporting how many elements are in the list
- Getting the value of the nth item in the list
- Setting the value of the nth item in the list
I’ll provide both Python and JavaScript implementations.
How many elements are in the list?
To find out how many elements are in a linked list, you have to start at its head and “step through” every item in the list by following each node’s next
property, keeping a count of each node you visit. When you reach the last node — the one whose next
property is set to None
in Python or null
in JavaScript — report the number of nodes you counted.
The Python version uses one of its built-in “dunder” (Pythonese for double-underscore) methods, __len__()
, to report the number of items in the list. Defining __len__()
for LinkedList means that any LinkedList
instance will report the number of items it contains when passed as an argument to the built-in len()
function. For example, if a LinkedList
instance named my_list
contains 5 items, len(my_list)
returns the value 5
.
Here’s the Python version, which you should add to the Python version of LinkedList
:
# Python
def __len__(self):
count = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve gone past the last node.
while current_node:
current_node = current_node.next
count += 1
return count
The JavaScript version is getCount()
. If a LinkedList
instance named myList
contains 5 items, len(myList)
returns the value 5
.
Here’s the JavaScript version, which you should add to the JavaScript version of LinkedList
:
// JavaScript
getCount() {
let count = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve gone past the last node.
while (currentNode) {
currentNode = currentNode.next
count++
}
return count
}
How do I get the data at the nth node in the list?
To get the data at the nth node of the list, you can use an approach that’s similar to the one for counting the list’s elements. You have to start at the head and “step through” every item in the list by following each node’s next
property, keeping a count of each node you visit and comparing it against n, where n is the index of the node whose data you want. When the count of nodes you’ve visited is equal to n, report the data contained within the current node.
Here’s the Python version, get_element_at()
, which you should add to the Python version of LinkedList
:
# Python
def get_element_at(self, index):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index:
# We’re at the node at the given index!
return current_node.data
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return None
Here’s the JavaScript version, getElementAt()
, which you should add to the JavaScript version of LinkedList
:
// JavaScript
getElementAt(index) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index) {
// We’re at the node at the given index!
return currentNode.data
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return null
}
How do I set the value of the nth node in the list?
To set the data at the nth node of the list, you can use an approach that’s similar to the one for getting the data at the nth node. You have to start at the head and “step through” every item in the list by following each node’s next
property, keeping a count of each node you visit and comparing it against n, where n is the index of the node whose data you want. When the count of nodes you’ve visited is equal to n, set the current node’s data
property to the new value.
Here’s the Python version, set_element_at()
, which you should add to the Python version of LinkedList
:
# Python
def set_element_at(self, index, value):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index:
# We’re at the node at the given index!
current_node.data = value
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
Here’s the JavaScript version, setElementAt()
, which you should add to the JavaScript version of LinkedList
:
// JavaScript
setElementAt(index, value) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index) {
// We’re at the node at the given index!
currentNode.data = value
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
The classes so far
Let’s take a look at the complete Node
and LinkedList
classes so far, in both Python and JavaScript.
Python
# Python
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __str__(self):
return f"{self.data}"
class LinkedList:
def __init__(self):
self.head = None
def __str__(self):
if self.head is None:
return('Empty list.')
result = ""
current_node = self.head
while current_node:
result += f'{current_node}\n'
current_node = current_node.next
return result.strip()
def add_last(self, value):
new_node = Node(value)
# If the list is empty,
# point `head` to the newly-added node
# and exit.
if self.head is None:
self.head = new_node
return
# If the list isn’t empty,
# traverse the list by going to each node’s
# `next` node until there isn’t a `next` node...
current_node = self.head
while current_node.next:
current_node = current_node.next
# If you’re here, `current_node` is
# the last node in the list.
# Point `current_node` at
# the newly-added node.
current_node.next = new_node
def __len__(self):
count = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve gone past the last node.
while current_node:
current_node = current_node.next
count += 1
return count
def get_element_at(self, index):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index:
# We’re at the node at the given index!
return current_node.data
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return None
def set_element_at(self, index, value):
current_index = 0
current_node = self.head
# Traverse the list, keeping count of
# the nodes that you visit,
# until you’ve reached the specified node.
while current_node:
if current_index == index:
# We’re at the node at the given index!
current_node.data = value
return True
# We’re not there yet...
current_node = current_node.next
current_index += 1
# If you’re here, the given index is larger
# than the number of elements in the list.
return False
JavaScript
// JavaScript
class Node {
constructor(data) {
this.data = data
this.next = null
}
toString() {
return this.data.toString()
}
}
class LinkedList {
constructor() {
this.head = null
}
toString() {
if (!this.head) {
return('Empty list.')
}
let output = ""
let currentNode = this.head
while (currentNode) {
output += `${currentNode.data.toString()}\n`
currentNode = currentNode.next
}
return output.trim()
}
addLast(value) {
let newNode = new Node(value)
// If the list is empty,
// point `head` to the newly-added node
// and exit.
if (this.head === null) {
this.head = newNode
return
}
// If the list isn’t empty,
// traverse the list by going to each node’s
// `next` node until there isn’t a `next` node...
let currentNode = this.head
while (currentNode.next) {
currentNode = currentNode.next
}
// If you’re here, `currentNode` is
// the last node in the list.
// Point `currentNode` at
// the newly-added node.
currentNode.next = newNode
}
getCount() {
let count = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve gone past the last node.
while (currentNode) {
currentNode = currentNode.next
count++
}
return count
}
getElementAt(index) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index) {
// We’re at the node at the given index!
return currentNode.data
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return null
}
setElementAt(index, value) {
let currentIndex = 0
let currentNode = this.head
// Traverse the list, keeping count of
// the nodes that you visit,
// until you’ve reached the specified node.
while (currentNode) {
if (currentIndex === index) {
// We’re at the node at the given index!
currentNode.data = value
return true
}
// We’re not there yet...
currentNode = currentNode.next
currentIndex++
}
// If you’re here, the given index is larger
// than the number of elements in the list.
return false
}
}
Wait…this “linked list” thing is beginning to look like a Python or JavaScript array, but not as fully-featured. What’s going on here?
The truth about linked lists
Here’s the truth: there’s a better-than-even chance that you’ll never use them…directly.
If you program in most languages from the 1990s or later (for example, Python was first released in 1991, PHP’s from 1994, Java and JavaScript came out in 1995), you’re working with arrays — or in Python’s case, lists, which are almost the same thing — that you can you can add elements to, remove elements from, perform searches on, and do all sorts of things with.
When you use one of these data types in a modern language…
- Python lists or a JavaScript arrays
- Dictionaries, also known as objects in JavaScript, hashes in Ruby, or associative arrays
- Stacks and queues
- React components
…there’s a linked list behind the scenes, moving data around by traversing, adding, and deleting nodes. There’s a pretty good chance that you’re indirectly using linked lists in your day-to-day programming; you’re just insulated from the details by layers of abstractions.
So why do they still pose linked list challenges in coding interviews?
History plays a major factor.
In older languages like FORTRAN (1957), Pascal (1970), and C (1972), arrays weren’t dynamic, but fixed in size. If you declared an array with 20 elements, it remained a 20-element array. You couldn’t add or remove elements at run-time.
But Pascal and C supported pointers from the beginning, and pointers make it possible to create dynamic data structures — the kind where you can add or delete elements at run-time. In those early pre-web days, when programming languages’ standard libraries weren’t as rich and complete as today’s, and you often had to “roll your own” dynamic data structures, such as trees, queues, stacks, and…linked lists.
If you majored in computer science in the ’70s, ’80s, and even the ’90s, you were expected to know how to build your own dynamic data structures from scratch, and they most definitely appeared on the exam. These computer science majors ended up in charge at tech companies, and they expected the people they hired to know this stuff as well. Over time, it became tradition, and to this day, you’ll still get the occasional linked list question in a coding interview.
What’s the point in learning about linked lists when we’re mostly insulated from having to work with them directly?
For starters, there’s just plain old pragmatism. As long as they’re still asking linked list questions in coding interviews, it’s a good idea to get comfortable with them and be prepared to answer all manner of questions about algorithms and data structures.
Secondly, it’s a good way to get better at the kind of problem solving that we need in day-to-day programming. Working with linked lists requires us to think about pointers/references, space and time efficiency, tradeoffs, and even writing readable, maintainable code. And of course, there’s always a chance that you might have to implement a linked list yourself, whether because you’re working on an IoT or embedded programming project with a low-level language like C or implementing something lower-level such as the reconciler in React.
Coming up next
- Adding a new element to the head of a linked list
- Adding a new element at a specific place in the middle of a linked list
- Deleting a specified element of a linked list
Previously in this series
- How to solve coding interview questions: Linked lists, part 1
- How to solve coding interview questions: Finding the first NON-recurring character in a string in Swift
- How to solve coding interview questions: Looking deeper into finding the first NON-recurring character in a string
- How to solve coding interview questions: The first NON-recurring character in a string
- Coding interview questions: “First recurring character” in Swift
- How to solve coding interview questions: The first recurring character in a string
- I Has the Dumb (or: How I Embarrassed Myself in My Interview with Google) — from way back in 2013