Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 46

Intro to Data Structure

Singly and Doubly


Linked List
Reading: Chapter 3
Data Structures and
Algorithms in C++
Second Edition
Why Data Structures?
• We have a list of N (= 10 million) students (with names and non-
contiguous IDs) stored in a file
• We want to support (infinitely many):
– Loads from disk (talk about memory)

– Insertions of new students

– Updates of student names

– Searches by name and/or ID

– Deletions of students

– Writes to disk

• How would you go about it?


• Arrays? 16-2
Why Data Structures?
• When would we sort the data?
• What would be the initial size of the array(s)?
• By how much do we increase the size of an array?
• Do we decrease the size?
• How do we deal with empty slots?
• Arrays are nice and simple for storing things in a certain order, but
they have drawbacks:
– They are not very adaptable
– For instance, we have to fix the size n of an array in advance, which makes
resizing an array difficult
– Insertions and deletions are difficult because elements need to be shifted
around

16-3
Singly Linked List
• We now explore the implementation of an alternate sequence,
known as the singly linked list
• A linked list, in its simplest form, is a collection of nodes that
together form a linear ordering

Copyright © 2017 Pearson Education, Ltd. All rights reserved. 16-4


Pointers to same type
• Objects can point to different type objects as fields
• Also, objects can point to same type objects as fields

• What would happen if we had a class that declared one of its own
type as a field?

class Strange {
public:
name Other
string name;
“Adam”
Strange *other;
};
• Strange object has two fields:
− One can store a single string name
− One is a pointer to another Strange object.
• We can use it to store a list of string values
A list node class
• List Node: a single element/object of a list structure.
• Each node contains its own data.
• Each list node object stores data and pointer(s):
– data can be of any primitive types or object of any class, etc.
– pointer (usually called next) is an object address that points to the next node
in the list.

Pseudocode
Class ListNode Data portion of Node
dataType data1;
dataType data2;

ListNode *next; link to another Node object

dat nex dat nex dat nex data next


a t a t a t 9 null
42 -3 17
What is a linked list ele
m
next

• A simple IntNode class : 18


class IntNode {
– member elem stores the element stored in this public:
node, which in this case is a integer int elem;
IntNode* next;
– member next stores a pointer to the next
IntNode();//optional
node };
• Linked lists gather objects of the same type in sequence, accessible
using pointers.
– These objects are usually called Nodes
• first, head, front, or list is used to keep track of the linked list.
– It is not a Node but a pointer to a node
– If you keep a reference to the front of the list, then you can get to anything in
it.
head ele nex ele nex ele nex elem next
m t m t m t 9 null
42 -3 17
List node client example
• Let us construct a list that stores the sequence of values [42, -3, 17].
• There are three values then we need three nodes that are linked together.
IntNode *list;
• The variable list is not itself a node but a variable capable of referring to a
node.
list ?

• ? Will be replaced with a reference to a node which means this box does not have
data or next field.
• We do not have the actual node yet. To have it, we should call new:
elem next
list = new IntNode(); list 0

• .
IntNode default construct initializes the fields as:
– Data initialized to 0
– Next initialized to null ( in these slides / indicates null)
#include <iostream>
using namespace std;
class IntNode {
public: List node
int elem;
IntNode* next; tester
IntNode();
}; example
int main() {
IntNode *list;
list = new IntNode();
list->elem = 42;
list->next = new IntNode();
list->next->elem = -3;
list->next->next = new IntNode();
list->next->next->elem = 17;
cout << list->elem;
cout <<" " << list->next->elem;
cout <<" " << list->next->next->elem;
}
IntNode::IntNode():elem (0), next(NULL){}

elem next elem next elem next


list 0 0-3 0
17 null
.

42

Output 42 -3 17
List node with constructor, private data, friends
#include <iostream>
using namespace std;

class IntNode {
private:
int elem;
IntNode* next;
public:
IntNode();
IntNode(int elem);
friend void manipulateList(IntNode *p);
};

void manipulateList(IntNode *p);

int main() {
IntNode *list;
manipulateList(list);
}

IntNode::IntNode():elem (0), next(NULL){}


IntNode::IntNode(int elem):elem (elem), next(NULL){}

void manipulateList(IntNode *list){


list = new IntNode(43);
list->next = new IntNode(-3);
list->next->next = new IntNode(17);
list->next->next->next = NULL;
cout << list->elem <<" " << list->next->elem <<" "
<< list->next->next->elem;
}
List node w/ constructor
void manipulateList(IntNode *list){
list = new IntNode(42);
list->next = new IntNode(-3);
list->next->next = new IntNode(17);
list->next->next->next = NULL;
cout << list->elem <<" " << list->next->elem <<" "
<< list->next->next->elem;
}

elem next elem next elem next


list 0 0 017
.

42 -3 null

Output 42 -3 17
Pointers vs. objects
variable = value; //assume left side is a pointer variable, right can be an address
null
a variable (left side of = ) is an arrow (the base of an arrow)
a value (right side of = ) is an object (a box; what an arrow points at)
• For the list at right: ele nex
– q = a->next; p m t
2
means to make q point at 20
– a->next = p; 1 ele nex 2 ele nex
a m m
means to adjust where points t1 t
Reassigning references 10 20
• a->next = b->next; q
– "Make the variable a->next refer to the
same value as b->next." elem next elem next
a
– Or, "Make a->next point to the same 10 20
place that b->next points."
elem next elem next
b
30 40
Traversing a list?
• Suppose we have a long chain of list nodes:

ele next ele next ele next


head m m ... m
10 20 990
• How would we print the data values in all the nodes?
– Start at the head of the list.
– While (there are more nodes to print):
• Print the current node's element.
• Go to the next node.
• How do we walk through the nodes of the list?
while (head != null) {
cout << head->elem;
head = head->next; // move to next node
}
• What's wrong with this approach?
– it loses the linked list as it prints it!)
Traversing a list with a current reference
• Don't change list. Make another variable, and change that.
– A IntNode variable is NOT a IntNode object
IntNode* current = head;
• What happens to the picture below when we write:
current = current->next;

ele nex ele nex ele nex


head m m m
t t ... t
current 10 20 990

• The correct way to print every value in the list:


IntNode* current = head;
while (current != null) {
cout << current->elem;
current = current->next; // move to next node
}
– Changing current does not damage the list.
A IntLinkedList class
• Let's write a class named IntLinkedList with the following interface
bool empty() const; // is list empty?
void addFront(int value); // add to front of list
int front() const; // get front element
void removeFront(); // remove front item
list
void showList(); //print the elements
//we can add more methods when needed
– The list is internally implemented as a chain of linked nodes
• The IntLinkedList keeps a reference to its head as a field
• NULL is the end of the list; a NULL head signifies an empty list

InLinkedtList
IntNode IntNode IntNode
head ele nex ele nex ele nex
empty() m t m t m t
addFront(value)
front() 42 -3 17
removeFront(i)
showList()
A IntLinkedList class
class IntNode {
private:
int elem;
IntNode* next;
public:
IntNode();
IntNode(int data);
friend class IntLinkedList;
};
class IntLinkedList {
private:
IntNode *head;

public:
IntLinkedList();
~IntLinkedList();
void addFront(int value); // add to front of list
bool empty() const; // is list empty?
int front() const; // get front element
void removeFront(); // remove front item list
void showList();
};
Adding to an empty list or to its front
• Before adding -3, list is empty:
head ele next
m
head -3
ele next
v m
42
– To add -3, we must create a new node and attach it to the list.
head = new IntNode(-3);
– To add 42 to the front, we must create a new node and attach it
to the list.
IntNode *v = new IntNode(42);
v->next = head;
head = v;
void IntLinkedList::addFront(int value){
IntNode *v = new IntNode(value);
v->next = head;
head = v;
}
Getting/removing the front node
• To get the front node element: return head->elem
ele next ele next ele next
m m m
head
42 -3 17
int IntLinkedList::front() const {
return head->elem;
}

• To remove the front node: void IntLinkedList::removeFront(){


IntNode *v = head; IntNode *v = head;
Head = head->next; head = head->next;
delete v;
delete v;
v=NULL;
}

v
ele next ele next ele next
m m m
head
42 -3 17
Check If a list is empty and print a list
• To check if a list is empty or not just we check the head
return (head==NULL); bool IntLinkedList::empty() const{
return (head==NULL);
}

• To print every value in the list, we have to traverse it:


void IntLinkedList::showList(){
IntNode *v = head;
while (v!=NULL){
cout << v->elem << " ";
v=v->next;
}
}

ele nex ele nex ele nex


head m m m
t t ... t
v 10 20 990

16-19
Construct and destruct
• To check if a list is empty or not just we check the head
IntLinkedList::IntLinkedList():head(NULL) {}
IntLinkedList *list;
list = new IntLinkedList();
List->addFront(5);
list head ele next
m
empty()
addFront(value) 5
front()
removeFront(i)
show()

• To destruct a list, we have to remove all the dynamically allocates


nodes IntLinkedList::~IntLinkedList(){
while (!empty()) removeFront();
}

ele nex ele nex ele nex


head m m m
t t ... t
10 20 990
IntLinkedList::IntLinkedList():head(NULL) {}
IntLinkedList::~IntLinkedList(){
while (!empty()) removeFront();
}
void IntLinkedList::addFront(int value){
IntNode *v = new IntNode(value);
v->next = head;
head = v;
}
bool IntLinkedList::empty() const { return (head==NULL); }

int IntLinkedList::front() const { return head->elem;}

void IntLinkedList::removeFront(){
IntNode *v = head;
head = head->next;
delete v;
v=NULL;
}
void IntLinkedList::showList() {
IntNode *v = head;
while (v!=NULL){
cout << v->elem << " ";
v=v->next;
}
}
int main() {
IntLinkedList *list = new IntLinkedList();
list->addFront(43);
list->addFront(143);
list->addFront(243);
list->showList();

list head
ele next
empty() ele next m
addFront(value)
ele next m
front() m 43
removeFront(i) 143
show() 243
Implementing Singly Linked List of String Nodes
• Similar to what we have done with IntNode and IntLinkedNode, we define
two classes StringNode and StringLinkedList
– member elem in StringNode stores the element stored in this node, which in this
case is a string and memember next stores a pointer to the next node
– We make StringLinkedList class a friend to StringNode, so that it can access
the StringNode’s private members
class StringNode { // a node in a list of strings
private:
string elem; // element value
StringNode* next; // next item in the list
friend class StringLinkedList; // provide StringLinkedList access
};

class StringLinkedList { // a linked list of strings


public:
StringLinkedList(); // empty list constructor
~StringLinkedList(); // destructor
bool empty() const; // is list empty?
const string& front() const; // get front element
void addFront(const string& e); // add to front of list
Void removeFront(); // remove front item list
private:
StringNode* head; // pointer to the head of list
}; CMPS 212 16-23
Implementing a Singly Linked List
StringLinkedList::StringLinkedList():head(NULL) {} // constructor

StringLinkedList::~StringLinkedList(){ // destructor
while (!empty()) removeFront();
}

void StringLinkedList::addFront(const string& e) { // add to front of list


StringNode* v = new StringNode; // create new node
v−>elem = e; // store data
v−>next = head; // head now follows v
head = v; // v is now the head
}
bool StringLinkedList::empty() const { // is list empty?
return (head==NULL);
}

string& StringLinkedList::front() const { // get front element


return head->elem;
}

void StringLinkedList::removeFront(){ // remove front item


StringNode* old = head; // save current head
head = old−>next; // skip over old head
delete old; // delete the old head
} CMPS 212 16-24
Insertion to/removal from the Front of a Singly Linked List

CMPS 212 16-25


Implementing Generic Singly Linked List
• Similar to what we have done, we define two template classes SNode and SLinkedList

template <class> class SLinkedList; //you add this line to let SNode become aware of

//template class SLinkedList


template <class E>
class SNode {
private:
E elem;
SNode<E> * next;
friend class SLinkedList<E>;
};
template <typename E> //typename or class keywords
class SLinkedList {
private:
Snode<E> *head;
public:
SLinkedList();
~SLinkedList();
void addFront(const E & value); // add to front of list
bool empty() const; // is list empty?
E & front() const; // get front element
void removeFront(); // remove front item list
void showList();
}; CMPS 212 16-26
Implementing a Generic Singly Linked List
template <typename E>
SLinkedList<E>::SLinkedList():head(NULL) {}

template <typename E>


SLinkedList<E>::~SLinkedList(){
while (!empty()) removeFront();
}

template <typename E>


void SLinkedList<E>::addFront(const E & value){
SNode<E> *v = new SNode<E>;
v->elem = value;
v->next = head;
head = v;
}

template <typename E>


bool SLinkedList<E>::empty() const{
return (head==NULL);
}

template <typename E>


E& SLinkedList<E>::front() const {
return head->elem;
}
CMPS 212 16-27
template <typename E>
void SLinkedList<E>::removeFront(){
SNode<E> *v = head;
Implementing a
head = head->next;
delete v;
Generic Singly
}
v=NULL;
Linked List
template <class E>
void SLinkedList<E>::showList(){
SNode<E> *v = head;
cout << endl ;
while (v!=NULL){
cout << v->elem << " ";
v=v->next;
}
cout << endl ;
}

int main() {
SLinkedList<string> list;
string first = "Laure";
list.addFront("kkkk");
list.addFront(first);
cout <<endl<<list.front();
SLinkedList<int> *list1 = new SLinkedList<int>();
list1->addFront(1);
list1->addFront(6);
list1->showList();
cout <<endl<<list1->front();
}
Arrays vs. Linked Lists

• How would you keep a linked list sorted?


• How would you search for an elements (by value or by index)?
• How would you insert an element at a specific position?
• How would you solve our students problem?
– We are going to add more methods/operations/ behaviors

– Improve the list to have links in both directions

– Introduce Tree later on in this course

CMPS 212 29
Adding to the tail of the list
• Consider following list:
data next data next
head 42 -3
data next
current v 17
– To add a node, we must modify the next pointer of the node before the
place you want to add/change
IntNode *v = new IntNode(17);
IntNode current = head;
while (current->next != null) {
current = current->next;
}
current->next = v; void IntLinkedList::addEnd(int value){
IntNode *v = new IntNode(value);
if (head==NULL)
head=v;
else{
IntNode *current = head;
while (current->next!=NULL){
current=current->next;
}
current->next=v;
}
}
Remove from the tail of the list
• Consider following list:
data next data next data next
head 42 -3 17

v
• In order to delete a node, we need to update the next link of the
node immediately preceding the deleted node

void IntLinkedList::removeEnd(){
if (head->next==NULL)
removeFront();
else{
IntNode *v = head;
while (v->next->next!=NULL){
v=v->next;
}
delete v->next;
v->next = NULL;
}
}
The size() method
// Returns current number of elements
int IntLinkedList::size(){
int count = 0;
IntNode * current = head;
while( current != NULL) {
current = current->next;
count++;
}
return count;
}

count 3
01
2
data next data next data next
head 42 -3 17
current
Adding a node between two nodes
• Consider the following list:
element 0 element 1 element 2
head data next data next data next
42 -3 17
before data next
5
v

• To add a node between two nodes, we first point to the node before
the location of the new node (Example: to add at index 2)
IntNode *v = new IntNode(5);
IntNode *before = head;
for (int i = 0; i < index - 1; i++)
before = before->next;
v->next = before->next;
before->next = v;
remove a node between two nodes
// Removes value at given index from list.
// Precondition: 0 <= index < size()
void IntLinkedList::remove(int index){
IntNode* v = head;
if (index == 0) {
// special case: removing first element
head = head->next;
} else {
IntNode* current = head;
// removing from elsewhere in the list
for (int i = 0; i < index - 1; i++) {
current = current->next;
}
v= current->next;
current->next = current->next->next;
}
delete v;
v=NULL;
}
data next data next data next
front 42 -3 17
current
v
Doubly Linked Lists
• Removing/adding an element at the tail of a singly linked list is not
easy.
– Indeed, it is time consuming to remove/add any node other than the head in a
singly linked list,
– we do not have a quick way of accessing the node immediately preceding the
one we want to remove or add.
– For some applications, it would be nice to have a way of going both directions
in a linked list.
• Concretly, How to see a doubly linked list?
• A sequence of nodes « back to back ».
• Each node gives one hand to its successor and one hand to its predecessor.
Doubly Linked List
• Doubly linked list allows to traverse backward as well as forward
through the list.
–Each node has two references to other nodes instead of one
next: points to the next node in the list
• Last node has no next node (no successor)
 prev: points to the previous node in the list
• first node has no previous node (no predecessor)
–Each list uses two pointer to access its first and last nodes
 header : refers to the first node
trailer : refers to the last node

trailer

header

JFK PVD SFO AAA

next next next next null

null prev prev prev prev


Doubly Linked List- DNode class
typedef string Elem; // list element type
class DNode { // doubly linked list node
private:
Elem elem; // node element value
DNode* prev; // previous node in list
DNode* next; // next node in list
public:
DNode();
DNode(const Elem & data);
friend class DLinkedList; // allow DLinkedList access
};

trailer

header

JFK PVD SFO AAA

next next next next null

null prev prev prev prev

37
Doubly Linked List- DLinkedList
class DLinkedList { // doubly linked list
public:
DLinkedList(); // constructor
~DLinkedList(); // destructor
bool empty() const; // is list empty?
const Elem& front() const; // get front element
const Elem& back() const; // get back element
void addFront(const Elem& e); // add to front of list
void addBack(const Elem& e); // add to back of list
void removeFront(); // remove from front
void removeBack(); // remove from back
private: // local type definitions
private:
DNode* header;
DNode* trailer;

};

trailer

header

JFK PVD SFO AAA

next next next next null

null prev prev prev prev


Doubly Linked List- DLinkedList
DNode::DNode():elem(""), next(NULL), prev(NULL){}
DNode::DNode(const Elem & data):elem(data), next(NULL), prev(NULL){}

DLinkedList::DLinkedList(): header(NULL), trailer(NULL) { }


DLinkedList::D̃LinkedList() { // destructor
while (!empty()) removeFront(); // remove all
}

bool DLinkedList::empty() const {


return ((header==NULL) && (trailer==NULL));
}

const Elem& DLinkedList::front() const { // get front element


return header−>elem;
}

const Elem& DLinkedList::back() const { // get back element


return trailer−>elem;
}
Doubly Linked List- DLinkedList
void DLinkedList::addFront(const Elem & value){
DNode* v = new DNode(value);
if (header == NULL) //check if it is an empty list
trailer = v;
else trailer null
header->prev = v; header
v->next = header; null
header = v;
} v

value

next null

null prev

trailer

header
v

value AAA BBB CCC DDD


null next next next next
next null
null prev prev prev prev 40
null prev
Doubly Linked List
void DLinkedList::addBack(const Elem& e) { // add to back of list
DNode* v = new DNode(e);
if (trailer == NULL) //check if the list is empty
header = v;
else
tailer->next = v;
v->prev = trailer;
trailer = v;
}

trailer
v
header

aaa bbb ccc ddd


e
next next next next null
next null
null prev prev prev prev
null prev

41
Doubly Linked List-Deletion
void DLinkedList::removeFront()
{
DNode* v=header;
if (header->next == NULL) //if only one item in the list
trailer = NULL;
else
header->next->prev = NULL;
header = header->next;
delete v;
v=NULL;
}
trailer

header

ssss aaaa bbbb dddd

next next next next null

v null prev prev prev prev


null
Doubly Linked List-Deletion
void DLinkedList::removeBack()
{
DNode* v = trailer;
if (trailer->prev == NULL) //if only one item in the list
header = NULL;
else
trailer->prev->next = NULL;
trailer = trailer->prev;
delete v;
v=NULL;
}
trailer

header

aaaa bbbb nnnn null gggg


next next next next null

null prev prev prev prev

v
Doubly Linked List
•Forward Traversal: start at the first node until the end of the list
DNode *current = header;
while (current != null)
current = current->next;

trailer

header

aaa nnnn jjjj frfff

next next next next null


null prev prev prev prev

current

44
Doubly Linked List
•Backward Traversal: start at the last node until the beginning of the list
DNode current =trailer;
while (current != NULL)
current = current->prev;

trailer

header

aaa bbb vvv xxxx

next next next next null


null prev prev prev prev

current

45
Doubly Linked List
Add a node at a specific insertion point in the list
DNode *v = new DNode(e);
DNode *current = header;

while (current != NULL && key != current->data)


current = current->next;
current->prev->next = v;
v->prev = current->prev;
v->next = current;
current->prev = v;

trailer
header current

aaaa bbbb target ddd

next next next next null

null prev prev prev prev


e

next

prev
v 46

You might also like