18-Linked List1
18-Linked List1
Slide 2
Announcements
Assignment 5 is due Friday
Slide 3
Today's Topics
Structs and pointers
Can you architect a queue?
Let's do it with a Vector
Let's try it with links
Introduction to the Linked List data structure
The Node struct
Lord of the Linked Lists
How is the Stack implemented with a linked list?
Linked List Queue implementation
Slide 4
I
cout << (*dPtr).month << endl;
toevaluate
same try
But, this notation is cumbersome, and the parentheses are necessary becasue the "dot" has a higher dptrdaytis
precedence than the *. which iswrong
So, there is a di!erent, more intuitive syntax, called the arrow syntax: ->
dPtr->day = 7; However
if we havean
away
Date d 7
int numDays = dPtr->daysInMonth();
cout << dPtr->month << endl; Array new Date
Then we can use dArrayto moth
Arrow notation, x->var is equivalent to (*x).var, and we will use it exclusively when using classes and
Woeste
structs.
referencefor
Slide 5
y
private:
Vector<int> data; // member variables
};
Let's assume that we have the back of the queue on the le", and the front of the queue on the right. If we
enqueue eight values, the vector would look like this:
back front
↓ ↓
1 2 3 4 5 6 7 8
The dequeue operation is relatively straightforward: we just remove and return the element from the end,
and the front is now at the previous index:
back front
↓ ↓
1 2 3 4 5 6 7 _
But, enqueue is more involved. Because the back is on the le! of the vector, we have to move all the
elements over one, one at at time before placing our value into the vector. If we enqueue(9) on the
vector, this is what happens:
1 2 3 4 5 6 7 7
1 2 3 4 5 6 6 7
1 2 3 4 5 5 6 7
1 2 3 4 4 5 6 7
1 2 3 3 4 5 6 7
1 2 2 3 4 5 6 7
1 1 2 3 4 5 6 7
9 1 2 3 4 5 6 7
Slide 6
Now, what if we want to enqueue(7) into the queue? Well, we can create a 7 just like we created the 8.
What if we simply put a 7 somewhere in memory, and then changed data to point to the 7, and made the
7 (somewhow) point to the 8? (The somehow, by the way, is a pointer that is associated with the 7 called
its next pointer, in a struct – we will get to that!).
Now we have enqueued the 7 into the back of the queue! Our data points to the 7, which we have
"pointed" to the 8.
Let's enqueue(6), and go through the process a bit slower.
Now we have a 6 in memory, but not yet connected to anything. We must get data to point to the 6, and
then have the 6 point to the 7. We actually first point the 6 to the 7. It turns out that if we didn't do that, It
turns out that we have to do it in this order becuase if we changed data first, we would lose access to the
7, becuase data was the only thing pointing to the 7 (we'll cover this soon!)
Remember, two pointers can point to the same value (in this case, both data and the 6 pointer).
Next, we can change data to point to the 6:
Oh, look, we've enqueued the 6!
There are two bits of extra information that we need to make this list a proper queue
We need to figure out what 8 points to. In this case, we will point 8 to nullptr, indicating that it is
the end of the list. If we check 8's pointer and find that it is nullptr, then we are at the end of the
list (or, in this case, the front of the queue). In the diagram, we just represent this with a slash
through 8's pointer region.
We need to designate the front as such, and the back as such:
It turns out that this is the wrong way to build a queue with a linked list! We'll see at the end of the lecture
a better way to do it by changing things just a bit.
Slide 7
Linked Lists
What we've just examined is the beginning of a linked list
A linked list is a chain of nodes
Each node contains two pieces of information:
Some piece of data that is stored in the sequence
A link to the next node in the list
We can traverse the list by starting at the first node and repeatedly following its link.
To add a node at the end, we chain the last element to the end, by first finding the end (traversing the
list), and then adding it:
Slide 8
Need to
keeptrack of loo'snextpointer
Slide 10
because once we've arrived at 3 we cannot
back totoo's pointer
come anymore
Why linked lists?
We can e"iciently splice new elements into the list or remove existing elements anywhere in the list
We never have to do a massive copy step
Linked lists have many tradeo"s, and are not o!en the best data structure!
Slide 11
struct Node {
string data;
/* ? */ next;
But, what is the type of next? It must point to another Node, so…it is a Node* type:
struct Node {
string data;
Node* next;
};
The structure is defined recursively! The compiler can handle the fact that in the definition of the Node
there is a Node*, becuase it knows it is simply a pointer. We could not recursively define the Node with an
actual Node object inside the struct, as that would be impossible to realize.
Slide 12
Always!
Always draw pictures when you are
building linked lists! This is critical to
getting the hang of it.
Slide 13
Slide 14
Slide 15
struct Tower {
string name; /* The name of this tower */
Tower* next; /* Pointer to the next tower */
};
head
late III
head = createTower("Mountain View", head);
head = createTower("Palo Alto", head);
DI
head = createTower("Menlo Park", head); changes
head = createTower("Redwood City", head);
to
TEJ
head = createTower("Millbrae", head);
head = createTower("Bayshore", head);
head = createTower("San Francisco", head);
signal(head);
By the way: the head pointer is not a Tower! it is only a pointer to a Tower, and the first Tower is San
Francisco.
Slide 16
struct Node {
int data;
Node *next;
};
Slide 17
Stack
Let's assume we have the following stack already, with 8 at the top, and 9 below 8. We then want to
push(7) onto the stack:
head = temp;
In other words: a linked list's elements must be pointed to, because we need to keep track of them. In our
attempt above, we've lost access to the 8, becuase the only thing pointing at the 8 was head. If we
reassign head to point to another object (the 7 in this case), we've broken the chain and lost 8. This is a
common bug!
Let's try again:
You might be thinking, wait – why didn't the arrow go from the 7 to the head? – this is a common
misconception of what is happening!
Remember, head is not a Node. head is a pointer to a Node. Notice that head does not have a next –
it's not an object, just a pointer.
The statement temp->next = head; says, "give 7's next pointer the same data as head", which is
what we want to do.
Now, we are able to reassign head to point to the 7, and we will have a correct linked list with 7 pushed
Our temp pointer actually disappears when the push operation is complete (it goes out of scope), so we
are le! with the following:
Slide 18
Stack pop()
To pop a data from our stack, we start like this:
head = head->next;
This is a bug! Our linked list is fine, but we have a memory leak! We have le! the 7 in memory and not
returned it to the operating system with delete.
Instead, we need to do this:
head = head->next;
Because we still have access to the 7, we can return the memory to the operating system:
delete temp;
return toReturn;
Slide 19
Stack Code
Header, intStack.h:
#pragma once
class IntStack {
public:
IntStack(); // constructor
~IntStack();
bool isEmpty();
private:
struct Node {
int data;
Node* next;
};
Node* head;
};
IntStack::IntStack()
{
head = nullptr;
}
IntStack::~IntStack()
{
while (head != nullptr) {
Node *temp = head;
head = head->next;
delete temp;
}
}
node->data = value;
node->next = head;
head = node;
}
int IntStack::pop()
{
if (isEmpty()) {
throw "Error! Trying to pop from empty stack!";
}
delete temp;
return toReturn;
}
Slide 20
#pragma once
class IntQueue {
public:
IntQueue(); // constructor
~IntQueue();
bool isEmpty();
private:
struct Node {
int data;
Node* next;
};
Node* _front;
Node* _back;
};
intQueue.cpp:
#include "intQueue.h"
IntQueue::IntQueue()
{
_front = nullptr;
_back = nullptr;
}
IntQueue::~IntQueue()
{
while (_front != nullptr) {
Node *temp = _front;
_front = _front->next;
delete temp;
}
}
int IntQueue::dequeue()
{
if (isEmpty()) {
throw "Error! Trying to dequeue from empty queue!";
}
if (_front == nullptr) {
_back = nullptr; // empty queue
}
delete temp;
return toReturn;
}
bool IntQueue::isEmpty()
{
return _front == nullptr;
}
int IntQueue::front()
{
return _front->data;
}