0% found this document useful (0 votes)
27 views

Data Structures: Linked Lists (Part 3)

The document discusses improving operations on linked lists by adding a tail pointer field. It describes how inserting at the end can now be done efficiently in O(1) time by updating the tail pointer, and that other operations like insertion at beginning and deletion may also need updating. The document also covers reverse traversal of linked lists, explaining how it can be done using nested loops or recursion for a more elegant solution. Finally, it briefly mentions doubly and circular linked lists.

Uploaded by

Space Cadet
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views

Data Structures: Linked Lists (Part 3)

The document discusses improving operations on linked lists by adding a tail pointer field. It describes how inserting at the end can now be done efficiently in O(1) time by updating the tail pointer, and that other operations like insertion at beginning and deletion may also need updating. The document also covers reverse traversal of linked lists, explaining how it can be done using nested loops or recursion for a more elegant solution. Finally, it briefly mentions doubly and circular linked lists.

Uploaded by

Space Cadet
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 32

Data Structures

CT077-3-2- DSTR and Version VD1

Linked Lists
(Part 3)
Topic Structure

• Linked list improvement


• Other types of linked list
- Doubly linked list
- Circular linked list

CT077-3-2-DSTR Linked List (Part 3) Slides 2 of 32


Objectives

By the end of this lesson, you will be able to:


• Improve implementation of some operations on
linked lists
• Create an abstract linked list type using C++
templates
• Implement reversed traversal of a linked list
using loops and recursion
• Understand the concept of Doubly Linked Lists,
and Circular Linked Lists

CT077-3-2-DSTR Linked List (Part 3) Slides 3 of 32


Key Terms

• Tail
• Reverse traversal
• Doubly linked list
• Circular linked list

CT077-3-2-DSTR Linked List (Part 3) Slides 4 of 32


Improving Operations

• Inserting a new item at beginning of a linked list is


fast (no traversal required)

X X
lastNode

newNode newNode

• While inserting at the end of the list required us to


traverse the whole list to reach the last element

CT077-3-2-DSTR Linked List (Part 3) Slides 5 of 32


Improving Operations

• If in a program there is a need to frequently insert


items at end of the list, it’s worth to change the data
structure to allow more efficient implementation
class LinkedList {
public:
NodeType* head;
NodeType* tail;
int size;
// .. the other implemented methods
};

CT077-3-2-DSTR Linked List (Part 3) Slides 6 of 32


Insert at the End

• Now insert at end can be implemented efficiently


void insertAtEnd(int value){
NodeType * newNode= new NodeType;
newNode->info = value;
newNode->link = NULL;
if (head == NULL)//inserting to an empty list
head = tail = newNode;
else{
tail->link = newNode;
tail = newNode;
}
size++;
}

X
5
newNode

CT077-3-2-DSTR Linked List (Part 3) Slides 7 of 32


Change Previous Implementations

• Note however that other implemented operations


may have to be changed as well to consider the
new tail field and properly update it if necessary
– Constructor and destructor
– clear()
– insertAtBeginning(…)
– deleteFirst(), deleteLast(), and deleteItemAt(…)
– insertItemAt(…)
• no change necessary if it depends on inserting at
beginning and end methods

CT077-3-2-DSTR Linked List (Part 3) Slides 8 of 32


Necessary Modifications

• For example, the constructor will be modified to:


LinkedList(){
this->size = 0;
• Inserting a new item at this->head= NULL;
this->tail= NULL;
the beginning of a list }
will affect the tail ONLY void insertAtBeginning(int value){
if the list was empty NodeType * newNode= new NodeType;
newNode->info = value;
newNode->link = head;
head = newNode;
size++;
if(tail == NULL)
tail = newNode;
}

CT077-3-2-DSTR Linked List (Part 3) Slides 9 of 32


Necessary Modifications

• deleteFirst() should also be changed


• Deleting first item in the list will affect tail ONLY if
the list becomes empty after deletion
void deleteFirst(){
if(size > 0){//else, may want to give a message or error
NodeType * toBeDeleted = head;
head = head->link;
delete toBeDeleted;
size--;

if(head == NULL)
tail = NULL;
}
}

CT077-3-2-DSTR Linked List (Part 3) Slides 10 of 32


deleteLast efficiency improvement?

• Note that deleteLast() will not benefit from the tail


pointer to improve efficiency, since we still need a
pointer to the item before the last one

beforeLast

• This can be solved later when doubly linked lists


are introduced

CT077-3-2-DSTR Linked List (Part 3) Slides 11 of 32


Reverse Traversal of a Linked List

• What if we needed to traverse list elements in


reversed order (for printing, or other purposes)?

• For array lists, this job was easy !


//..since we have fast access to any array item directly
for(int i= arrSize-1; i>=0; i--)
cout << arr [ i ] << " ";

CT077-3-2-DSTR Linked List (Part 3) Slides 12 of 32


Reverse Print of a Linked List

• One way is to repeatedly traverse the list up to a


certain decreasing limit (and print item at that limit)

Note that this


solution is not
current efficient, since
the nested loop
means we are
class LinkedList { traversing most
void printReverse(){ of list elements
for(int limit= size-1; limit>=0; limit--){ multiple times
NodeType * current = head;
for(int i= 0; i<limit; i++) Equivalent to:
current = current->link; cout<<getItemAt(limit)
cout << current->info << " ";
}
}
};
CT077-3-2-DSTR Linked List (Part 3) Slides 13 of 32
Reverse Print – Using Recursion

• Using a recursive function, a more elegant and


efficient (although not best) way can be implemented

class LinkedList {
void printReverse(){
printRecursive(head);
}
void printRecursive(NodeType * current){
if(current != NULL) {//stop recursion if we reach list end
printRecursive(current->link); //print remaining items first
cout << current->info << " "; //then print current item
}
}
};
CT077-3-2-DSTR Linked List (Part 3) Slides 14 of 32
p
r
p
i
rn How is Recursion Executed?
it
n
R
te
R
c
e
u
cr
void printRecursive(NodeType
p * current){
u
s
if(current
ri != NULL){
ri nt
printRecursive(current->link);
sv void printReverse(){ cout s << current->info << " ";
ie printRecursive(head); 6
3
7
} 5,
4,
6,
v }
e } r
o
et
f prints 45, done. return to caller
o u
fn r
n
o
n to
d
u c
e
l al
l7 le recursion ends,
4
6
3
r execution returns to
5
4
6
n
, caller
o
d
c
e
a
,l
l
n
s
o

CT077-3-2-DSTR Linked List (Part 3) Slides 15 of 32
Doubly Linked Lists

• Some applications need to efficiently access adjacent


nodes, whether before or after a certain item
• This allows list traversal in any direction
– Re-implement printReverse()

template <class T> template <class T>


class DoublyNodeType { class DoublyLinkedList {
public: public:
T info; DoublyNodeType<T> * head;
DoublyNodeType<T> * next; DoublyNodeType<T> * tail;
DoublyNodeType<T> * prev; int size;
}; };
CT077-3-2-DSTR Linked List (Part 3) Slides 16 of 32
Implementation Updates

• Operations need to be updated. For example:


void insertAtEnd(T value){
DoublyNodeType<T> * newNode= new DoublyNodeType<T>;
newNode->info = value;
newNode->next = NULL;
newNode->prev = tail;
tail = newNode;
if (head == NULL)//inserting to an empty list
head = newNode;
else
newNode->prev->next = newNode;
size++;
}

X
X
4
newNode

CT077-3-2-DSTR Linked List (Part 3) Slides 17 of 32


Implementation Updates

• Inserting at the beginning becomes


void insertAtBeginning(T value){
DoublyNodeType<T> * newNode= new DoublyNodeType<T>;
newNode->info = value;
newNode->next = head;
newNode->prev = NULL;
head = newNode;
if (tail == NULL)//inserting to an empty list
tail = newNode;
else
newNode->next->prev = newNode;
size++;
}

X
X

4 newNode

CT077-3-2-DSTR Linked List (Part 3) Slides 18 of 32


Implementation Updates
• Inserting at an arbitrary index (e.g. index=2)
void insertItemAt(T value, int index){
if(index <= size)
if(index == 0)
insertAtBeginning(value);
else if(index == size)
insertAtEnd(value);
else{ //the general case
DoublyNodeType<T> * newNode= new DoublyNodeType<T>;
newNode->info = value;
DoublyNodeType<T> * beforeThis = head; Can be optimized if (index > size/2)
for(int i=0; i<index; i++) by starting from the tail and moving
backwards to find beforeThis.
beforeThis = beforeThis->next; More on this later
//code continues
}
}

beforeThis newNode
CT077-3-2-DSTR Linked List (Part 3) Slides 19 of 32
Inserting at Arbitrary Index
//code continues
newNode->next = beforeThis;
newNode->prev = beforeThis->prev;
beforeThis->prev = newNode;
newNode->prev->next = newNode;
size++;

newNode beforeThis

beforeThis

newNode

CT077-3-2-DSTR Linked List (Part 3) Slides 20 of 32


Deleting a List Item

• Code for deleting a node from beginning, end, or arbitrary


index must also be updated
• For example, deleting item at index 1
//locate the
node //to be
deleted
toDelete

//if prev and next nodes exist


teDelete->prev->next =
toDelete->next;
toDelete->next->prev =
toDelete->prev;

//update head/tail if necessary


delete toDelete;
size--;

CT077-3-2-DSTR Linked List (Part 3) Slides 21 of 32


Notes on Doubly Linked Lists

• The main advantage of Doubly Linked Lists is to


efficiently traverse list nodes in both directions
– Applications: to implement back and forward buttons in a
web browser, undo and redo actions, etc.

• We can also use two-way traversal to reduce time


required to reach an item at a given index
– if index <= size/2, start from head and move forward
– otherwise, start at tail and move backward
– on average, would make insertItemAt(), deleteItemAt(),
getItemAt() and setItemAt() 2 times faster
CT077-3-2-DSTR Linked List (Part 3) Slides 22 of 32
Circular Linked Lists

• Some problems are inherently circular (squares on a


Monopoly board), and many solutions can be solved
more naturally using a circular data structure (round
robin scheduling, players taking turns, playing video
and sound files in “looping” mode, etc.)
• A linked list is made circular if its end points to its
beginning, i.e. the last node points to the first one
– Circular traversal is made more natural, rather than always
testing if we have reached to the end and starting over

CT077-3-2-DSTR Linked List (Part 3) Slides 23 of 32


Circular Linked Lists

• Data representation for a Circular Linked List is not


different from the normal Linked List
– The concept is just not to store NULL at the link field of last
node, and respect that in implementing all operations
– Often, tail pointer is only used instead of head

template <class T>


class CircularLinkedList {
public:
NodeType<T> * head;
NodeType<T> * tail;
int size;
};
CT077-3-2-DSTR Linked List (Part 3) Slides 24 of 32
Circular Doubly Linked Lists

• Circularity can be also applied to a doubly linked list,


making it Circular Doubly Linked List

template <class T>


class CircularDoublyLinkedList {
public:
DoublyNodeType<T> * tail;
int size;
};
CT077-3-2-DSTR Linked List (Part 3) Slides 25 of 32
Circular Linked List Operations

• Operations must be updated accordingly, e.g.


template <class T>
class CircularLinkedList {
public:
NodeType<T> * tail;
int size;

void print(){
if(tail != NULL){
NodeType<T> * current = tail->link;
do{
cout << current->info << " ";
current = current->link;
}while (current != tail->link);
cout << endl;
}
}
};

CT077-3-2-DSTR
current
Linked List (Part 3) Slides 26 of 32
Circular Linked List Operations

• Insert at beginning becomes


void
voidinsertAtBeginning(T
print(){ value){
NodeType<T> * newNode=
if(tail != NULL){new NodeType<T>;
newNode->info
NodeType =* value;
current = tail->next;
if (tail
do{ == NULL){//inserting to an empty list
tail =cout
newNode;
<< current->info << " ";
tail->link = tail;
current = current->next;
} }while (current != tail->next);
else{ cout << endl;
newNode
newNode->link
} = tail->link;
tail->link
} = newNode;
}
size++;
}

X
4
newNode
CT077-3-2-DSTR Linked List (Part 3) Slides 27 of 32
Array or Linked List?
• Finally, should we always use linked lists?
• Decision depends on what operations will be used
most frequently, and which factors (speed/memory)
are more critical. Following are some hints:
– Number of elements is known: use array
– Dynamic addition and expansion: linked list
– Deletion at any position: linked list
– Need lots of random access: use array
– Searching and items are unsorted: both same
– Searching and items are sorted: array (binary search)
– Sorting using bubble sort: both same
– Sorting using other methods: depends on the method
CT077-3-2-DSTR Linked List (Part 3) Slides 28 of 32
Summary of Main Teaching

• Linked list improvement


• Other types of linked list
- Doubly linked list
- Circular linked list

CT077-3-2-DSTR Linked List (Part 3) Slides 29 of 32


Quick review

• Explain the advantages and disadvantages of


using recursive function and doubly linked list for
reverse traversal in linked list.
• Give example of applications that are better
implemented using array instead of linked list
and vice versa.

CT077-3-2-DSTR Linked List (Part 3) Slides 30 of 32


CT077-3-2-DSTR Linked List (Part 3) Slides 31 of 32
What will we cover next

• Stack
– Applications
– Operations
– Implementation

CT077-3-2-DSTR Linked List (Part 3) Slides 32 of 32

You might also like