FDS Unit 4 Notes
FDS Unit 4 Notes
Unit – IV
Linked List
Introduction to Static and Dynamic Memory Allocation
In programming, memory allocation refers to reserving memory space for data storage during
the execution of a program. There are two main types:
o Memory is allocated at compile-time, meaning the size and type of data must be known
before the program runs.
o This allocation is usually managed by the compiler, and variables are stored in the stack.
o Since the memory size is fixed, static allocation is faster but less flexible.
o Memory is allocated at runtime, allowing for flexibility in memory size as the program
requires.
o This allocation is handled on the heap, and the program can request and release memory
as needed.
o Functions like malloc and free in C or new and delete in C++ are used for dynamic
allocation.
o Dynamic allocation is slower but offers better memory management for variable data
sizes.
1. Linked List
A linked list is a linear data structure which can store a collection of "nodes" connected together
via links i.e. pointers. Linked lists nodes are not stored at a contiguous location, rather they are
linked using pointers to the different memory locations. A node consists of the data value and
a pointer to the address of the next node within the linked list.
2|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
A linked list is a dynamic linear data structure whose memory size can be allocated or de-
allocated at run time based on the operation insertion or deletion, this helps in using system
memory efficiently. Linked lists can be used to implement various data structures like a stack,
queue, graph, hash maps, etc.
A linked list starts with a head node which points to the first node. Every node consists of data
which holds the actual data (value) associated with the node and a next pointer which holds the
memory address of the next node in the linked list. The last node is called the tail node in the
list which points to null indicating the end of the list.
In case of arrays, the size is given at the time of creation and so arrays are of fixed length
whereas Linked lists are dynamic in size and any number of nodes can be added in the linked
lists dynamically. An array can accommodate similar types of data types whereas linked lists
can store various nodes of different data types.
A dynamic data structure is a data structure that can grow, shrink, or change in size during
program execution. Unlike static data structures (e.g., arrays), which have a fixed size
determined at compile-time, dynamic data structures can adjust their size based on the
program's needs, thanks to dynamic memory allocation.
1. Flexible Size: They can expand or contract as elements are added or removed,
providing efficient use of memory.
2. Efficient Memory Use: Memory is allocated only when needed, which helps manage
memory more effectively.
1.1 Operations
3|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
The basic operations in the linked lists are insertion, deletion, searching, sorting, traversing,
finding length, concatenating, and deleting an element at a given key.
1. Creation:
o Sets the head (and optionally tail) to NULL, preparing the list to add nodes.
o The first step before performing any other operation on the list.
2. Insertion:
o Can occur at the beginning (head), at the end (tail), or at a specified position within the
list.
3. Deletion:
o Pointers need to be updated to maintain the structure, and memory for the deleted node
should be freed.
4. Traversal:
o Accesses each node in the list sequentially, usually starting from the head.
o Used to search for elements, print the list, or perform operations on each element.
5. Search:
o Involves traversing the list until the element is found or the end of the list is reached.
6. Finding Length:
Typically involves traversing through each node and counting until the end of the list.
4|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
7. Concatenation:
Involves linking the end of the first list to the head of the second list.
8. Sorting:
Arranges the nodes in the list in a specific order, such as ascending or descending.
Common sorting algorithms for linked lists include bubble sort, merge sort, and
insertion sort, with adjustments to handle pointers rather than indices.
The data is generally stored in key sequence in a list which has a head structure consisting
of count, pointers and address of compare function needed to compare the data in the list.
The data node contains the pointer to a data structure and a self-referential pointer which points
to the next node in the list.
remove() – Remove the first occurrence of any element from a non-empty list.
A singly linked list is a fundamental data structure in computer science and programming, it
consists of nodes where each node contains a data field and a reference to the next node in
the node. The last node points to null, indicating the end of the list. This linear structure
5|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
supports efficient insertion and deletion operations, making it widely used in various
applications.
Node Structure
In a singly linked list, each node consists of two parts: data and a pointer to the next node. The
data part stores the actual information, while the pointer (or reference) part stores the address
of the next node in the sequence. This structure allows nodes to be dynamically linked together,
forming a chain-like sequence.
In this representation, each box represents a node, with an arrow indicating the link to the next
node. The last node points to NULL, indicating the end of the list.
struct Node {
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {} // Constructor
//for node
};
Opera ons on Singly Linked List
Creating a Singly Linked List involves initializing an empty list and then adding nodes to it
as needed. The list starts with head set to nullptr, indicating it’s empty. Nodes can be added
at the beginning, end, or any specific position to build the list progressively.
struct Node {
int data;
Node* next;
Node(int value) : data(value), next(nullptr) {} // Constructor
6|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
//for node
};
Traversal in a Singly Linked List involves visiting each node in the list, typically starting
from the head node and moving through each node sequentially until reaching the end (where
the next pointer is nullptr). This operation is commonly used to display node values, search
for specific data, or perform any action on each element of the list.
cout << current->data << " "; // Process the current node
Searching in a Singly Linked List involves traversing the list from the head to find a node
containing a specific value. The process continues until the value is found or the end of the list
is reached. This linear search is efficient for small lists, but since a singly linked list requires
sequential access, it can be slower for larger lists.
Finding the Length in a Singly Linked List involves traversing the list from the head and
counting each node until reaching the end. This process provides the total number of nodes in
the list.
}
5. Insertion in Singly Linked List
Insertion in a Singly Linked List involves adding a new node at a specified position—
typically at the beginning, at the end, or at a specific index within the list. Each insertion
requires adjusting pointers to ensure the list remains properly linked.
Types of Insertion
a. Insertion at the Beginning: It involves adding a new node as the first element in the list.
This is done by setting the new node's next pointer to the current head node and then updating
the head to point to the new node. This operation is efficient since it requires only a few pointer
adjustments.
Deletion involves removing a node from the linked list. Similar to insertion, there are different
scenarios for deletion:
a. Deletion at the Beginning: It involves removing the first node (head node) from the list.
This is done by updating the head pointer to the next node in the list, then freeing the memory
of the removed node.
Sorting in a Singly Linked List involves rearranging the nodes in a specific order, typically
in ascending or descending order based on their values. One common approach to sort a singly
14 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
linked list is to use the Bubble Sort algorithm, which repeatedly steps through the list,
compares adjacent nodes, and swaps them if they are in the wrong order.
bool swapped;
do {
swapped = false; // Initialize swapped as false
Node* current = head; // Start from the head of the list
Concatenation in Singly Linked Lists involves joining two singly linked lists to form a single
list. This is achieved by linking the last node of the first list to the head of the second list. The
process is efficient since it does not require creating new nodes; instead, it simply adjusts
pointers.
Node* current = head1; // Start from the head of the first list
while (current->next != nullptr) { // Traverse to the last node
// of the first list
current = current->next;
}
i) Search given roll no and delete that record. Draw diagram of opera on.
ii) Add given number a er specified number in the list. Draw diagram of opera on.
Answer:
struct StudentNode {
int roll_number;
string name;
StudentNode* next;
};
} else {
delete newNode; // Specified roll number not found, discard
// new node
}
}
Diagram of Operation:
Q. Write pseudocode to perform merging of two sorted singly linked lists of integers into
third list. Write complexity of it.
head2 = head2->next;
}
Time Complexity: O(n + m) where n is the number of nodes in the first list and m is
the number of nodes in the second list. This is because we traverse each list exactly
once.
Space Complexity: O(1) if we are merging in place and not using additional data
structures for the merged list. The space used for the merged list nodes is not counted
since we are merely rearranging the existing nodes without creating new ones.
A doubly linked list is a data structure that consists of a set of nodes, each of which contains
a value and two pointers, one pointing to the previous node in the list and one pointing to
the next node in the list. This allows for efficient traversal of the list in both directions,
making it suitable for applications where frequent insertions and deletions are required.
In a data structure, a doubly linked list is represented using nodes that have three fields:
1. Data
Each node in a Doubly Linked List contains the data it holds, a pointer to the next node in
the list, and a pointer to the previous node in the list. By linking these nodes together through
the next and prev pointers, we can traverse the list in both directions (forward and
backward), which is a key feature of a Doubly Linked List.
21 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
struct Node {
int data;
Node* next;
Node* prev;
};
1. Creation of a Doubly Linked List: To create a doubly linked list, we link each node’s next
pointer to the following node and its prev pointer to the preceding node.
struct Node {
int data;
Node* next;
Node* prev;
};
Node* createDoublyLinkedList(int values[], int n) {
if (n == 0) return nullptr; // Empty list
Traversal in a doubly linked list can occur in two directions: forward (from head to tail) and
backward (from tail to head). This is possible because each node contains pointers to both the
next and previous nodes.
1. Forward Traversal:
o Start from the head and move through each node using the next pointer until
reaching a node with next as nullptr.
2. Backward Traversal:
o Start from the tail and move back through each node using the prev pointer
until reaching a node with prev as nullptr.
current = current->prev;
}
cout << endl;
}
3. Searching in a Doubly Linked List
Searching in a doubly linked list involves traversing the list from either the head (for a
forward search) or the tail (for a backward search) to find a node with a specified value. Since
each node in a doubly linked list has both next and prev pointers, you can search in both
directions efficiently.
To find the length of a doubly linked list, you need to traverse the list from the head to the
tail, counting each node until reaching the end.
}
5. Insertion in a Doubly Linked List:
Inserting a new node in a doubly linked list can be done at the beginning, end, or at a specified
position. When inserting, both next and prev pointers of the new node and adjacent nodes
must be updated to maintain the bidirectional links.
a. Insertion at the beginning in a doubly linked list: It involves adding a new node before
the current head node. After insertion, the new node becomes the new head of the list, and its
pointers must be updated to maintain the doubly linked structure.
if (head != nullptr) {
head->prev = newNode; // Update the current head's prev to
// point to the new node
}
head = newNode; // Make the new node the new head of the list
}
b. Insertion at the end in a doubly linked list: It involves adding a new node after the current
last node, making it the new tail of the list. This operation requires updating the pointers of
both the new node and the previous last node.
25 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
if (head == nullptr) {
newNode->prev = nullptr; // New node's prev is nullptr since
// it will be the only node
head = newNode; // Make the new node the head if list is empty
return;
}
last->next = newNode; // Point the last node's next to the new node
newNode->prev = last; // Point the new node's prev to the last node
}
26 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
c. Insertion at a specific position in a doubly linked list: It involves adding a new node at a
given index, updating the pointers of both the new node and the adjacent nodes to maintain the
doubly linked structure.
if (current == nullptr) {
27 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
if (current->next != nullptr) {
current->next->prev = newNode; // Update next node's prev if
// it exists
}
current->next = newNode; // Link current's next to new node
}
Deletion in a doubly linked list involves removing a specified node and updating the pointers
of the adjacent nodes to maintain the integrity of the list. The deletion can occur at the
beginning, end, or at a specific position.
a. Deletion in a Doubly Linked List at the beginning: It involves removing the first node
(head) of the list and updating the head pointer to point to the next node in the list.
return;
}
}
if (current->next != nullptr) {
current->next->prev = current->prev; // Link next node back
// to the previous node
}
Sorting a doubly linked list at a specific position with bubble sort involves repeatedly
swapping adjacent nodes if they are out of order, working only within a sublist defined by the
specific range. This can be useful when you need to sort only a part of the list rather than the
entire structure.
}
}
8. Concatenation of Doubly Linked Lists:
Concatenation of doubly linked lists involves combining two doubly linked lists into a single
list. This operation requires adjusting the pointers of the last node of the first list and the head
of the second list so that they connect seamlessly.
// Link the last node of the first list to the head of the second list
last->next = head2;
head2->prev = last; // Link the head of the second list back to
// the last node of the first list
}
2.3 Circular Linked List
33 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
A circular linked list is a data structure where the last node connects back to the first, forming
a loop. This structure allows for continuous traversal without any interruptions. Circular linked
lists are especially helpful for tasks like scheduling and managing playlists, thus allowing for
smooth navigation.
In Circular Singly Linked List, each node has just one pointer called the “next” pointer. The
next pointer of last node points back to the first node and this results in forming a circle. In
this type of Linked list, we can only move through the list in one direction.
struct Node {
int data;
Node* next;
Node(int value){
data = value;
next = nullptr;
}
};
1. Creation of a Circular Linked List:
Creation of a circular linked list involves creating a linked list in which the last node points
back to the first node, forming a circle. This structure allows for continuous traversal of the list
without needing to keep track of the end.
Node* createCircularLinkedList(int n) {
if (n <= 0) {
return nullptr; // Return null if the number of nodes is non-
positive
}
Traversal in a circular linked list involves visiting each node in the list starting from a given
node (usually the head) and continuing until you return to the starting node. Because of the
circular structure, it is important to avoid infinite loops by keeping track of when you return to
the starting point.
do {
cout << current->data << " "; // Print current node's data
current = current->next; // Move to the next node
} while (current != head); // Stop when we reach the head again
Finding the length of a circular linked list involves counting the number of nodes in the list.
Since the list is circular, a specific method is used to ensure that the traversal stops after visiting
all nodes without getting stuck in an infinite loop.
do {
count++; // Increment count for each node visited
current = current->next; // Move to the next node
} while (current != head); // Stop when we reach the head again
36 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Searching in a circular linked list involves traversing the list to find a node that contains a
specific value. Since the list is circular, care must be taken to avoid infinite loops during
traversal.
do {
if (current->data == target) {
return current; // Return the node if found
}
current = current->next; // Move to the next node
} while (current != head); // Stop when we reach the head again
Insertion in a circular linked list involves adding a new node to the list at a specified position.
This can be done in three main scenarios: inserting at the beginning, at the end, or at a specific
position in the list.
a. Insertion in a Circular Linked List at the beginning: It involves adding a new node as
the new head of the list. Since the list is circular, the last node should continue to point to the
head, and this needs to be updated to accommodate the new node.
37 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
if (head == nullptr) {
// If the list is empty, make the new node point to itself
head = newNode;
newNode->next = head;
} else {
// Find the last node
Node* last = head;
while (last->next != head) {
last = last->next;
}
if (head == nullptr) {
// If the list is empty, make the new node point to itself
head = newNode;
newNode->next = head;
} else {
// Find the last node
Node* last = head;
while (last->next != head) {
last = last->next;
}
if (position == 0) {
// Insert at the beginning
if (head == nullptr) {
head = newNode;
newNode->next = head; // Point to itself
} else {
// Find the last node to update its next pointer
Node* last = head;
while (last->next != head) {
last = last->next;
}
Deletion involves removing a node from the linked list. The main difference is that we need to
ensure the list remains circular after the deletion. We can delete a node in a circular linked list
in three ways:
a. Deletion in a Circular Linked List at the beginning: It involves removing the head node
while maintaining the circular structure. The next pointer of the last node needs to be updated
to point to the new head after deletion.
It involves removing the last node in the list while maintaining the circular structure. The next
pointer of the second-to-last node must be updated to point back to the head after deletion.
It involves removing a node at the given position while maintaining the circular structure of
the list. After deletion, the next pointers must be adjusted to keep the list circular.
// If position is valid
if (temp->next != head || position == 0) {
prev->next = temp->next; // Bypass the node to delete
delete temp;
}
}
}
7. Sorting in a Circular Linked List:
Sorting in a circular linked list involves arranging the nodes in ascending or descending order
based on their values. Since a circular linked list does not have a natural "end," care must be
taken during the sorting process to ensure that the entire list is sorted without causing an infinite
loop.
One common method to sort a circular linked list is to use the Bubble Sort algorithm. Bubble
Sort repeatedly steps through the list, compares adjacent nodes, and swaps them if they are in
the wrong order. This process is repeated until no more swaps are needed.
bool swapped;
Node* current;
Node* nextNode = nullptr;
// Bubble Sort
do {
swapped = false;
current = head;
do {
nextNode = current->next; // Get the next node
}
8. Concatenation of Circular Linked Lists:
Concatenation of circular linked lists involves combining two circular linked lists into a
single circular linked list. This process ensures that the resulting list maintains its circular
property.
// Link the last node of the first list to the head of the second list
last1->next = head2;
// Link the last node of the second list to the head of the first list
46 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
last2->next = head1;
In circular doubly linked list, each node has two pointers prev and next, similar to doubly
linked list. The prev pointer points to the previous node and the next points to the next node.
Here, in addition to the last node storing the address of the first node, the first node will also
store the address of the last node.
struct Node {
int data;
Node* next;
Node(int value){
data = value;
next = nullptr;
}
};
2.5 Advantages of Doubly Linked List Over Singly Linked List
1. Bidirectional Traversal:
o Doubly linked lists allow traversal in both forward and backward directions,
thanks to each node containing a pointer to both the next and previous nodes.
47 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
o In a doubly linked list, deleting a specific node is easier and more efficient
because each node has a pointer to its previous node.
o Unlike a singly linked list, where we must traverse from the head to find the
previous node to update pointers, in a doubly linked list, we can directly access
the previous node, making deletion quicker.
o Inserting or deleting a node at the end of a doubly linked list is more efficient
since we can traverse from either end and update the tail node if needed.
o In a singly linked list, we need to start from the head and go through the list to
find the last node, especially when inserting or deleting at the tail.
o Doubly linked lists are commonly used in the implementation of complex data
structures, such as deques (double-ended queues) and certain tree structures,
where forward and backward navigation is beneficial.
o In contrast, reversing a singly linked list requires reassigning next pointers and
is more cumbersome.
Insertion:
o Space: O(1)
48 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Deletion:
o Space: O(1)
Traversal:
o Time: O(n)
o Space: O(1)
Finding Length:
Creation:
Searching:
Concatenation:
o Time:
O(m + n) for singly and doubly linked lists (because you need to traverse
to the end of the first list to connect it).
O(1) for circular and doubly circular linked lists (since you can link the
tail of one list directly to the head of the other).
3. Polynomial Manipulations
3.1 Polynomial Addition
Polynomial addition using linked lists involves representing each polynomial as a linked list,
where each node stores the coefficient and exponent of a term. By aligning terms of the same
degree, we can efficiently add polynomials.
o Each node contains two parts: coefficient and exponent, along with a next
pointer to the next term.
o Start at the head of both linked lists and compare the exponents.
o If the exponents are equal, add the coefficients and create a new node with the
summed coefficient and same exponent.
o If the exponent in the first list is greater, add the term from the first list to the
result list.
o If the exponent in the second list is greater, add the term from the second list to
the result list.
50 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
o If one polynomial has remaining terms after the other list is exhausted, append
these remaining terms to the result list.
4. Resulting Polynomial:
o The resulting linked list represents the sum of the two polynomials, where terms
are ordered by descending exponent.
Q. Write Pseudo C++ code for addition of two polynomials using singly linked list.
struct Node {
int coeff; // Coefficient of the term
int pow; // Exponent of the term
Node* next; // Pointer to the next term
struct Node {
int coeff; // Coefficient of the term
int pow; // Exponent of the term
Node* next; // Pointer to the next term
Node* prev; // Pointer to the previous term
}
}
A Generalized Linked List L, is defined as a finite sequence of n >= 0 elements, l1, l2, l3, l4, …,
ln, such that li are either item or the list of items. Thus L = (l1, l2, l3, l4, …, ln) where n is total
number of nodes in the list.
To represent a list of items there are certain assumptions about the node structure.
Down pointer is the address of node which is down of the current node
Next pointer is the address of node which is attached as the next node
Generalized linked lists are used because although the efficiency of polynomial operations
using linked list is good but still, the disadvantage is that the linked list is unable to use multiple
variable polynomial equation efficiently. It helps us to represent multi-variable polynomial
along with the list of elements.
When first field is 0, it indicates that the second field is variable. If first field is 1 means the
second field is a down pointer, means some list is starting.
class GeneralizedListNode{
private:
GeneralizedListNode *next;
bool tag;
union{
char data;
GeneralizedListNode *down;
};
};
3.3 Representation of Polynomial using GLL
In the above example the head node is of variable x. The temp node shows the first field as 2
means coefficient and exponent are present.
Since temp node is attached to head node and head node is having variable x, temp node having
coefficient = 9 and exponent = 5. The above two nodes can be read as 9x5.
Similarly, in the above figure, the node temp1 can be read as x4.
temp2 = y
temp3 = coefficient = 7
exponent = 1
temp2 is attached to temp3 this means 7y1 and temp2 is also attached to temp1 means
temp1 × temp2