Lecture18 LinkedLists
Lecture18 LinkedLists
C++ basics
Implementation
User/client
vectors + grids arrays
dynamic memory
stacks + queues
management
sets + maps linked data structures
real-world
Midterm algorithms
Implementation
User/client
arrays
dynamic memory
management
4. How do we manipulate
linked lists?
Review
[memory and pointers]
Abstract Data
Structures
Levels of abstraction
Data Organization
Strategies
Fundamental C++
Data Storage
Computer
Hardware
How is computer memory organized?
0xfca0b000
Pointers and Memory
● Every variable you create has an address in memory on your computer (either
on the stack or the heap).
How is computer memory organized?
Stack Heap
Static memory allocation Dynamic memory allocation
● When you dynamically allocate variables on the heap, you must use the
keyword new (or new[] for arrays) and must store the address in a pointer to
keep track of it.
○ E.g. int* number = new int;
○ E.g. int* numArr = new int[5];
Pointers and Memory
● Every variable you create has an address in memory on your computer (either
on the stack or the heap)
● When you dynamically allocate variables on the heap, you must use the
keyword new (or new[] for arrays) and must store the address in a pointer to
keep track of it.
○ E.g. int* number = new int; Dynamically allocated variables
○ E.g. int* numArr = new int[5]; are the only reason we’ll use
pointers in this class!
Pointers and Memory
● Every variable you create has an address in memory on your computer (either
on the stack or the heap)
● When you dynamically allocate variables on the heap, you must use the
keyword new (or new[] for arrays) and must store the address in a pointer to
keep track of it.
● To get the value located at the memory address stored in a pointer, you must
dereference the pointer using the * operator (e.g. cout << *number << endl;).
Pointers and Memory
● Every variable you create has an address in memory on your computer (either
on the stack or the heap)
● When you dynamically allocate variables on the heap, you must use the
keyword new (or new[] for arrays) and must store the address in a pointer to
keep track of it.
● To get the value located at the memory address stored in a pointer, you must
dereference the pointer using the * operator (e.g. cout << *number << endl;).
Today: Using pointers
in practice
Today: Using pointers
in practice
How can we use pointers to organize non-contiguous
memory on the heap?
Today: Using pointers
in practice
How can we use pointers to organize non-contiguous
memory on the heap?
Not arrays!
Abstract Data
What is the interface for the user?
Structures
Levels of abstraction
Data Organization
How is our data organized?
Strategies
Data Organization
How is our data organized?
Strategies
Data Organization
How is our data organized?
Strategies
Data Organization
How is our data organized?
Strategies
// client code
numItems 4
The remove() operation
106 42 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4
The remove() operation
106 42 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4
The remove() operation
106 -3 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4
The remove() operation
106 -3 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4
The remove() operation
106 -3 27 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4
The remove() operation
106 -3 27 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 3
The insert() operation
106 -3 27 ? ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 3
The insert() operation
106 -3 27 ? ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 3 vec.insert(0, 198);
The insert() operation
106 -3 27 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 3 vec.insert(0, 198);
The insert() operation
106 -3 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 3 vec.insert(0, 198);
The insert() operation
106 106 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 3 vec.insert(0, 198);
The insert() operation
106 106 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4 vec.insert(0, 198);
The insert() operation
198 106 -3 27 ? ? ? ?
0 1 2 3 4 5 6 7
// client code
vec.remove(1);
numItems 4 vec.insert(0, 198);
Can we do better?
● A way to store elements as a sequence even if they’re not physically next to
each other on the computer memory
○ So we can easily insert new elements into the list
○ So we can easily remove elements from the list
○ So we can easily resize the list
○ (So we don’t have to mass copy elements and shift them over or shift them into a new block of
memory)
Can we do better?
● Nope. Class for the rest of the quarter is cancelled; computing as we know it
has been a standstill since 1954.
( just kidding)
What is a linked list?
What is a linked list?
● A linked list is a chain of nodes.
What is a linked list?
● A linked list is a chain of nodes.
● We can traverse the list by starting at the first node and repeatedly following its
link.
Node
Data
Link
Pointer to a node
Data
0xfca0b000
Link
ptr
Pointer to a node that points to a node
Data Data
0xfca0b000
Link Link
ptr
Pointer to a node that points to a node that points to a node
0xfca0b000
Link Link Link
ptr
Pointer to a node that points to a node that points to a node
0xfca0b000
Link Link Link
ptr
Why use linked lists?
● More flexible than arrays!
○ Since they’re not contiguous, they’re easier to rearrange.
● We can efficiently splice new elements into the list or remove existing
elements anywhere in the list. (We’ll see how shortly!)
● But linked lists still have many tradeoffs and are not always the best data
structure!
Linked lists in C++
The Node struct
struct Node {
string data;
Node* next;
}
The Node struct
struct Node {
string data;
Node* next;
}
● The structure is defined recursively! (both the Node and the linked list itself)
The Node struct
struct Node {
string data;
Node* next;
}
● The structure is defined recursively! (both the Node and the linked list itself)
● The compiler can handle the fact that in the definition of the Node there is a
Node* because it knows it is simply a pointer.
○ (It would be impossible to recursively define the Node with an actual Node object inside the
struct.)
Pointer to a node
string data
Node*
0xfca0b000
Node* next
list
0xfca0b000
Node* next
list
"someData"
Node*
0xfca0b000
Node* next
list
"someData"
Node*
0xfca0b000
Node* next
list
"someData"
Node*
0xfca0b000
Node* next
list
"someData" PTR
Node*
0xfca0b000
list
"someData" PTR
Node*
0xfca0b000
list
"someData" PTR
Node*
0xfca0b000
list
"someData" PTR
Node*
0xfca0b000
list
● Rewiring
○ How do we rearrange the elements in a linked list?
● Insertion
○ How do we add an element to a linked list?
● Deletion
○ How do we remove an element from a linked list?
Implementing a Stack
Note: You could do this with an array! This is just for the
sake of getting practice with linked lists.
Stack as a linked list
● We’ll keep a pointer Node* top that points to the “top” element in our stack.
○ This member var will get initialized to nullptr when our stack is empty!
Stack as a linked list
● We’ll keep a pointer Node* top that points to the “top” element in our stack.
○ This member var will get initialized to nullptr when our stack is empty!
● Our linked list nodes will be connected from the top to the bottom of our stack.
Stack as a linked list
● We’ll keep a pointer Node* top that points to the “top” element in our stack.
○ This member var will get initialized to nullptr when our stack is empty!
● Our linked list nodes will be connected from the top to the bottom of our stack.
● Our stack will specifically hold integers, so our Node struct will hold an int
type for our data field:
struct Node {
int data;
Node* next;
}
Three Stack operations
● push()
● pop()
● Destructor
Three Stack operations
● push()
● pop()
● Destructor
Common linked lists operations
● Traversal
○ How do we walk through all elements in the linked list?
● Rewiring
○ How do we rearrange the elements in a linked list?
● Deletion
○ How do we remove an element from a linked list?
push()
● Suppose we have the following Stack we want to push to:
0xfca0b000
top
push()
● Suppose we have the following Stack we want to push to:
Goal:
7 8 9 PTR
Node*
0xfca0b000
top
Let’s code push()!
Live Activity Summary
● We strongly recommend watching the live recording of the coding activity, as
the code and explanations contextualize the following diagrams
Initial State (beginning of push() function)
Node *temp = new Node;
temp->data = 7;
Node *temp = new Node;
temp->data = 7;
top = temp; // INCORRECT
Node *temp = new Node;
temp->data = 7;
temp->next = top;
Node *temp = new Node;
temp->data = 7;
temp->next = top;
top = temp;
Three Stack operations
● push()
● pop()
● Destructor
Common linked lists operations
● Traversal
○ How do we walk through all elements in the linked list?
● Rewiring
○ How do we rearrange the elements in a linked list?
● Insertion
○ How do we add an element to a linked list?
● Deletion
○ How do we remove an element from a linked list?
pop()
● Now we want to remove the top value:
...
myStack.pop(); // we want the result to be {9, 8}
7 8 9 PTR
Node*
0xfca0b000
top
pop()
● Now we want to remove the top value:
...
myStack.pop(); // we want the result to be {9, 8}
Goal:
8 9 PTR
Node*
0xfca0b000
top
Let’s code pop()!
Initial State (beginning of pop() function)
top = top->next; // INCORRECT
Node* temp = top;
Need photo, i missed it
● pop()
● Destructor
Common linked lists operations
● Traversal
○ How do we walk through all elements in the linked list?
● Rewiring
○ How do we rearrange the elements in a linked list?
● Insertion
○ How do we add an element to a linked list?
● Deletion
○ How do we remove an element from a linked list?
Destructor
● We have to make sure we delete all of the Nodes.
PTR
Node*
0xfca0b000
top
Let’s code the
destructor!
IntStack takeaways
● Linked lists are chains of Node structs, which are connected by pointers.
○ Since the memory is not contiguous, they allow for fast rewiring between nodes (without
moving all the other Nodes like an array might).
● Common bugs
○ Be careful about the order in which you delete and rewire pointers!
○ It’s easy to end up with dangling pointers or memory leaks (memory that hasn’t been
deallocated but that you not longer have a pointer to)
How do we manipulate linked
lists?
Linked list utility functions
● We’ve now seen linked lists in the context of classes, where we used a linked
list as the data storage underlying an implementation of a Stack.
Linked list utility functions
● We’ve now seen linked lists in the context of classes, where we used a linked
list as the data storage underlying an implementation of a Stack.
● However, linked lists are not limited only to use within classes. In fact, the next
assignment will ask you to implement "standalone" linked list functions that
operate on provided linked lists, outside the context of a class.
Linked list utility functions
● We’ve now seen linked lists in the context of classes, where we used a linked
list as the data storage underlying an implementation of a Stack.
● However, linked lists are not limited only to use within classes. In fact, the next
assignment will ask you to implement "standalone" linked list functions that
operate on provided linked lists, outside the context of a class.
● This is the paradigm that we will work under for the several functions. In doing
so, we'll gain a little more flexibility to get practice with many different linked
list operations and build our linked list toolbox!
Common linked lists operations
● Traversal
○ How do we walk through all elements in the linked list?
● Rewiring
○ How do we rearrange the elements in a linked list?
● Insertion
○ How do we add an element to a linked list?
● Deletion
○ How do we remove an element from a linked list?
Linked List Traversal
Traversal utility functions
● Freeing a linked list
Node*
0xab40
list
Node*
0xab40
list
Node*
0xab40
list
Node*
delete
0xab40
list
Node*
0xab40
list
Node*
Node*
0xab40
list
Node*
0xab40
list
Node*
0xab40
Behavior!
list
delete list;
list = list->next;
}
Node*
} 0xab40
list
Node*
} 0xab40 0xab42
list next
Node*
} 0xab40 0xab42
list next
Node*
} 0xab40 0xab42
list next
Node*
} 0xab40
list
Node*
} 0xab40
list
Node*
} 0xab40
list
Node*
Node*
} 0xab40 0xbc70
list next
Node*
Node*
} 0xab40 0xbc70
list next
Node*
Node*
} 0xab40 0xbc70
list next
Node*
Node*
} 0xab40 0xbc70
list next
Node*
Node*
} 0xbc70 0xbc70
list next
Node*
Node*
} 0xbc70 0xbc70
list next
Node*
} 0xbc70
list
Node*
} 0xbc70
list
Node*
Node*
} 0xbc70
list
Node*
Node*
} 0xbc70 0x40f0
list next
Node*
Node*
} 0xbc70 0x40f0
list next
Node*
Node*
} 0xbc70 0x40f0
list next
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0xbc70 0x40f0
list next
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 0x40f0
list next
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 0x40f0
list
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 0x40f0
list
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 nullptr
list next
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 nullptr
list next
"Trip" PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 nullptr
list next
PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} 0x40f0 nullptr
list next
PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} nullptr nullptr
list next
PTR
void freeList(Node* list) {
while (list != nullptr) {
Node* next = list->next;
delete list;
list = next;
}
Node*
Node*
} nullptr nullptr
list
PTR
All memory
freed! Wooo!
Traversal utility functions
● Freeing a linked list
● There are two main ways to do so: using the debugger and printing to the
console
Inspecting Linked List Contents
● Being able to "see" the contents of a linked list is a really helpful debugging
tool!
● There are two main ways to do so: using the debugger and printing to the
console
● There are two main ways to do so: using the debugger and printing to the
console
● There are two main ways to do so: using the debugger and printing to the
console
0xab40
list
0xab40
list
} 0xab40 Node*
list 0xab40
list
} 0xab40 Node*
list 0xab40
list
} 0xab40 Node*
list 0xab40
list
} 0xab40 Node*
list 0xab40
list
} 0xab40 Node*
list 0xab40
list
} 0xab40 Node*
list 0xbc70
list
} 0xab40 Node*
list 0xbc70
list
} 0xab40 Node*
list 0xbc70
list
} 0xab40 Node*
list 0xbc70
list
} 0xab40 Node*
list 0xbc70
list
} 0xab40 Node*
list 0x40f0
list
} 0xab40 Node*
list 0x40f0
list
} 0xab40 Node*
list 0x40f0
list
} 0xab40 Node*
list 0x40f0
list
} 0xab40 Node*
list 0x40f0
list
} 0xab40 Node*
list nullptr
list
} 0xab40 Node*
list nullptr
list
0xab40
list
0xab40
list
● Rewiring
○ How do we rearrange the elements in a linked list?
● Insertion
○ How do we add an element to a linked list?
● Deletion
○ How do we remove an element from a linked list?
Linked list traversal takeaways
● Temporary pointers into lists are very helpful!
○ When processing linked lists iteratively, it’s common to introduce pointers that point to cells in
multiple spots in the list.
○ This is particularly useful if we’re destroying or rewiring existing lists.
● Using a while loop with a condition that checks to see if the current pointer is
nullptr is the prevailing way to traverse a linked list.
A linked list
0xfca0b000
Link Link Link
ptr
What’s next?
Object-Oriented
Roadmap Programming
C++ basics
Implementation
User/client
vectors + grids arrays
dynamic memory
stacks + queues
management
sets + maps linked data structures
real-world
Diagnostic algorithms