
We’ve all been there.
We’ve all been there.
“To sustain the United States’ technology leadership in the face of China’s formidable economic and military challenge,” write Graham Allison, a professor of government at the Harvard Kennedy School and Eric Schmidt, former CEO and executive chairman of Google in Foreign Policy, “U.S. President Joe Biden should launch an urgent drive to recruit and retain 1 million tech superstars from around the world by the end of his first term in office.”
Their article, The U.S. Needs a Million Talents Program to Retain Technology Leadership, starts with a story about one key player we lost in our failure to take in new people.
In 2009, Erdal Arikan, a Turkish graduate of the California Institute of Technology and MIT published a paper that solved a big information theory problem. He could’ve continued his work in the U.S., but he had to leave because he couldn’t get an academic appointment or sponsorship to stay. He returned to Turkey, turned to China, and Huawei — yes, that Huawei — used his work to create 5G solutions.
The article sums us the situation like so:
And while Huawei has produced one-third of the 5G infrastructure now operating around the world, the United States does not have a single major company competing in this race. Had the United States been able to retain Arikan—simply by allowing him to stay in the country instead of making his visa contingent on immediately finding a sponsor for his work—this history might well have been different.
And with the recent discovery that Huawei equipment installed as part of U.S. communications infrastructure could have a secondary purpose disrupting nuclear arsenal communications, the magnitude of our failure to let Arikan stay in the country has become even more apparent.
China’s leader Xi Jinping said it himself: “technological innovation has become the main battleground of the global playing field, and competition for tech dominance will grow unprecedentedly fierce.”
In 2001, China was still behind technologically. But in the space of only a couple of decades, they’ve leapfrogged the West in communications, facial and voice recognition, other aspects of AI, and green technology, and their education system is producing four times the STEM bachelor’s degrees and twice as many graduate degrees.
Their insularity remains their Achilles’ heel. They naturalize fewer than 100 citizens a year, while the U.S. does so with 1 million a year. That’s a difference of four orders of magnitude.
And what a difference it makes! Half of all U.S. unicorns were founded or co-founded by immigrants, and there are so many big-ass, big-deal companies founded or run by people born outside the U.S.:
Big-ass company | Founder or leader | Where they were born |
Auth0 | Eugenio Pace, founder | Argentina |
Cloudflare | Michelle Zatlyn, founder | Canada |
Credit Karma | Kenneth Lin, founder | China |
Crowdstrike | Dmitri Alperovitch, founder | Russia |
Databricks | Ali Ghodsi, founder | Iran |
Discord | Stanislav Vishnevsky, founder | Ukraine |
Eventbrite | Renaud Visage, founder | France |
Evernote | Stepan Pachikov, founder | Azerbaijan |
Sergei Brin, founder | Russia | |
Sundar Pinchai, CEO | India | |
Instacart | Apoorva Mehta, founder | India |
Instacart | Fidji Simo, CEO | France |
Microsoft | Satya Nadella, CEO | India |
Nvidia | Jensen Huang, founder | Taiwan |
Palantir | Peter Thiel, founder | Germany |
Peloton | Yony Feng | China |
Robinhood | Vlad Tenev, founder | Bulgaria |
Slack | Stewart Butterfield, cofounder | Canada |
SpaceX, Tesla | Elon Musk, CEO | South Africa |
Sprinklr | Ragy Thomas, founder | India |
Stripe | Patrick and John Collison, founders | Ireland |
Uber | Garrett Camp, founder | Canada |
Warby Parker | David Gilboa | Sweden |
Zoom | Eric Yuan | China |
Zscaler | Jay Caudhry | India |
I deliberately included German-born Peter Thiel in the list particularly because he spends a lot of time canoodling with the anti-immigrant crowd. He’s one of those people who says “Ever since my family came to this country, we’ve had nothing but trouble from the immigrants.”
Let’s also not forget that Apple founder Steve Jobs was the son of a Syrian immigrant, and that there are many children of immigrants who’ve contributed so much.
That’s what Allison and Schmidt write, and what they mean is that the U.S. should:
…and most importantly:
There’s more in the article, and I strongly encourage you to read it.
And hey, if you’re in Tampa Bay and looking for an example of a highly-skilled green card holder who’s making a difference, let me point you to the handsome well-dressed gentleman in the photo below:
Don’t let my accent, grasp of the culture, or accordion skill fool you — I’m a first-generation green card holder from the Philippines, and I’m here to make a difference in the tech world, locally and nationally.
(If you’re really curious, check out my article about my green card interview, which took place a week after the Trump inauguration.)
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.
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.
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.
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:
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.
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:
(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.
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.
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 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:
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:
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.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.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, or null
if the list is empty. This property has the same name in the Python version.constructor()
: The class constructor, which initializes head
to null
, meaning that any newly created LinkedList
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 string Empty list
. In the Python version, the __str__()
method has this role.addLast()
: Given a value, it creates a new Node
containing that value and adds that Node
to the end of the list. In the Python version, the add_last()
method has this role.In this article, we’re going to add some much-needed additional functionality to the LinkedList class, namely:
I’ll provide both Python and JavaScript implementations.
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
}
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
}
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
}
Let’s take a look at the complete Node
and LinkedList
classes so far, in both Python and JavaScript.
# 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
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
}
}
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…
…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.
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.
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.
The Coders, Creatives, and Craft Beer meetup is coming back, starting Tuesday, July 19th at 6:30 p.m. at Green Bench Brewing, where we’ll be throwing a warm-party for StartupBus Florida!
StartupBus is no ordinary hackathon. It doesn’t happen in an afternoon in the comfortable surroundings of a coworking space, cafe, or corporate office. It takes THREE DAYS. On a BUS. With all the regular problems posed by a road trip (such as unreliable power and spotty wifi) as well as additional challenges that the organizers have devised. It’s been described as “Navy SEAL training for techies and entrepreneurs.”
While on the bus, the participants — we call them buspreneurs — form teams, come up with an idea for a startup, and build the business and the software that supports it. At the same time, they’re working on a pitch for that business, and practicing the pitch regularly.
After three days when the bus reaches its destination city — Austin, Texas — the real competition begins. StartupBus Florida’s bus will meet up with the other 4 buses coming from different parts of the U.S. and Mexico, and all the teams will pitch in front of judges in the semifinals. A handful of the best teams will go on to the finals, where they’ll pitch to the finals judges. And one will emerge victorious.
StartupBus Florida traditionally departs from Tampa Bay, and we have a solid track record. We had two teams make it to the finals in 2017 and 2019, and our alumni have gone on to start their own companies, build notable careers, and help grow Tampa’s tech scene. This will be the first StartupBus event since 2019, and we’re excited to be back!
StartupBus Florida doesn’t do what it does alone — it’s backed by our favorite hometown heroes: our sponsors! They are…
Join us at the StartupBus Warm-Up! Come meet StartupBus Florida’s buspreneurs (contestants) and conductors (coaches), sponsors, alumni, and fans at Green Bench Brewing in St. Pete.
We’re about a week and a half away from Wednesday, July 27th, when four buses — one from here in Tampa Bay, along with buses from Cincinnati, Silicon Valley, and Mexico City — will start a three-day journey to Austin Texas.
During those three days, the buses’ riders will be participating in a hackathon, where they’ll be challenged to:
We’ve got a lot of idea, business, and design people on this year’s StartupBus Florida, but we need more developers.
This is a call to developers in the Tampa Bay area and beyond to join StartupBus Florida. You don’t have to be from Tampa Bay or even Florida to ride StartupBus Florida — you just have to be up for a hackathon, road trip, and personal growth all rolled up together!
I’ve participated in and organized plenty of hackathons, and I’ve seen so many people resist participating because they believe that they’re not good enough coders. I think that if you can code, you’re good enough — and more importantly, participating in a hackathon can make you a better coder!
Here’s the thing: in a hackathon, you’re not coding a full-fledged app in its final form. You’re building a prototype application that demonstrates what would be possible if you had the time and resources to build a full-fledged app in its final form. You’re building the thing that makes the difference between a startup idea and a startup reality.
If you’re unsure about joining StartupBus Florida as a coder, ask yourself these questions:
If you can answer “yes” to questions 1 and 4, as well as answer “yes” to either questions 2 or 3 (or both), you should join StartupBus Florida as a coder!
Can you code a back end in Express, Flask, or Laravel? You should code on StartupBus Florida.
Can you code a front end in React, Next, Vue, Angular, Svelte or any other framework? You should code on StartupBus Florida.
Can you write a mobile app in Swift, Kotlin, Xamarin, Ionic, or React Native? You should code on StartupBus Florida.
Sign up on the StartupBus “Apply” page, select “Florida” and use the code JOEY22.
If you’re interviewing for a position that requires you to code, the odds are pretty good that you’ll have to face a coding interview. This series of articles is here to help you gain the necessary knowledge and skills to tackle them!
Over the next couple of articles in this series, I’ll walk you through a classic computer science staple: linked lists. And by the end of these articles, you’ll be able to handle this most clichéd of coding interview problems, satirized in the meme below:
You’ve probably seen the ads for AlgoExpert with the woman pictured above, where she asks you if you want a job at Google and if you know how to reverse a linked list.
But before you learn about reversing linked lists — or doing anything else with them — let’s first answer an important question: what are they?
A linked list is a data structure that holds data as a list of items in a specific order. Linked lists are simple and flexible, which is why they’re the basis of many of the data structures that you’ll probably use in your day-to-day programming.
The basic component of a linked list is a node, which is diagrammed below:
In its simplest form, a node is a data structure that holds two pieces of information:
data
: This holds the actual data of the list item. For example, if the linked list is a to-do list, the data could be a string representing a task, such as “clean the bathroom.”next
: A linked list is simply a set of nodes that are linked together. In addition to the data it holds, a node should also know where the next node is, and that’s what its next
property is for: it points to the next item in the list. In languages like C and Go, this would be a pointer, while in languages like C#, Java, JavaScript, Kotlin, Python, and Swift, this would be a reference.Here’s a Python implementation of a node. We’ll use it to implement a linked list:
# Python
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __str__(self):
return f"{self.data}"
With Node defined, you can create a new node containing the data “Hello, world!” with this line of code:
new_node = Node("Hello, world!")
To print the data contained inside a node, call Node’s
print()
method, which in turn uses the value returned by its __str__()
method:
print(new_node)
# Outputs “Hello, world!”
You create a linked list by connecting nodes together using their next
properties. For example, the linked list in the diagram below represents a list of the first four letters of the alphabet: a, b, c, and d, in that order:
The diagram above features the following:
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.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.Let’s build the linked list pictured above by putting some nodes together:
# Python
# Let’s build this linked list:
# a -> b -> c -> d
head = Node("a")
head.next = Node("b")
head.next.next = Node("c")
head.next.next.next = Node("d")
That’s a lot of next
s. Don’t worry; we’ll come up with a better way of adding items to a linked list soon.
Here’s an implementation that creates the same list but doesn’t rely on chaining next
s:
# Python
# Another way to build this linked list:
# a -> b -> c -> d
head = Node("a")
node_b = Node("b")
head.next = node_b
node_c = Node("c")
node_b.next = node_c
node_d = Node("d")
node_c.next = node_d
To see the contents of the list, you traverse it by visiting each node. This is possible because each node points to the next one in the list:
# Python
print(head)
print(head.next)
print(head.next.next)
print(head.next.next.next)
print(head.next.next.next.next)
Here’s the output for the code above:
a
b
c
d
None
Of course, the code above becomes impractical if you don’t know how many items are in the list or as the list grows in size.
Fortunately, the repetition in the code — all those print
s and next
s — suggests that we can use a loop to get the same result:
# Python
current = head
while current is not None:
print(current)
current = current.next
Here’s the output for the code above:
a
b
c
d
Working only with nodes is a little cumbersome. Let’s put together a linked list class that makes use of the Node class
:
# Python
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 is not None:
result += f'{current_node}\n'
current_node = current_node.next
return result.strip()
def add_last(self, data):
new_node = Node(data)
# 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
This LinkedList
class has the following members:
head
: A property containing a pointer or reference to the first item in the list, or None
if the list is empty.__init__()
: The class initializer, which initializes head
to None
, meaning that any newly created LinkedList
instance is an empty list.__str__()
: 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 string Empty list
.add_last()
: Given a value, it creates a new Node
containing that value and adds that Node
to the end of the list.With this class defined, building a new list requires considerably less typing…
# Python
list = LinkedList()
list.add_last("a")
list.add_last("b")
list.add_last("c")
list.add_last("d")
…and displaying its contents is reduced to a single function call, print(list)
, which produces this output:
a
b
c
d