Download as pdf or txt
Download as pdf or txt
You are on page 1of 96

Lecture 5: Linked Structures

CIS-275
Linked Structures
● After arrays, linked structures are the most commonly used data structure in
computer science.
○ Linked structures are often called linked lists.
● When we design more complex data structures, such as stacks and queues, we will
often use either an array or a linked list to internally store the structure’s data.
● Linked Lists and arrays each have pros and cons, which we will discuss at the end of
this lecture.

Chapter 4 Arrays and Linked Structures 2


Singly and Doubly Linked Structures
● A linked structure consists of objects that are linked together.
○ i.e. each object has two main attributes: one to hold some data and one to link
(or reference) to the next object in the list.
● The two most common types of linked structures are singly linked structures and
doubly linked structures:
slash indicates node links to
nothing else.

Chapter 4 Arrays and Linked Structures 3


Singly Linked Structures
● Typically, we will make a class called Node to represent an item in the chain.
● Instances of the Node class will have two attributes:
○ _data, which will contain that Node’s data.
○ _next, which will contain the memory address of the next Node in the chain.
● The first Node in the chain is referenced by an external variable called the head link
(or head for short).
● Each object in the following diagram (other than the head) is a Node. The first box
represents its _data, the second box its _next attribute:

Chapter 4 Arrays and Linked Structures 4


Doubly Linked Structures
● A Node class for a doubly linked structure is a little different.
○ It should three attributes: _data, _next, and _previous.
● We also need two external variables:
○ head references the first Node in the list.
○ tail references the last Node in the list.

Chapter 4 Arrays and Linked Structures 5


Singly and Doubly Linked Structures
● Given these two structures, any thoughts on what the pros and cons of each might be?

Chapter 4 Arrays and Linked Structures 6


Singly and Doubly Linked Structures
● Given these two structures, any thoughts on what the pros and cons of each might be?
● Singly Pros: Easier to implement, take less memory (each Node has less attributes)
● Doubly Pros: Easier to add a Node in the middle (we’ll see why later) and faster to
reach items at or near the end of the list.

Chapter 4 Arrays and Linked Structures 7


Linked Structures and Memory
● Memory is used for a linked structure in a very different way than for an Array.
○ An item can be added to or removed from the structure without any shifting of
the other data items in memory.
○ A linked structure resizes during each insertion or removal with no memory cost
and no copying of data items.

Chapter 4 Arrays and Linked Structures 8


Noncontiguous Memory and Nodes
● Recall that array items are stored in contiguous memory locations.
○ i.e. All items are directly next to each other in memory: item 1 is next to item 2,
item 2 is next to item 3, etc.
● Each Node in a linked structure, on the other hand, can be anywhere in memory.
○ i.e. item 1 might be nowhere near item 2, etc.
○ As an example:

0x1234 0x256 0x3050

● This memory representation scheme is referred to as noncontiguous memory.

Chapter 4 Arrays and Linked Structures 9


Defining a Singly Linked Node Class
● A Node class is usually fairly simple.
● The most basic type of Node requires two attributes:
○ A variable to reference the piece of data the Node “contains”.
○ A reference (or link) to the next Node in the list.

class Node():
""" Represents a singly linked Node """

def __init__ (self, data, next= None):


""" By default, this Node will not link to another Node """
self.data = data
self.next = next

● When the client creates a Node object, they can initialize it to point to another Node,
but by default it will point to nothing.

Chapter 4 Arrays and Linked Structures 10


Defining a Singly Linked Node Class
● Consider our Node class. What does the following client code do?

node1 = None

node1
Chapter 4 Arrays and Linked Structures 11
Defining a Singly Linked Node Class
● Consider our Node class. What does the following client code do?

node1 = None # Create a variable that references nothing

● node1 is a variable that currently points to nothing.


● We draw a variable pointing to None as a box with a slash through it to indicate that
the variable does not contain a memory address.

node1
Chapter 4 Arrays and Linked Structures 12
Defining a Singly Linked Node Class
● What does the second line of code do?
node1 = None
node2 = Node("A")

node1
Chapter 4 Arrays and Linked Structures 13
Defining a Singly Linked Node Class
● What does the second line of code do?
node1 = None
node2 = Node("A") # Create a variable that references a Node object.

● The reference variable node2 is set to reference a new Node object in memory.
● This object contains two attributes: the string “A” as its data and an empty link.
○ Recall that if we don’t pass a second argument, the Node’s _next attribute is
assigned None by default.

node2

node1 A

Chapter 4 Arrays and Linked Structures 14


Defining a Singly Linked Node Class
● What does the third line of code do?
node1 = None
node2 = Node( "A")
node3 = Node( "B", node2)

node2

node1 A

Chapter 4 Arrays and Linked Structures 15


Defining a Singly Linked Node Class
● What does the third line of code do?
node1 = None
node2 = Node( "A")
node3 = Node( "B", node2)

● The variable node3 references a new Node object.


● This new object contains two attributes:
○ Its _data attribute contains the string “B”
○ Its _next attribute points to the same object that node2 points to.
node3 node2

node1 B A

Chapter 4 Arrays and Linked Structures 16


Defining a Singly Linked Node Class
● We have officially created a (very simple) linked list!
node1 = None
node2 = Node( "A")
node3 = Node( "B", node2)

● Suppose we want to add another Node to the list.


● What will the following line of code do?
node1._next = node3

node3 node2

node1 B A

Chapter 4 Arrays and Linked Structures 17


Defining a Singly Linked Node Class
● We have officially created a (very simple) linked list!
node1 = None
node2 = Node( "A")
node3 = Node( "B", node2)

● Suppose we want to add another Node to the list.


● What will the following line of code do? ERROR!
node1._next = node3
Node1 references None.
It does not have a _next
node3 node2 attribute to set!

node1 B A

Chapter 4 Arrays and Linked Structures 18


Defining a Singly Linked Node Class
● This code first creates a new Node object for node1 to reference.
node1 = None
node2 = Node( "A")
● The final statement sets its _next attribute to point
node3 = Node( "B", node2) to node3.
node1 = Node( "C")
node1._next = node3 ● We now have a linked list of three Nodes.

node1 node3 node2

C B A

Chapter 4 Arrays and Linked Structures 19


Traversal of a Singly Linked List
● A traversal is the process of visiting each Node in a linked list and printing its data.
● Do we need all three of the node1, node3, and node2 reference variables to traverse
this linked list?

node1 node3 node2

C B A

Chapter 4 Arrays and Linked Structures 20


Traversal of a Singly Linked List
● A traversal is the process of visiting each Node in a linked list and printing its data.
● Do we need all three of the node1, node3, and node2 reference variables to traverse
this linked list? No!
● If we start at the Node referenced by node1, we can simply follow next pointers to
access the whole list.
● In fact, we would never really build a linked list this way. Having three external
pointers is unnecessary (and defeats the purpose!)

node1 node3 node2

C B A

Chapter 4 Arrays and Linked Structures 21


Traversal of a Singly Linked List
● A linked list typically has a single external variable named head which references the
first Node in the list.
○ The first Node’s next pointer references the second Node in the list, the second
Node’s next pointer references the third Node in the list, etc.
○ The Node at the end of the list has a next pointer that references None.
● Let’s write code to generate the following linked list with the use of a single variable.
(Hint: The first step is to assign the Node containing “A” to head)

Head

C B A
Chapter 4 Arrays and Linked Structures 22
Generating a Singly Linked List
● In the 1st step, we assign a new Node to head. Its data is “A” and its next is None.

head = Node("A")

● What is the next step?

Head

A
Chapter 4 Arrays and Linked Structures 23
Generating a Singly Linked List
● In the 2nd step, we assign a new Node to head. Its data is “B” and its next is “A”.

head = Node( "A")


head = Node("B", head)

● Note: The right side of = is evaluated first. When we pass head to the constructor, it is
still referencing “A”.
○ Therefore, the constructor points the new Node’s next to “A”.
○ After the constructor returns, the = operator points head to the new Node, “B”

head
B A
Chapter 4 Arrays and Linked Structures 24
Generating a Singly Linked List
● In the 3rd step, we assign a new Node to head. Its data is “C” and its next is “B”.

head = Node( "A")


head = Node( "B", head)
head = Node("C", head)

● We have now built a proper linked list with 3 Nodes and a single external pointer.

head

C B A
Chapter 4 Arrays and Linked Structures 25
Generating a Singly Linked List
● Let’s write a loop that creates a linked list with the values 1 through 5.

Chapter 4 Arrays and Linked Structures 26


Generating a Singly Linked List
● Let’s write a loop that creates a linked list with the values 1 through 5.

head = None
for count in range(1, 6):
head = Node(count, head)

● In the first iteration of the loop, we create a Node with 1 as its data.
○ head currently references None, so None is assigned to this Node’s next.
● In the second iteration, we create a Node with 2 as its data.
○ Its next is pointed to the Node containing 1.
○ head is then pointed to the new Node containing 2.
● etc...

Chapter 4 Arrays and Linked Structures 27


Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

● This code creates the following linked list.


● Note that the most recently added Node is at the front of the list.
● Suppose we wanted to print all items in the list. How could we do this?

head

5 4 3 2 1
Chapter 4 Arrays and Linked Structures 28
Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● In the first iteration of the while loop, the data of the first Node in the list is printed.
● head is then updated to point to the next Node in the list.
● This process continues until head references the Node containing “1”.
● After printing 1, head is updated to head.next, which is None, and the loop ends.

head

5 4 3 2 1
Chapter 4 Arrays and Linked Structures 29
Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● Consider this code. Suppose we wanted to print the list a second time.
○ How would we do this?

Chapter 4 Arrays and Linked Structures 30


Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● Consider this code. Suppose we wanted to print the list a second time.
○ How would we do this? We couldn’t!
● In the first iteration of the while loop, ‘5’ is printed.
● head is then updated to point to ‘4’.

head

5 4 3 2 1
Chapter 4 Arrays and Linked Structures 31
Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● What happens to “5” once head points to “4”?

head

5 4 3 2 1
Chapter 4 Arrays and Linked Structures 32
Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● What happens to “5” once head points to “4”?


● It gets garbage collected! No variables reference it, so it is deleted.
● “4” is now officially the first Node in the list.

head

4 3 2 1
Chapter 4 Arrays and Linked Structures 33
Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● In the final iteration of the while loop, “1” is the only remaining Node.
● The statement head = head.next sets head to reference None.

head

1
Chapter 4 Arrays and Linked Structures 34
Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

while head != None:


print(head.data)
head = head.next

● This while loop prints the list while also deleting it!
● Can we change this code to still traverse the list but not delete it (while also saving the
external pointer to the start of the list)?

head

Chapter 4 Arrays and Linked Structures 35


Traversing a Linked List
head = None
for count in range(1, 6):
head = Node(count, head)

probe = head
while probe is not None:
print(probe.data)
probe = probe.next

● Let’s use a second variable to traverse the list.


○ Before the loop, point a variable (named probe here) to the same Node as head
(i.e. the first Node in the list)
○ In each iteration, update probe to point to the next Node in the list.
○ The loop continues until probe points to None.
● The head variable is not updated. It still points to the start of the list once the loop is
finished.
Chapter 4 Arrays and Linked Structures 36
Traversing a Linked List
head = None
for count in range(1, 4):
head = Node(count, head)

probe = head
while probe is not None:
print(probe.data)
probe = probe.next

● In this example, we create a


slightly smaller list.
● This diagram demonstrates each
iteration (or pass) of the loop.
● Note that head still points to the start of the list once the traversal is over.
● We never want to update head during a traversal.
Chapter 4 Arrays and Linked Structures 37
Searching a Linked List
head = None
while True:
name = input("Enter a name or 'q' to stop: ")
if name == 'q':
break
head = Node(name, head)

● Consider this code which allows the user to create a linked list of names.
● After they have finished, what steps do we need to take to see if the user entered a
specific name (i.e. “Steve”)?

Chapter 4 Arrays and Linked Structures 38


Searching a Linked List
● The following steps search for a target item in a linked list:
○ Create a new reference variable and traverse the list.
■ If any Node in the traversal contains the target word, then target is in the list.
■ If the traversal ends, target is not in the list.
● Let’s write a function is_in_list which returns True if a given target is in the given
list and False otherwise:

def is_in_list(list, target):


???

Chapter 4 Arrays and Linked Structures 39


Searching a Linked List
def is_in_list(list, target):
""" Returns True if 'target' is in the linked list 'list' """
probe = list
while probe is not None:
if probe.data.lower() == target.lower():
# We found target.
return True
probe = probe.next

# End of loop: we have traversed whole list and 'target' was not found
return False

● If we find target during the traversal, we can immediately return True.


● If the traversal finishes, the entire list was checked and we can return False.

Chapter 4 Arrays and Linked Structures 40


Searching a Linked List
def is_in_list(list, target):
""" Returns True if 'target' is in 'list' """
● This program will print “True” if
probe = list the user added “Steve” to the list
while probe is not None:
if probe.data.lower() == target.lower(): and “False” otherwise.
# We found target.
return True
probe = probe.next
return False

def main():
head = None
while True:
name = input("Enter a name or 'q' to stop: ")
if name == 'q':
break
head = Node(name, head)

print(is_in_list(head, "Steve"))

Chapter 4 Arrays and Linked Structures 41


Functions to Update a List
● Until now, we have simply added or removed items from a linked list by directly
manipulating the head or some other Node in the list.
● It can be a lot more convenient to write functions that each perform specific updates to
a list and return the result.
● Over the next few slides, we’ll write functions to add an item to the front of the list, the
end of the list, and remove an item from the list.

Chapter 4 Arrays and Linked Structures 42


Inserting at the Beginning
● Suppose we want to add a Node to the beginning (or ‘head’) of a linked list.
● There are two possible situations. What are they?

Chapter 4 Arrays and Linked Structures 43


Inserting at the Beginning
● Suppose we want to add a Node to the beginning (or ‘head’) of a linked list.
● There are two possible situations. What are they?
○ The linked list is currently empty:

○ The linked list is not currently empty.

Chapter 4 Arrays and Linked Structures 44


Inserting at the Beginning of an Empty List
● Suppose the list is empty:

● What steps do we need to take in the following code to add a new Node containing
“Cypress College” to the beginning of this list?
head = None
???

Chapter 4 Arrays and Linked Structures 45


Inserting at the Beginning of an Empty List
● Suppose the list is empty:

● What steps do we need to take in the following code to add a new Node containing
“Cypress College” to the beginning of this list?
head = None
head = Node( "Cypress College", head)

● Simply create a new Node whose _next points to None. Then, set head to point to it.
● Since head already references None, we can pass that instead.
Chapter 4 Arrays and Linked Structures 46
Inserting at the Beginning of a non-empty List
● Suppose the list is not empty:

● What statement would add a new Node containing “D2” to the beginning of this list?

Chapter 4 Arrays and Linked Structures 47


Inserting at the Beginning of a non-empty List
● Suppose the list is not empty:
Inserting at the beginning of a non-empty list
is the same as an empty list

● What statement would add a new Node containing “D2” to the beginning of this list?

head = Node( "D2", head)

● Create a new Node and set its next to point the current front of the linked list.
● Then, set head to point to this new Node, making it the new front.
● Note that this statement is the same as adding to the beginning of an empty list!

Chapter 4 Arrays and Linked Structures 48


Inserting at the Beginning
def add_to_list(my_list, val_to_add):
""" add 'val_to_add' to the front of the list. It will either point to the previous
First node in the list, or None (in the case where the list is empty and
my_list references 'None') """

my_list = Node(val_to_add, my_list)


return my_list

● This simple function takes care of both possible scenarios in a single line of code.
○ If my_list points to the first Node in a list, the new Node we create will have its
_next set to point to it.
○ Otherwise, it will be set to point to None (which is what we want: the first Node of
a linked list with one Node should have a next which points to None).

Chapter 4 Arrays and Linked Structures 49


Inserting at the Beginning
def add_to_list(my_list, val_to_add):
""" add 'item' to the front of the list. It will either point to the previous
First node in the list, or None (in the case where the list is empty and
my_list references 'None' """

my_list = Node(val_to_add, my_list)


return my_list

● What will this client code print?

head = Node( 1, None)


head = Node( 2, head)
add_to_list(head, 5)
print_list(head)

Chapter 4 Arrays and Linked Structures 50


Inserting at the Beginning
def add_to_list(my_list, val_to_add):
""" add 'item' to the front of the list. It will either point to the previous
First node in the list, or None (in the case where the list is empty and
my_list references 'None' """

my_list = Node(val_to_add, my_list)


return my_list

● What will this client code print? 2 1

head = Node( 1, None)


head = Node( 2, head)
add_to_list(head, 5)
print_list(head)

● The 5 didn’t print! Why not?

Chapter 4 Arrays and Linked Structures 51


Inserting at the Beginning
def add_to_list(my_list, val_to_add):
""" add 'item' to the front of the list. It will either point to the previous
First node in the list, or None (in the case where the list is empty and
my_list references 'None' """

my_list = Node(val_to_add, my_list)


return my_list

● Recall that if a parameter is pointed to a different object inside a function, the argument
variable is not updated.
● Note also that add_to_list returns the updated my_list parameter.
● We need to assign this return value to head after the function ends.

Chapter 4 Arrays and Linked Structures 52


Inserting at the Beginning
def add_to_list(my_list, val_to_add):
""" add 'item' to the front of the list. It will either point to the previous
First node in the list, or None (in the case where the list is empty and
my_list references 'None' """

my_list = Node(val_to_add, my_list)


return my_list

This client code will now work and print 5 2 1

head = Node( 1, None)


head = Node( 2, head)
head = add_to_list(head, 5)
print_list(head)

Chapter 4 Arrays and Linked Structures 53


Inserting at the End
● Suppose we want to add a Node to the end (or tail) of a linked list.
● There are two possible situations. What are they?

Chapter 4 Arrays and Linked Structures 54


Inserting at the End
● Suppose we want to add a Node to the end (or tail) of a linked list.
● There are two possible situations. What are they?

○ The linked list is currently empty (the same steps apply as inserting at the front):

○ The linked list is not currently empty.

Chapter 4 Arrays and Linked Structures 55


Inserting at the End (List is not empty)
● Suppose the list is not empty:

● What steps do we need to take?

Chapter 4 Arrays and Linked Structures 56


Inserting at the End (List is not empty)
● Suppose the list is not empty:

● What steps do we need to take?


○ Create a new Node containing the data to add.
○ Traverse the linked list until we find the Node whose next is None (i.e. the Node
at the very end of the list).
○ Point the last Node’s next to the new Node.

Chapter 4 Arrays and Linked Structures 57


Inserting at the End (List is not empty)
● We again use a separate variable named
probe to traverse the list.
● In each step of the traversal, we see if
probe.next is None.
● Once probe is pointing to a Node that
whose next is None, we add the new
Node after it.
○ We do this by updating probe.next
to point to the new Node.

Chapter 4 Arrays and Linked Structures 58


Inserting at the End
def add_to_end(my_list, val_to_add):
# new_node goes at list end, so it will point to None whether my_list is empty or not.
new_node = Node(val_to_add)
if my_list is None:
my_list = new_node
else:
probe = my_list
while probe.next is not None:
probe = probe.next

# Loop ends when probe points to last Node in list. Update its next to point to the new Node
probe.next = new_node
return my_list

● In this function, we first check to see if the linked list is empty.


○ If it is, we simply set my_list to point to the newly created Node.
● Otherwise, we loop until probe.next is None (rather than until probe is None)
○ After the loop stops, probe still refers to the final Node in the list.
○ This way, we can easily point the last Node’s next to new_node.
Chapter 4 Arrays and Linked Structures 59
Removing at the Beginning
● If the client tries to remove an item from the front of an empty linked list, we simply
do nothing.
● What steps do we need to take to remove the Node at the front of a non-empty linked
list, such as this one?

Chapter 4 Arrays and Linked Structures 60


Removing at the Beginning
● If the client tries to remove an item from the front of an empty linked list, we simply
do nothing.
● What steps do we need to take to remove the Node at the front of a non-empty linked
list, such as this one?

Just one step!


1. Point head to head.next (i.e. the second Node in the list)
head = head._next

The original first Node will be garbage collected.


Chapter 4 Arrays and Linked Structures 61
Removing at the Beginning
def remove_at_beginning(my_list):
""" Removes the first Node from my_list (if it is not empty) """
if my_list is not None:
my_list = my_list.next
return my_list

● This function updates its parameter to point to the Node after the one it is currently
pointing to (assuming the parameter doesn’t initially point to None).
● What will the following client code print?

head = Node( 1, None)


head = Node( 2, head)
head = add_to_list(head, 5)
head = remove_at_beginning(head)
print_list(head)

Chapter 4 Arrays and Linked Structures 62


Removing at the Beginning
def remove_at_beginning(my_list):
""" Removes the first Node from my_list (if it is not empty) """
if my_list is not None:
my_list = my_list.next
return my_list

● This function updates its parameter to point to the Node after the one it is currently
pointing to (assuming the parameter doesn’t initially point to None).
● What will the following client code print? 2 1

head = Node( 1, None)


head = Node( 2, head)
head = add_to_list(head, 5)
head = remove_at_beginning(head)
print_list(head)

Chapter 4 Arrays and Linked Structures 63


Removing at the End
● Removing at the end has three cases to consider:
○ The first is that the list is empty, so we do nothing
○ What are the other two?

Chapter 4 Arrays and Linked Structures 64


Removing at the End
● Removing at the end has three cases to consider:
○ The first is that the list is empty, so we do nothing
○ The list only has a single Node in it.
○ The list has more than one Node in it.

Chapter 4 Arrays and Linked Structures 65


Removing at the End
● The first two cases can be combined into one:

def remove_at_end(my_list):
""" Removes the final Node from my_list (if it is not empty) """
if my_list is None or my_list.next is None:
# list is empty or only has one node. Return 'None' to indicate the list is now empty
return None

● If the list only has a single Node in it, we can return None to indicate that the list is
empty after the removal.
● Note: We could raise an Error if the client tries to remove from an empty list, but for
now we can skip that.

Chapter 4 Arrays and Linked Structures 66


Removing at the End
● The first two cases can be combined into one:

def remove_at_end(my_list):
""" Removes the final Node from my_list (if it is not empty) """
if my_list is None or my_list.next is None:
# list is empty or only has one node. Return 'None' to indicate the list is now empty
return None
else:
# list has two or more nodes. Find the node before the last one.
probe = my_list
while probe.next.next != None:
probe = probe.next
probe.next = None
return my_list

● If the list has two or more Nodes, we probe to find the second-to-last Node. We then
set its _next to None, making it the new last Node.

Chapter 4 Arrays and Linked Structures 67


Removing at the End
● Consider this list with three Nodes.
● In the first step, probe.next.next doesn’t reference
None...it references “D3”.
● In step two, probe is updated to reference “D2”.
probe.next.next now references None!
○ We break out of the while loop at this point.
○ We then set probe.next to None.
○ This removes “D3” from the list, which is
garbage collected.

Chapter 4 Arrays and Linked Structures 68


Inserting at Any Position
● So far we have inserted Nodes at the beginning or end of the list.
● Inserting a Node in the middle of the list is a bit more complicated.
○ In a future lecture, we will build a linked list of sorted items.
○ Adding a new item in this case requires probing the list until we find an item
larger than the one being entered.
● For now, we will simply write code to insert a Node at a given “index”.

Chapter 4 Arrays and Linked Structures 69


Inserting at Any Position
● Suppose we have a linked list of three Nodes.
● To add a new Node to “index” 2, what steps
do we need to take?

Chapter 4 Arrays and Linked Structures 70


Inserting at Any Position
● Suppose we have a linked list of three Nodes.
● To add a new Node to “index” 2, what steps
do we need to take?

1. Traverse the list until we reach the Node at


index 1 (“D2” in this example).
2. Create a new Node and point its _next to the
Node currently at index 2.
3. Point the _next of the Node at index 1 to the
newly created Node.

Chapter 4 Arrays and Linked Structures 71


Inserting at Any Position
def add_to_index(my_list, val_to_add, index):
if index == 0:
● The very first thing we do is
return add_to_list(my_list, val_to_add) see is see if the client wants to
else:
insert at index 0.
● If they do, we can simply call
add_to_list which already
adds an item to the front of the
list.
● What do we need to do in the
else clause?

Chapter 4 Arrays and Linked Structures 72


Inserting at Any Position
def add_to_index(my_list, val_to_add, index):
if index == 0:
● In the else clause, we traverse
return add_to_list(my_list, val_to_add) the linked list.
else:
probe = my_list ● A variable cur_index tracks
cur_index = 0
while cur_index < index - 1:
the index of the Node that
probe = probe.next probe is pointing to.
cur_index += 1
● We stop the loop once probe
points to the Node before the
desired index.
● What do we do next?

Chapter 4 Arrays and Linked Structures 73


Inserting at Any Position
def add_to_index(my_list, val_to_add, index):
if index == 0:
● In the else clause, we traverse
return add_to_list(my_list, val_to_add) the linked list.
else:
probe = my_list ● A variable cur_index tracks
cur_index = 0
while cur_index < index - 1:
the index of the Node that
probe = probe.next probe is pointing to.
cur_index += 1
# Insert new node after node at position index - 1 ● We stop the loop once probe
probe.next = Node(val_to_add, probe.next)
points to the Node before the
return my_list
desired index.
● What do we do next?

● Add the new Node after the one probe points to.
● To do this, we point the new Node’s _next to the Node currently pointed to by probe,
then we point probe’s _next to the new Node.
Chapter 4 Arrays and Linked Structures 74
Inserting at Any Position
def add_to_index(my_list, val_to_add, index):
if index == 0:
● Is there anything that can go
return add_to_list(my_list, val_to_add) wrong with this function?
else:
probe = my_list
cur_index = 0
while cur_index < index - 1:
probe = probe.next
cur_index += 1
# Insert new node after node at position index - 1
probe.next = Node(val_to_add, probe.next)
return my_list

Chapter 4 Arrays and Linked Structures 75


Inserting at Any Position
def add_to_index(my_list, val_to_add, index):
if index == 0:
● Is there anything that can go
return add_to_list(my_list, val_to_add) wrong with this function?
else:
probe = my_list ● Suppose the client tries to add
cur_index = 0
while cur_index < index - 1:
to an index that doesn’t exist in
probe = probe.next the list:
cur_index += 1
# Insert new node after node at position index - 1
head = add_to_index(head, 5, 10)
probe.next = Node(val_to_add, probe.next)
return my_list

Chapter 4 Arrays and Linked Structures 76


Inserting at Any Position
def add_to_index(my_list, val_to_add, index):
if index == 0:
return add_to_list(my_list, val_to_add)
else:
probe = my_list
cur_index = 0
while cur_index < index - 1:
if probe.next is None:
raise IndexError ("List is too small")
probe = probe.next
cur_index += 1
# Insert new node after node at position index - 1
probe.next = Node(val_to_add, probe.next)
return my_list

● During traversal, if probe.next is ever None, we’ve reached the end of the list
without finding the target index.
● At this point, we can raise an Error.
Chapter 4 Arrays and Linked Structures 77
Example: Inserting at Index 2
The next few slides will demonstrate how the while
loop we just wrote works.
● Suppose we have a linked list with 3 items and
cur_index 0 the client wants to add “D4” to index 2.
index 2 ● In step 1, we begin traversing the list.
● cur_index is not index - 1, so we are not
at the Node before index 2 yet.

Chapter 4 Arrays and Linked Structures 78


Example: Inserting at Index 2
● Suppose we have a linked list with 3 items and
the client wants to add “D4” to index 2.
● In step 2, probe is set to point to “D2”.
● cur_index is index - 1, so we are at the
Node before index 2.
● This means want to place a new Node after
“D2” and before “D3”.
cur_index 1
index 2

Chapter 4 Arrays and Linked Structures 79


Example: Inserting at Index 2
● Suppose we have a linked list with 3 items and
the client wants to add “D4” to index 2.
● In step 3, we create a new Node object and set
its _next to probe’s _next.
● We then point probe’s _next to the new
Node containing “D4”.

Chapter 4 Arrays and Linked Structures 80


Removing at Any Position
● Suppose we now want to be able to remove a Node at a given position.
● There are three possible scenarios. What are they?

Chapter 4 Arrays and Linked Structures 81


Removing at Any Position
● Suppose we now want to be able to remove a Node at a given position.
● There are three possible scenarios. What are they?

1. The list is empty.


2. The client wants to remove the first item from the list.
3. The client wants to remove a Node from the middle or end of the list.

Scenarios 1 and 2 are fairly simple. What do we need to do to perform scenario 3?

Chapter 4 Arrays and Linked Structures 82


Removing at Any Position
● Let’s say the client wants to remove the Node at “index 2” from this list.
● We traverse the list until we reach the Node at “index 1”.
○ We then point the Node at “index 1” to the Node at “index 3”.

Chapter 4 Arrays and Linked Structures 83


Removing at Any Position
def remove_at_index(my_list, index):
""" Removes the Node at the given index """
● This function is very similar to
if index == 0: insert_at_index.
# Simply call remove_at_front
return remove_at_front(my_list) ● The main difference is that after
else:
probe = my_list
the traversal, we point probe’s
cur_index = 0 _next to the Node after
while cur_index < index - 1:
if probe.next is None:
raise IndexError ("List too small")
probe = probe.next
cur_index += 1
# Remove the node after node at index - 1
probe.next = probe.next.next
return my_list

Chapter 4 Arrays and Linked Structures 84


Example: Removing at Index 2
The next few slides will demonstrate how
the while loop we just wrote works.
● Suppose we have a linked list with 4
cur_index 0 items and the client wants to remove
index 2 from “index 2”
● In step 1, we begin traversing the list.
● cur_index is not index - 1, so we
are not at the Node before index 2 yet.

Chapter 4 Arrays and Linked Structures 85


Example: Removing at Index 2
● Suppose we have a linked list with 4
items and the client wants to remove
from “index 2”
● In step 2, probe is set to point to “D2”.
● cur_index is index - 1, so we are
at the Node before index 2.
● This means want to remove the next
cur_index 1 Node.
index 2

Chapter 4 Arrays and Linked Structures 86


Example: Removing at Index 2
● Suppose we have a linked list with 4
items and the client wants to remove
from “index 2”
● In step 3, probe’s next is pointed to
probe.next.next.
● Will “D3” be garbage collected?

Chapter 4 Arrays and Linked Structures 87


Example: Removing at Index 2
● Suppose we have a linked list with 4
items and the client wants to remove
from “index 2”
● In step 3, probe’s next is pointed to
probe.next.next.
● Will “D3” be garbage collected? Yes!
● Although “D3” is pointing to
something, nothing is pointing to “D4”.

Chapter 4 Arrays and Linked Structures 88


Complexity Trade-Off: Time and Space
● What is the running time for accessing a value at the ith index in:
○ An Array?

○ A Linked List?

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 89


Complexity Trade-Off: Time and Space
● What is the running time for accessing a value at the ith index in:
○ An Array? O(1), every-time case. The memory address of the ith item is calculated
by: address of the start of array + index.

○ A Linked List? O(n), average case. We have to start at head and traverse the list
until we reach the desired index.
■ It is average case, because if the index is 1, it runs in O(1) time.

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 90


Complexity Trade-Off: Time and Space
● What is the running time for replacement at the ith position?
○ An Array?

○ A Linked List?

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 91


Complexity Trade-Off: Time and Space
● What is the running time for replacement at the ith position?
○ An Array? O(1), every-time case. Same reasoning as access.

○ A Linked List? O(n), average case. Same reasoning as access.

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 92


Complexity Trade-Off: Time and Space
● What is the running time for insertion at beginning?
○ An Array?

○ A Linked List?

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 93


Complexity Trade-Off: Time and Space
● What is the running time for insertion at beginning?
○ An Array? O(n), every-time case. We must move every other item one index to the
right!

○ A Linked List? O(1), every-time case. All we need to do is set head to the new
Node.

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 94


Complexity Trade-Off: Time and Space
● What is the running time for insertion at end (logical end for array)?
○ An Array?

○ A Linked List?

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 95


Complexity Trade-Off: Time and Space
● What is the running time for insertion at end (logical end for array)?
○ An Array? O(1) if the array doesn’t have to be resized, O(n) if it does (because
increasing array capacity is O(n) time).

○ A Linked List? O(n), every-time case. We need to traverse to the end of the list
over n Nodes.

my_array D1 D2 D3 D4

Chapter 4 Arrays and Linked Structures 96

You might also like