CS1301: DATA STRUCTURES
UNIT-3: LINKED LISTS
Singly linked lists, linked stacks and queues, doubly linked lists and
dynamic storage management, circular linked list, Applications of
Stacks, Queues and Linked lists.
Abstract Data Type (ADT)
• An Abstract Data Type is:
• Set of values
• Set of operations which can be uniformly applied to these values
• Set of Axioms
What is List?
• Countable number of ordered values
• Each occurrence of a value is a distinct item
• Implemented differently in different programming
languages
• Linked list is an implementation of list
List Abstract Data Type
• List ADT has:
• Values based on what type of data list stores
• Main operations:
• new() : creates a new list
• prepend(L, key): add element key to front of list L
• append(L, key): add element key to end of list L
• remove(L, key): removes element key from list L
• search(L, key): find location of element key in L
• head(L): returns the first object in L
• isEmpty(L): checks whether list L is empty or not
• Axioms:
• Based on implementation
Drawbacks with Array
• Is array also a list?
Drawbacks with Array
• Is array also a list?
• Yes, certainly.
• Problem: An array has a limited number of elements
• Routines inserting a new value have to check that there is room
• Solution: Multiple solutions exist
• Increase the size of array with some constant each time array is full
• Double the size of array each time array is full
• Use Linked List data structure
Linked List(LL)
• Nodes stores the data/values
• Sequence of nodes; each node points to next node in list
• Add node dynamically when required
• Can store infinite data until memory of system is
exhausted
• The address of first node is stored in separate location
called as head or first
• Last node contains a null link
head
a b c d
Dynamic Memory Allocation(1)
• Allocate memory to elements one at a time as needed,
have each element keep track of the next element
• Result is referred to as linked list of elements, track next
element with a pointer
Array of Elements in Memory
Jane Bob Anne
Linked List
Jane Anne Bob
More Terminology
• A node’s successor is the next node in the sequence
• The last node has no successor
• A node’s predecessor is the previous node in the
sequence
• The first node has no predecessor
• A list’s length is the number of elements in it
• A list may be empty (contain no elements)
• Linked list in context of C are sometime referred as List
• Until not specified, linked list refers to Singly-Linked List
LL with 3 nodes (dynamic)
void main() { /*assume data is int */
LinekedList ListStart = NULL; /*safe to give a legal
value*/
Pictorially In Memory
ListStart ListStart
0
100
ListStart = (Eptr)malloc(sizeof(Estruct));
/* ListStart points to memory allocated at location 108 */
ListStart ListStart Data Next
? ? 108 ? ?
Data Next 100 108
}
Declaration of LL
• Because the node of linked list contains 2 components it
should be declared as class or struct
struct nodeType
{
int info;
nodeType *link;
};
Properties of LL
VALUE EXPLANATION
head 2000
Because head is 2000 and the info of
head -> info 17
the node at location 2000 is 17
head -> link 2800
Because head -> link is 2800 and the
head -> link -> info 92
info of the node at location 2800 is 92
• Suppose that current is a pointer which has type as
pointer head. Then, the statement : current=head copies
the value of head into current
value
current 2000
current -> info 17
current -> link 2800
current -> link -> info 92
Insertion in Linked List
• Involves two step:
• Finding the correct position
• Doing the work to add the node
• Three possible positions:
• Front
• End
• Somewhere in the middle
Insertion in Linked List (Front)
• Finding the correct location
• No work required as already known
• Irrespective of list is empty or not, header will always point to
correct location
Insertion in Linked List (Front)
• Add new node to list
• Save element in data field of new node
ListStart New Node
5 9 6 X 3
Insertion in Linked List (Front)
• Add new node to list
• Save element in data field of new node
• Make new node’s next pointer to point start of existing list
ListStart New Node
5 9 6 X 3
Insertion in Linked List (Front)
• Add new node to list
• Save element in data field of new node
• Make new node’s next pointer to point start of existing list
ListStart New Node
5 9 6 X 3
• Make start of list point to new node in main
Insertion in Linked List (End)
• Find last of list
ListStart
5 9 6 X
Last
Insertion in Linked List (End)
• Find last of list
• Add new node to list
• Save element in data field of new node
• Save NULL in next field of new node
• Make last node’s next pointer to point new node
ListStart New Node
5 9 6 3 X
Last
Insertion in Linked List (Between)
• Find the node you want to insert after
ListStart
5 9 6 X
After
Insertion in Linked List (Between)
• Find the node you want to insert after
• Add new node to list
• Save element in data field of new node
• Save address of following node in next field of new node
• Save address of new node in “after” node
ListStart New Node
5 9 6 X 3
After
Item insertion to LL
• Consider linked list before insertion
• variable declaration
nodeType *head, *p, *q, *newnode;
• Suppose that p points to the node with info 65, and a new
node with info 50 is to be created and inserted after p
newNode = new nodeType;//create newNode
newNode -> info = 50;//store 50 in the new node
newNode -> link = p -> link;
p -> link = newNode;
p -> link = newNode;
newNode -> link = p -> link;
• newNode points back to itself and the remainder of the list is lost
• by using to pointers insertion code can be simplified
• suppose q points to the node with info 34
newNode -> link = q;
p -> link = newNode;
Above statements insert newNode between p and q
Print a Linked List
• Use a “walker” to examine list from start to end
void printList() {
node *temp = start; /*temp is walker variable*/
while (temp != NULL) {
cout<<temp->data;
temp = temp->next;
}
}
• Why we need “walker” when we already have “start”?
• Try to avoid start, could be required later in the function
Operations on LL
Print the LL Destroy the LL
current = first; while (first != NULL)
//set current so that it points to //while there are nodes in the list
{
//the first node temp = first;
while (current != NULL) //set temp to the current node
//while more data to print first = first->link;
{ //advance first to the next node
cout << current->info << " "; delete temp;
//deallocate the memory occupied by temp
current = current->link; }
} last = NULL;
}//end print //initialize last to NULL; first has already
//been set to NULL by the while loop
Search in Linked List
• Compare every element in list; return pointer to node if
found else return NULL
• Can we perform binary search given elements are sorted?
• Yes, but the performance will be similar to linear search
Search in Linked List
• Search 9
ListStart
5 9 6 X
findP
• Search 4
ListStart
5 9 6 X
findP
Deletion in Linked List
• Involves two steps:
• Find the node to be deleted
• Change its predecessor to point its successor
• Deletion from front
• Change header to point to second node
• Deletion from elsewhere
• Change predecessor to point successor
Deletion in Linked List (Front)
• Change the header to point to second node.
• Set first node free
• Set start to returned pointer after deletion
Deletion in Linked List (Except Front)
• Change the predecessor to point to successor
• Set deleted node free
Deletion
• We have LL shown below
• How to delete node with info 34
• p->link = p->link->link;
• the node with info 34 is removed from the list. However, the
memory is still occupied by this node, and this memory is
inaccessible; that
• is, this node is dangling. To deallocate the memory, we need a
pointer to this node.
Deallocate deleted node
• The following statements delete the node from the list and deallocate the memory
occupied by this node.
q = p->link;
p->link = q->link;
delete q;
Building LL
• Suppose that the nodes are in the usual info-link form,
and info is of type int.
• Assume to process the following data: 2, 15, 8, 24, 34
• Will be needed:
• three pointers to build the list: one to point to the first node in the
list, which cannot be moved;
• one to point to the last node in the list;
• and one to create the new node.
nodeType *first, *last, *newNode;
int num;
Building LL…
nodeType *first, *last, *newNode; newNode->link = NULL;
int num; /*initialize the link field of newNode to NULL*/
if (first == NULL)
/*Suppose that first points to the first node in the //if first is NULL, the list is empty;
list. Initially, the list is empty, so both first and last //make first and last point to newNode
are NULL*/ {
first = NULL; first = newNode;
last = NULL; last = newNode;
}
cin >> num; //read a number in num else //list is not empty
{
newNode=new nodeType;
last->link = newNode;
/*allocate memory of type nodeType //insert newNode at the end of the list
and store the address of the last = newNode;
allocated memory in newNode*/ //set last so that it points to the
newNode->info = num; //actual last node in the list
/*copy the value of num into the }
info field of newNode*/
Building LL…
nodeType *first, *last, *newNode;
int num; 4 newNode->link = NULL;
/*initialize the link field of newNode to NULL*/
/*Suppose that first points to the first node in the 5 if (first == NULL)
list. Initially, the list is empty, so both first and last //if first is NULL, the list is empty;
are NULL*/ //make first and last point to newNode
first = NULL; {
last = NULL; 5a first = newNode;
5b last = newNode;
1 cin >> num; //read a number in num }
2 newNode=new nodeType;
6 else //list is not empty
/*allocate memory of type nodeType {
and store the address of the 6a last->link = newNode;
allocated memory in newNode*/ //insert newNode at the end of the list
3 newNode->info = num; 6b last = newNode;
/*copy the value of num into the //set last so that it points to the
//actual last node in the list
info field of newNode*/ }
• We now repeat statements 1 through 6b. After
• After statement 1 executes, num is 2. Statement
2 creates a node and stores the address of that statement 1 executes, num is 15. Statement 2
node in newNode. Statement 3 stores 2 in the creates a node and stores the address of this
info field of newNode, and statement 4 stores node in newNode. Statement 3 stores 15 in the
NULL in the link field of newNode info field of newNode, and statement 4 stores
NULL in the link field of newNode
• Because first is NULL, we execute statements
5a and 5b.
• Because first is not NULL, we execute
statements 6a and 6b.
Building LL Backward
• For the previously given data 2, 15, 8, 24, 34 the linked
list is as shown below as
Backward LL
Forward LL
Pseudo code of building Backward LL
• 1. Initialize first to NULL.
• 2. For each item in the list,
• Create the new node, newNode.
• Store the item in newNode.
• Insert newNode before first.
• Update the value of the pointer first.
C++ function to build Backward LL
nodeType* buildListBackward()
{
nodeType *first, *newNode;
int num;
cout<<"Enter a list of integers ending with -1."<<endl;
cin >> num;
first = NULL;
while (num != -1)
{
newNode = new nodeType; //create a node
newNode->info = num; //store the data in newNode
newNode->link = first; //put newNode at the beginning of the list
first = newNode; //update the head (first) pointer of the list
cin >> num; //read the next number
}
return first;
} //end buildListBackward
Linked List Variation: Dummy Head Node
Using "dummy" first (head) node:
Empty linked list
ListStart
?
Sample list:
ListStart
? 5 9 6
• Why?
• No special case for inserting/deleting at beginning
• Header (ListStart) does not change after it is initialized
• Disadvantage
• cost of one extra element
Linked List Variation: Sorted List
• Idea: Keep the items on the list in a sorted order
• sort based on data value in each node
• Advantages:
• already sorted
• operations such as delete, find, etc. need not search to the end of the
list if the item is not in list
• Disadvantages
• insert must search for the right place to add element (slower than simply
adding at beginning)
Doubly Linked List
• Each node contain data, link to its successor and a link to
its predecessor
• Two headers pointing to first and last node respectively or
pointing to NULL (if list is empty)
myDL
First L Last
a b c
Doubly Linked List
• Advantages:
• Can be traversed in either direction (may be essential for some
programs)
• Some operations, such as deletion and inserting before a node,
become easier
• Disadvantages:
• Requires more space
• List manipulations are slower (because more links must be
changed)
• Greater chance of having bugs (because more links must be
manipulated)
Insertion in DLL
• Change forward and backward pointers accordingly
• Insert element “d” after “a”
myDL
First L Last
a b c
New Node
d
Insertion in DLL
• Change forward and backward pointers accordingly
• Eg. Insert element “d” after “a”
myDL
First L Last
a b c
New Node
d
Deletion in DLL
• Change forward and backward pointers accordingly
• Insertion and Deletion in beginning and last are special
cases and should be handled differently
• Eg. Delete “b” from previous list
myDL
First L Last
a X b X c
X X
Deletion in DLL
• Change forward and backward pointers accordingly
• Insertion and Deletion in beginning and last are special
cases and should be handled differently
• Eg. Delete “b” from previous list
myDL
First L Last
a c
Stack using LL
• Since all the action happens at the top of a stack, a
singly-linked list (SLL) is a fine way to implement it
• The header of the list points to the top of the stack
myStack:
44 97 23 17
• Pushing is inserting an element at the front of the list
• Popping is removing an element from the front of the list
Stack using LL
• Initialize stack; set header to NULL
myStack:
NULL
Stack using LL
• Initialize stack; set header to NULL
• push(44)
myStack:
44 NULL
Stack using LL
• Initialize stack; set header to NULL
• push(44)
• push(97)
myStack:
97 44 NULL
Stack using LL
• Initialize stack; set header to NULL
• push(44)
• push(97)
• push(23)
myStack:
23 97 44 NULL
Stack using LL
• Initialize stack; set header to NULL
• push(44)
• push(97)
• push(23)
• pop()
myStack:
97 44 NULL
Stack using LL
• Initialize stack; set header to NULL
• push(44)
• push(97)
• push(23)
• pop()
• push(17)
myStack:
17 97 44 NULL
Stack using LL
• Initialize stack; set header to NULL
• push(44)
• push(97)
• push(23)
• pop()
• push(17)
• pop()
myStack:
97 44 NULL
Stack using LL details
• With a linked-list representation, overflow will not happen
(unless you exhaust memory, which is another kind of
problem)
• Underflow can happen, and should be handled the same
way as for an array implementation
• When a node is popped from a list, and the node
references an object, the reference (the pointer in the
node) does not need to be set to NULL
• Unlike an array implementation, it really is removed--you can no
longer get to it from the linked list
Queue using LL
• In a queue, insertions occur at one end, deletions at the
other end
• Operations at the front of a singly-linked list (SLL) are
O(1), but at the other end they are O(n)
• Because you have to find the last element each time
• In order to implement both insertions and deletions in
O(1) time in a SLL
• You always need a pointer to the first thing in the list
• You can keep an additional pointer to the last thing in the list
Queue using LL
• In an SLL you can easily find the successor of a node, but
not its predecessor
• Remember, pointers (references) are one-way
• If you know where the last node in a list is, it’s hard to
remove that node, but it’s easy to add a node after it
• Hence,
• Use the first element in an SLL as the front of the queue
• Use the last element in an SLL as the rear of the queue
• Keep pointers to both the front and the rear of the SLL
Enqueueing a Node
• Create a new node
last
first New Node
44 97 23 X 17 X
Enqueueing a Node
• Create a new node
• Change pointer of last node to point new node
last
first New Node
44 97 23 17 X
Enqueueing a Node
• Create a new node
• Change pointer of last node to point new node
• Change last pointer to point to new node
last
first New Node
44 97 23 17 X
Dequeueing a Node
• Change “first” to point to second node
last
first
44 97 23 17 X
Dequeueing a Node
• Change “first” to point to second node
• Optionally, set deleted node free
last
first
44 97 23 17 X
Queue using LL details
• With a linked-list representation, overflow will not happen
(unless you exhaust memory, which is another kind of
problem)
• Underflow can happen, and should be handled the same
way as for an array implementation
• When a node is dequeued from a list, and the node
references an object, the reference (the pointer in the
node) does not need to be set to NULL
• Unlike an array implementation, it really is removed--you can no
longer get to it from the linked list
Polynomial Representation in LL
• Represent polynomial expression using Linked List
• A node in linked list stores coefficient and exponent of
each term in polynomial expression
• Eg.
5 12 2 9 -1 3 NULL
Polynomial Addition
• Consider Two polynomial expressions
• Addition of above two expression is
Algo for Polynomial Addition
• Represent two polynomials in two linked lists L1 and L2
• Create a third empty linked list L3
• Compare the items in L1 with the items in L2
• If there is no item having the same exponent, append these items
to the third list.
• If there are two items with the same exponent exp and coefficient
coff1 and coff2, append an item with exponent exp and coefficient
coff1+coff2 to L3
Pseudocode for Polynomial Addition
Polyadd(list L1, list L2, list L3)
while (L1 != NULL and L2 != NULL) //While both list are not empty
if (L1->pow > L2->pow) //if power of L1 is greater than L2, append L1 to L3
L3->coff = L1->coff; L3->pow = L1->pow;
L1 = L1->next;
if (L1->pow < L2->pow) //if power of L2 is greater than L1, append L1 to L3
L3->coff = L2->coff; L3->pow = L2->pow;
L2 = L2->next;
if (L1->pow = L2->pow) //if power of L1 & L2 is equal, add coefficient
L3->coff = L1->coff + L2->coff; L3->pow = L1->pow;
L1 = L1->next; L2= L2->next;
while (L1 != NULL) //if L2 has reached end, append remaining terms of L1
L3->coff = L1->coff; L3->pow = L1->pow; L1 = L1->next;
while (L2 != NULL) //if L1 has reached end, append remaining terms of L2
L3->coff = L2->coff; L3->pow = L2->pow; L1 = L2->next;
Example Polynomial Addition
L1
5 12 2 9 -1 3 NULL
L2
5 11 -4 9 2 3 -1 1 NULL
L3
NULL
Example Polynomial Addition
L1
5 12 2 9 -1 3 NULL
L2
5 11 -4 9 2 3 -1 1 NULL
L3
5 12 NULL
Example Polynomial Addition
L1
5 12 2 9 -1 3 NULL
L2
5 11 -4 9 2 3 -1 1 NULL
L3
5 12 5 11 NULL
Example Polynomial Addition
L1
5 12 2 9 -1 3 NULL
L2
5 11 -4 9 2 3 -1 1 NULL
L3
5 12 5 11 -2 9 NULL
Example Polynomial Addition
L1
5 12 2 9 -1 3 NULL
L2
5 11 -4 9 2 3 -1 1 NULL
L3
5 12 5 11 -2 9 1 3 NULL
Example Polynomial Addition
L1
5 12 2 9 -1 3 NULL
L2
5 11 -4 9 2 3 -1 1 NULL
L3
5 12 5 11 -2 9 1 3
-1 1 NULL
Algo for Polynomial Subtraction
• Represent two polynomials in two linked lists L1 and L2
• Create a third linked list L3, with coefficient of L2 negated
• Perform the algo for addition of list L1 and L3
Sparse Matrices
• Sparse: Many elements are zero
• Dense: Many elements are non-zero
• A matrix is a collection of relation between two entities of
same of different groups
• Eg. Airline Flight Matrix between cities
city
0 0 3 0 4
0 0 5 7 0
0 0 0 0 0
0 2 6 0 0
4 1 0 3 0
Structured Sparse Matrices
• Has a proper structure of zero and non-zero elements
• Eg. Diagonal, tridiagonal, lower triangular
Diagonal Tridiagonal
a1 0 0 0 a1 b1 0 0
0 a2 0 0 c1 a2 b2 0
0 0 a3 0 0 c2 a3 b3
0 0 0 a4 0 0 c3 a4
• May be mapped into a 1D array so that a mapping
function can be used to locate an element.
Unstructured Sparse Matrices
• Airline flight matrix.
airports are numbered 1 through n
flight(i, j) = list of nonstop flights from airport i to airport j
n = 1000 (say)
n x n array of list references (assuming 4Bytes for one reference)
=> 4 million bytes
total number of flights = 20,000 (say)
need at most 20,000 list references => at most 80,000 bytes
Representation of
Unstructured Sparse Matrix(USM)
• Single linear list in row-major order.
• scan the nonzero elements of the sparse matrix in row-major
order
• each nonzero element is represented by a triple (row, column,
value)
• the list of triples may be an array list or a linked list (chain)
Example: 00304
00570
00000
02600
Array Representation of USM
Example: 00304
00570
00000
02600
Element 0 1 2 3 4 5
Row 1 1 2 2 4 4
Column 3 5 3 4 2 3
Value 3 4 5 7 2 6
LL Representation of USM
Example:0 0 3 0 4 Row 1 1 2 2 4 4
00570
LL = Column 3 5 3 4 2 3
00000
02600 Value 3 4 5 7 2 6
1 3 3 1 5 4 2 3 5 2 4 7
4 2 2 4 3 6 NULL
Array of LL Representation of USM
Example:0 0 3 0 4
00570
00000
02600
3 3 5 4 NULL
3 5 4 7 NULL
NULL
2 2 3 6 NULL
Row[]
Memory Requirements (Approx.)
• 500 x 500 matrix with 1994 nonzero elements
• 2D Array 500*500*4 = 106 Bytes
• Single Array List 3*1994*4 = 23,928 Bytes
• Array of LL 23,928 + 500*4 = 25,928 Bytes
Circular Linked List(CLL)
• Extension of Linear/Singly Linked List where last node
points to beginning of list
• Can be implemented in two ways
1. Header Pointer: An extra pointer pointing to start of list
2. Header Node: A special node in the beginning of list
• Header node stores some special value, like a negative number if
list contains only positive numbers
• We can use flag to specify header and non-header nodes
• CLL does not have any NULL pointer except for empty list
Circular Linked List (CLL)
• Empty CLL
ListStart
NULL
• Non-Empty CLL
ListStart
5 9 6
• Header Node
-1 5 9 6
Traversing in CLL
void print(Eptr ListStart) {
Eptr temp = ListStart;
print (temp->data);
while (temp->next != ListStart) {
print (temp->data);
temp = temp->next;
}
}
Advantages of CLL
• Some operations can be made efficient in CLL compare to
Singly LL like search multiple entries subsequently
• CLL are useful when element of lists are to be visited in
“Loop” fashion
Insertion in CLL
Insert in Empty CLL
• Change Header to point new node and new node to point
itself
Insert at Start
• Create new node; point to existing first node
• Change last node to point to new node; change header
Insert in middle
• Change new node to point successor of the node after
which node is inserted
• Change predecessor to point to new node
Deletion in CLL
Delete Last node
• If node points to itself, set header to point to null
Delete from Front
• Change last node to point second node in list
• Update header to point to second node
Delete from middle
• Change predecessor to point successor