Data Structures: Abstract Data Types Explained
Data Structures: Abstract Data Types Explained
UNIT 1
Introduction to Data Structures and Algorithms
Data Structures is about how data can be stored in different structures.
When we talk about algorithms in Computer Science, the step-by-step instructions are
written in a programming language, and instead of food ingredients, an algorithm uses
data structures.
Abstract data type in data structure type allows reusability of a code i.e. it makes it
convenient for a programmer to write a shorter code.
An Abstract Data Type in data structure is a kind of a data type whose behavior is
defined with the help of some attributes and some functions. Generally, we write
these attributes and functions inside a class or a structure so that we can use an object
of the class to use that particular abstract data type.
Examples of Abstract Data Type in Data Structure are list, stack, queue etc.
List ADT
Lists are linear data structures in which data is stored in a non - continuous fashion.
List consists of data storage boxes called 'nodes'. These nodes are linked to each other
i.e. each node consists of the address of some other block. In this way, all the nodes
are connected to each other through these links. You can read more about lists in this
article: Linked List Data Structure.
Stack ADT
Stack is a linear data structure in which data can be only accessed from its top. It only
has two operations i.e. push (used to insert data to the stack top) and pop (used to
remove data from the stack top).
Queue ADT
Queue is a linear data structure in which data can be accessed from both of its ends i.e.
front and rear. It only has two operations i.e. push (used to insert data to the rear of
the queue) and pop (used to remove data from the front of the queue).
You can read more about queues in this article: Queue Data Structure.
1. Abstract data type in data structure makes it very easy for us to use the
complex data structures along with their complex functions. It follows an
object - oriented programming paradigm.
2. By using abstract data types, we can also customize any data structure
depending on how we plan to use that particular data structure.
3. Abstract data type in data structure follows the concept of reusability of a code.
This means that we don't have to write a particular piece of code again and
again. We can just create an abstract data type and we can use it by simply
calling the functions present in it.
An abstract data type in data structure is a kind of a data type whose behavior is
defined with the help of some attributes and some functions.
An abstract data type in data structure can be that of a list data structure, stack
data structure and a queue data structure. Several valid operations on a particular
data structure are defined in the abstract data type.
Abstract data types in data structure follow an object - oriented paradigm.
List ADT
• In memory we can store the list in two ways, one way is we can store the elements
in sequential memory locations. This is known as arrays. And the other way is, we can
use pointers or links to associate the elements sequentially. This known as Linked
Lists.
• In ADT the implementation details are hidden. Hence the ADT will be-
AbstractDataType List
3. Searching: Based on the value of the key element the desired element can be
searched.
4. Modification: The value of the specific element can be changed without changing
its location.
• The linked list that can be represented by arrays is called static linked list.
• In this section we will discuss in detail how exactly the list can be represented using
arrays.
• Basically list is a collection of elements.
• To show the list using arrays we will have 'data' and 'link' fields in the array.
struct node
int data;
int next;
} a[10];
1. Creation of list.
2. Display of list.
1. Creation of list
The list can be created by placing the node of list in an array. Each node consists of
two fields 'data' and 'next' pointer. We need to give the address of starting node which
is to be placed in the array.
While creating the list we have to first enter the location in an array where the first
node is placed and then input the pair: Data and next.
2. Display
After creation we can display the list. Normally when the list is displayed simply the
data fields are to be displayed.
3. Deletion of a node
While deleting a node from the list we simply manipulate the next pointer of previous
node in the list. And the data field of the node to be deleted is initialized to - 1.
In a singly linked list, each node consists of two parts: data and a pointer to the next
node. This structure allows nodes to be dynamically linked together, forming a
chain-like sequence.
int data;
Node* next;
Node(int data)
{
this->data = data;
this->next = nullptr;
}
};
In this example, the Node class contains an integer data field (data) to store the
information and a pointer to another Node (next) to establish the link to the next
node in the list.
Searching in a Singly Linked List refers to the process of looking for a specific
element or value within the elements of the linked list.
Step-by-step approach:
1. Start from the head of the linked list.
2. Check each node’s data:
If it matches the target value, return true (element found).
Otherwise, move to the next node.
3. Repeat until the end (NULL) is reached.
4. If no match is found, return false.
Finding the length of a Singly Linked List means counting the total number of
nodes.
Step-by-step approach:
1. Initialize a counter (length = 0).
2. Start from the head, assign it to current.
3. Traverse the list:
Increment length for each node.
Move to the next node (current = current->next).
4. Return the final length when current becomes NULL.
Insertion is a fundamental operation in linked lists that involves adding a new node
to the list. There are several scenarios for insertion:
a. Insertion at the Beginning of Singly Linked List: Insertion at the beginning
involves adding a new node before the current head, making it the new head.
Insert a Node at the Front/Beginning of Linked List
Step-by-step approach:
Create a new node with the given value.
Set the next pointer of the new node to the current head.
Move the head to point to the new node.
Return the new head of the linked list.
b. Insertion at the End of Singly Linked List: To insert a node at the end of the
list, traverse the list until the last node is reached, and then link the new node to the
current last node
Step-by-step approach:
Create a new node and assign it a value.
If inserting at the beginning (position = 1):
o Point the new node’s next to the current head.
o Update the head to the new node.
o Return (Insertion done).
Otherwise, traverse the list:
o Start from the head and move to the (position - 1)ᵗʰ node (just before
the desired position).
o If the position is beyond the list length, return an error or append
at the end.
Insert the new node:
o Point the new node’s next to the next node of the current position.
o Update the previous node’s next to the new node.
Return the updated list.
Deletion involves removing a node from the linked list. Similar to insertion, there
are different scenarios for deletion:
a. Deletion at the Beginning of Singly Linked List: To delete the first node,
update the head to point to the second node in the list.
Updating in a Singly Linked List means modifying the value of a node at a given
position.
Step-by-step approach:
Start from the head of the list.
Traverse to the required position (move current node to position).
Check if the position is valid:
o If the position is out of bounds, return an error.
Update the node’s data with the new value.
Return the modified list.
A circular linked list is a special type of linked list where all the nodes are connected
to form a circle. Unlike a regular linked list, which ends with a node pointing to
NULL, the last node in a circular linked list points back to the first node. This means
that you can keep traversing the list without ever reaching a NULL value.
We can create a circular linked list from both singly linked lists and doubly linked
lists. So, circular linked lists are basically of two types:
In Circular Singly Linked List, each node has just one pointer called the "next"
pointer. The next pointer of the 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.
Representation of Circular Singly Linked List
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.
Here’s an example of creating a circular linked list with three nodes (2, 3, 4):
In the above code, we have created three nodes first, second, and last having values 2,
3, and 4 respectively.
After creating three nodes, we have connected these node in a series.
Connect the first node "first" to "second" node by storing the address of
"second" node into first's next
Connect the second node "second" to "third" node by storing the address of
"third" node into second's next
After connecting all the nodes, we reach the key characteristic of a circular
linked list: linking the last node back to the first node. Therefore, we store the
address of the "first" node in the "last" node.
We can do some operations on the circular linked list similar to the singly and doubly
linked list which are:
1. Insertion
Insertion at the empty list
Insertion at the beginning
Insertion at the end
Insertion at the given position
2. Deletion
Delete the first node
Delete the last node
Delete the node from any position
3. Searching
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:
1. Delete the first node in circular linked list
To delete the first node of a circular linked list, we check if the list is
empty or has only one node. If so, we handle those cases by deleting
the node and updating the last pointer. For multiple nodes, we update
the last node’s next pointer to skip the head and free the head node,
returning the updated last pointer.
In circular linked list, the last node points to the first node. There are no null
references, making traversal easier and reducing the chances of encountering
null pointer exceptions.
We can traverse the list from any node and return to it without needing to
restart from the head, which is useful in applications requiring a circular
iteration.
Circular linked lists can easily implement circular queues, where the last
element connects back to the first, allowing for efficient resource management.
In a circular linked list, each node has a reference to the next node in the
sequence. Although it doesn't have a direct reference to the previous node like a
doubly linked list, we can still find the previous node by traversing the list.
Circular linked lists are more complex to implement than singly linked lists.
Traversing a circular linked list without a clear stopping condition can lead
to infinite loops if not handled carefully.
Debugging can be more challenging due to the circular nature, as traditional
methods of traversing linked lists may not apply.
In a data structure, a doubly linked list is represented using nodes that have three
fields:
1. Data
2. A pointer to the next node (next)
3. A pointer to the previous node (prev)
Node Definition
struct Node {
// To store the Value or data.
int data;
Traversal in a Doubly Linked List involves visiting each node, processing its data,
and moving to the next or previous node using the forward (next) and backward (prev)
pointers.
Step-by-Step Approach for Traversal:
1. Start from the head of the list.
2. Traverse forward:
Visit the current node and process its data (e.g., print it).
Move to the next node using current = current->next.
Repeat the process until the end of the list (current == NULL).
3. Optionally, traverse backward:
Start from the tail (last node).
Visit the current node and process its data.
Move to the previous node using current = current->prev.
Repeat the process until the beginning of the list (current == NULL).
Traversal is useful for displaying or processing all nodes in a doubly linked list.
A Doubly Linked List (DLL) is a type of linked list where each node has two pointers:
1. One pointing to the next node in the sequence.
2. One pointing to the previous node in the sequence.
To find the length of a doubly linked list, we need to traverse the list while counting
the nodes.
Step-by-Step Approach for finding length:
1. Initialize a counter: Start with a counter variable (count = 0).
2. Set a pointer to the head node: Use a pointer (current) and initialize it to the
head of the linked list.
3. Traverse the list:
While the pointer (current) is not NULL, increment the count by 1.
Move to the next node (current = [Link]).
4. Stop at the end of the list: When the pointer reaches NULL, stop the loop.
5. Return the count: The final value of count gives the length of the doubly
linked list.
Insertion in a Doubly Linked List (DLL) involves adding a new node at a specific
position while maintaining the connections between nodes. Since each node contains
a pointer to both the previous and next node, insertion requires adjusting these
pointers carefully.
There are three primary types of insertion in a DLL:
1. Insertion at the Beginning
1. Create a new node with the given data.
2. Set the next pointer of the new node to the current head.
3. If the list is not empty, update the prev pointer of the current head to point to
the new node.
4. Update the head of the list to the new node.
2. Insertion at the End
1. Create a new node with the given data.
2. If the list is empty, set the new node as the head.
3. Traverse the list until the last node is found.
4. Set the next pointer of the last node to the new node.
5. Set the prev pointer of the new node to the last node.
3. Insertion at a Specific Position
1. Create a new node with the given data.
2. If inserting at the beginning, follow the steps for insertion at the start.
3. Traverse the list to find the node after which insertion is needed.
4. Set the next pointer of the new node to the next node of the current position.
5. Set the prev pointer of the new node to the current node.
6. Update the prev pointer of the next node to point to the new node (if it exists).
7. Update the next pointer of the previous node to point to the new node.
Efficient traversal in both directions: Doubly linked lists allow for efficient
traversal of the list in both directions, making it suitable for applications where
frequent insertions and deletions are required.
Easy insertion and deletion of nodes: The presence of pointers to both the
previous and next nodes makes it easy to insert or delete nodes from the list,
without having to traverse the entire list.
Can be used to implement a stack or queue: Doubly linked lists can be used
to implement both stacks and queues, which are common data structures used in
programming.
More complex than singly linked lists: Doubly linked lists are more complex
than singly linked lists, as they require additional pointers for each node.
More memory overhead: Doubly linked lists require more memory overhead
than singly linked lists, as each node stores two pointers instead of one.
Dynamic Memory Allocation is one of the key application of linked list, where
memory is assigned at run-time instead of compile time. Unlike arrays, which actually
requires a fixed size, a linked list dynamically allocates and deallocates memory as
needed, preventing wastage.
One of the most useful and important applications of linked list is making stacks and
queues, which help store and manage data. A stack is like a stack of plates, where you
can only add or remove the top element. This follows the LIFO structure (Last In,
First Out), meaning the last thing you put in is the first thing you take out. A queue is
like a ticket counter line, where the first person in line goes first, following FIFO (just
opposite of using stacks).
In applications of linked list data structure ,stacks and queues are better than arrays
because linked lists don’t need a fixed size. With arrays, you have to decide the size
before using them, and making them bigger is hard. But with a linked list, you can
add or remove elements whenever you want, making it more flexible.
Undo / Redo functionality is the coolest applications of linked list data structure.
When you type something and press undo, the editor or user goes back to the previous
state. If you press the Redo option, it moves forward again. A doubly linked list data
structure executes this process that lets you move backward and forward easily.
Music and video playlists are another important application of linked list. When you
play songs or videos in order, you can go to the next or previous one easily. This
works using a doubly linked list, where each song or video is a node connected to the
one before and after it.
Since applications of linked list data structure allows smooth movement in directions.
You can shuffle, repeat or skip tracks without issues. This makes the playlist work
efficiently without needing a fixed size. Application of linked list helps media players
manage songs and videos easily.
One important application of linked list is in hash tables, especially for handling
collisions. When two pieces of data end up in the same spot (called a collision), a
linked list helps by storing both items in a list.
When we talk about the structure of a hash table then each location is like a box, and
when multiple items are stored in the same box we need linked list data structure to
keep them in order.
One useful application of linked list is in organizing file system directory structures.
Each folder or file can be a node in a linked list, with links connecting files and
subfolders. This helps navigate and manage files efficiently, making it easy to add,
remove, or search for files.
There are various linked list operations that allow us to perform different actions on
linked lists. For example, the insertion operation adds a new element to the linked list.
Here's a list of basic linked list operations that we will cover in this article.
struct node {
int data;
};
When temp is NULL, we know that we have reached the end of the linked list so we
get out of the while loop.
printf("%d --->",temp->data);
temp = temp->next;
newNode->data = 4;
newNode->next = head;
head = newNode;
newNode->data = 4;
newNode->next = NULL;
temp = temp->next;
temp->next = newNode;
if(temp->next != NULL) {
temp = temp->next;
newNode->next = temp->next;
temp->next = newNode;
head = head->next;
temp = temp->next;
temp->next = NULL;
if(temp->next!=NULL) {
temp = temp->next;
temp->next = temp->next->next;
The goal here is merge two linked lists that are already sorted.
For example: if L1 = 1 -> 3 -> 10 and L2 = 5 -> 6 -> 9 then your program should
output the linked list 1 -> 3 -> 5 -> 6 -> 9 -> 10.
Algorithm
The algorithm for this question is quite simple since the two linked lists are already
sorted. We create a new linked list and loop through both lists appending the smaller
nodes.
(1) Create a new head pointer to an empty linked list. (2) Check the first value of both
linked lists. (3) Whichever node from L1 or L2 is smaller, append it to the new list
and move the pointer to the next node. (4) Continue this process until you reach the
end of a linked list.
Example
L1 = 1 -> 3 -> 10 L2 = 5 -> 6 -> 9 L3 = null Compare the first two nodes in both
linked lists: (1, 5), 1 is smaller so add it to the new linked list and move the pointer in
L1. L1 = 3 -> 10 L2 = 5 -> 6 -> 9 L3 = 1 Compare the first two nodes in both linked
lists: (3, 5), 3 is smaller so add it to the new linked list and move the pointer in L1. L1
= 10 L2 = 5 -> 6 -> 9 L3 = 1 -> 3 Compare the first two nodes in both linked lists: (10,
5), 5 is smaller so add it to the new linked list and move the pointer in L2. L1 = 10 L2
= 6 -> 9 L3 = 1 -> 3 -> 5 Compare the first two nodes in both linked lists: (10, 6), 6 is
smaller so add it to the new linked list and move the pointer in L2. L1 = 10 L2 = 9 L3
= 1 -> 3 -> 5 -> 6 Compare the first two nodes in both linked lists: (10, 9), 9 is smaller
so add it to the new linked list and move the pointer in L2. L1 = 10 L2 = null L3 = 1 -
> 3 -> 5 -> 6 -> 9 Because L2 points to null, simply append the rest of the nodes from
L1 and we have our merged linked list. L3 = 1 -> 3 -> 5 -> 6 -> 9 -> 10
Code
function Node(data, next) {
[Link] = data;
[Link] = next;
}
function merge(L1, L2) {
}
// create first linked list: 1 -> 3 -> 10var n3 = new Node(10, null);var n2 = new
Node(3, n3);var n1 = new Node(1, n2);var L1 = n1;
// create second linked list: 5 -> 6 -> 9var n6 = new Node(9, null);var n5 = new
Node(6, n6);var n4 = new Node(5, n5);var L2 = n4;
merge(L1, L2);