M.Tech JNTUK ADS UNIT-1
M.Tech JNTUK ADS UNIT-1
A Linked List is a linear data structure which looks like a chain of nodes, where each node is
a different element. Unlike Arrays, Linked List elements are not stored at a contiguous
location.
It is basically chains of nodes, each node contains information such as data and a pointer to
the next node in the chain. In the linked list there is a head pointer, which points to the first
element of the linked list, and if the list is empty then it simply points to null or nothing.
1. Singly-linked list
Traversal of items can be done in the forward direction only due to the linking of every node to
its next node.
// 1. allocate node
Node* new_node = new Node();
// Used in step 5
Node* last = *head_ref;
Complexity Analysis:
Time complexity: O(N), where N is the number of nodes in the linked list. Since there is a
loop from head to end, the function does O(n) work.
This method can also be optimized to work in O(1) by keeping an extra pointer
to the tail of the linked list/
Auxiliary Space: O(1)
#include <bits/stdc++.h>
using namespace std;
/* Driver code*/
int main()
{
/* Start with the empty list */
Node* head = NULL;
int x = 21;
// Function call
search(head, x) ? cout << "Yes" : cout << "No";
return 0;
}
Time Complexity: O(N), Where N is the number of nodes in the LinkedList
Auxiliary Space: O(1)
Keeps track of pointer before node to delete and pointer to node to delete
temp = head;
prev = head;
for(int i = 0; i < position; i++)
{
if(i == 0 && position == 1)
head = head->next;
free(temp)
else
{
if (i == position - 1 && temp)
{
prev->next = temp->next;
free(temp);
}
else
{
prev = temp;
if(prev == NULL) // position was greater than number of nodes in the list
break;
temp = temp->next;
}
}
}
Advantages Of Linked List :
Dynamic data structure: A linked list is a dynamic arrangement so it can grow and shrink
at runtime by allocating and deallocating memory . So there is no need to give the initial
size of the linked list.
No memory wastage: In the Linked list, efficient memory utilization can be achieved since
the size of the linked list increase or decrease at run time so there is no memory wastage
and there is no need to pre-allocate the memory.
Implementation: Linear data structures like stacks and queues are often easily
implemented using a linked list.
Insertion and Deletion Operations: Insertion and deletion operations are quite easier in
the linked list. There is no need to shift elements after the insertion or deletion of an
element only the address present in the next pointer needs to be updated.
Flexible: This is because the elements in Linked List are not stored in contiguous memory
locations unlike the array.
Efficient for large data: When working with large datasets linked lists play a crucial role
as it can grow and shrink dynamically.
Scalability: Contains the ability to add or remove elements at any position.
Disadvantages Of Linked List :
Memory usage: More memory is required in the linked list as compared to an array.
Because in a linked list, a pointer is also required to store the address of the next element
and it requires extra memory for itself.
Traversal: In a Linked list traversal is more time-consuming as compared to an array.
Direct access to an element is not possible in a linked list as in an array by index. For
example, for accessing a node at position n, one has to traverse all the nodes before it.
Reverse Traversing: In a singly linked list reverse traversing is not possible, but in the
case of a doubly-linked list , it can be possible as it contains a pointer to the previously
connected nodes with each node. For performing this extra memory is required for the back
pointer hence, there is a wastage of memory.
Random Access: Random access is not possible in a linked list due to its dynamic memory
allocation.
Lower efficiency at times: For certain operations, such as searching for an element or
iterating through the list, can be slower in a linked list.
Complex implementation: The linked list implementation is more complex when
compared to array. It requires a complex programming understanding.
Difficult to share data: This is because it is not possible to directly access the memory
address of an element in a linked list.
Not suited for small dataset: Cannot provide any significant benefits on small dataset
compare to that of an array.
Doubly Linked List is a variation of Linked list in which navigation is possible in both ways,
forward as well as backward easily as compared to Single Linked List. Following are the
important terms to understand the concept of doubly linked list.
Link − Each link of a linked list can store a data called an element.
Next − Each link of a linked list contains a link to the next link called Next.
Prev − Each link of a linked list contains a link to the previous link called Prev.
Linked List − A Linked List contains the connection link to the first link called First and
to the last link called Last.
As per the above illustration, following are the important points to be considered.
Doubly Linked List contains a link element called first and last.
Each link carries a data field(s) and a link field called next.
Each link is linked with its next link using its next link.
Each link is linked with its previous link using its previous link.
The last link carries a link as null to mark the end of the list.
Basic Operations in Doubly Linked List
In this operation, we create a new node with three compartments, one containing the data, the
others containing the address of its previous and next nodes in the list. This new node is inserted
at the beginning of the list.
Algorithm
1. START
2. Create a new node with three variables: prev, data, next.
3. Store the new data in the data variable
4. If the list is empty, make the new node as head.
5. Otherwise, link the address of the existing first node to the
next variable of the new node, and assign null to the prev variable.
6. Point the head to the new node.
7. END
In this insertion operation, the new input node is added at the end of the doubly linked list; if the
list is not empty. The head will be pointed to the new node, if the list is empty.
Algorithm
1. START
2. If the list is empty, add the node to the list and point
the head to it.
3. If the list is not empty, find the last node of the list.
4. Create a link between the last node in the list and the
new node.
5. The new node will point to NULL as it is the new last node.
6. END
Algorithm
1. START
2. Check the status of the doubly linked list
3. If the list is empty, deletion is not possible
4. If the list is not empty, the head pointer is
shifted to the next node.
4. END
Advantages Of DLL:
Reversing the doubly linked list is very easy.
It can allocate or reallocate memory easily during its execution.
As with a singly linked list, it is the easiest data structure to implement.
The traversal of this doubly linked list is bidirectional which is not possible in a singly
linked list.
Deletion of nodes is easy as compared to a Singly Linked List . A singly linked list deletion
requires a pointer to the node and previous node to be deleted but in the doubly linked list,
it only required the pointer which is to be deleted.’
Doubly linked lists have a low overhead compared to other data structures such as arrays.
Implementing graph algorithms.
Disadvantages Of DLL:
It uses extra memory when compared to the array and singly linked list.
Since elements in memory are stored randomly, therefore the elements are accessed
sequentially no direct access is allowed.
Traversing a doubly linked list can be slower than traversing a singly linked list.
Implementing and maintaining doubly linked lists can be more complex than singly linked
lists.
3) Insertion in between the nodes: To insert a node in between the two nodes, follow these
steps:
Create a node, say T.
Search for the node after which T needs to be inserted, say that node is P.
Make T -> next = P -> next;
P -> next = T.
1) Delete the node only if it is the only node in the circular linked list:
Free the node’s memory
The last value should be NULL A node always points to another node, so NULL
assignment is not necessary.
Any node can be set as the starting point.
Nodes are traversed quickly from the first to the last.
2) Deletion of the last node:
Locate the node before the last node (let it be temp)
Keep the address of the node next to the last node in temp
Delete the last memory
Put temp at the end
3) Delete any node from the circular linked list: We will be given a node and our task is to
delete that node from the circular linked list.
Algorithm:
Case 1: List is empty.
If the list is empty we will simply return.
Case 2:List is not empty
If the list is not empty then we define two pointers curr and prev and initialize the
pointer curr with the head node.
Traverse the list using curr to find the node to be deleted and before moving to curr to the
next node, every time set prev = curr.
If the node is found, check if it is the only node in the list. If yes, set head = NULL and
free(curr).
If the list has more than one node, check if it is the first node of the list. Condition to check
this( curr == head). If yes, then move prev until it reaches the last node. After prev reaches
the last node, set head = head -> next and prev -> next = head. Delete curr.
If curr is not the first node, we check if it is the last node in the list. Condition to check this
is (curr -> next == head).
If curr is the last node. Set prev -> next = head and delete the node curr by free(curr).
If the node to be deleted is neither the first node nor the last node, then set prev -> next =
curr -> next and delete curr.
If the node is not present in the list return head and don’t do anything.
Advantages of Circular Linked Lists:
Any node can be a starting point. We can traverse the whole list by starting from any point.
We just need to stop when the first visited node is visited again.
Useful for implementation of a queue. Unlike this implementation, we don’t need to
maintain two pointers for front and rear if we use a circular linked list. We can maintain a
pointer to the last inserted node and the front can always be obtained as next of last.
Circular lists are useful in applications to repeatedly go around the list. For example, when
multiple applications are running on a PC, it is common for the operating system to put the
running applications on a list and then cycle through them, giving each of them a slice of
time to execute, and then making them wait while the CPU is given to another application.
It is convenient for the operating system to use a circular list so that when it reaches the end
of the list it can cycle around to the front of the list.
Circular Doubly Linked Lists are used for the implementation of advanced data structures
like the Fibonacci Heap.
Implementing a circular linked list can be relatively easy compared to other more complex
data structures like trees or graphs.
Disadvantages of circular linked list:
Compared to singly linked lists, circular lists are more complex.
Reversing a circular list is more complicated than singly or doubly reversing a circular list.
It is possible for the code to go into an infinite loop if it is not handled carefully.
It is harder to find the end of the list and control the loop.
Although circular linked lists can be efficient in certain applications, their performance can
be slower than other data structures in certain cases, such as when the list needs to be
sorted or searched.
Circular linked lists don’t provide direct access to individual nodes
Applications of circular linked lists:
Multiplayer games use this to give each player a chance to play.
A circular linked list can be used to organize multiple running applications on an operating
system. These applications are iterated over by the OS.
Circular linked lists can be used in resource allocation problems.
Circular linked lists are commonly used to implement circular buffers,
Circular linked lists can be used in simulation and gaming.
To implement a stack using the singly linked list concept, all the singly linked list operations
should be performed based on Stack operations LIFO(last in first out) and with the help of that
knowledge, we are going to implement a stack using a singly linked list.
So we need to follow a simple rule in the implementation of a stack which is last in first
out and all the operations can be performed with the help of a top variable. Let us learn how to
perform Pop, Push, Peek, and Display operations
Stack Operations:
push(): Insert a new element into the stack i.e just insert a new element at the beginning of
the linked list.
pop(): Return the top element of the Stack i.e simply delete the first element from the
linked list.
peek(): Return the top element.
display(): Print all elements in Stack.
Push Operation:
Initialise a node
Update the value of that node by data i.e. node->data = data
Now link this node to the top of the linked list
And update top pointer to the current node
Pop Operation:
First Check whether there is any node present in the linked list or not, if not then return
Otherwise make pointer let say temp to the top node and move forward the top node by 1
step
Now free this temp node
Peek Operation:
Check if there is any node present or not, if not then return.
Otherwise return the value of top node of the linked list
Display Operation:
Take a temp node and initialize it with top pointer
Now start traversing temp till it encounters NULL
Simultaneously print the value of the temp node
#include <bits/stdc++.h>
using namespace std;
// Constructor
Node(int n)
{
this->data = n;
this->link = NULL;
}
};
class Stack {
Node* top;
public:
Stack() { top = NULL; }
// Function to remove
// a key from given queue q
void pop()
{
Node* temp;
// Driven Program
int main()
{
// Creating a stack
Stack s;
return 0;
}
Time Complexity: O(1), for all push(), pop(), and peek(), as we are not performing any kind
of traversal over the list. We perform all the operations through the current pointer only.
Auxiliary Space: O(N), where N is the size of the stack
Dynamic memory allocation: The size of the stack can be increased or decreased dynamically
by adding or removing nodes from the linked list, without the need to allocate a fixed amount
of memory for the stack upfront.
Efficient memory usage: Since nodes in a singly linked list only have a next pointer and not a
prev pointer, they use less memory than nodes in a doubly linked list.
Easy implementation: Implementing a stack using a singly linked list is straightforward and
can be done using just a few lines of code.
Versatile: Singly linked lists can be used to implement other data structures such as queues,
linked lists, and trees.
In summary, implementing a stack using a singly linked list is a simple and efficient way to
create a dynamic stack data structure in Python.
we maintain two pointers, front, and rear. The front points to the first item of the queue
and rear points to the last item.
enQueue(): This operation adds a new node after the rear and moves the rear to the next
node.
deQueue(): This operation removes the front node and moves the front to the next node.
Follow the below steps to solve the problem:
Create a class QNode with data members integer data and QNode* next
A parameterized constructor that takes an integer x value as a parameter and sets
data equal to x and next as NULL
Create a class Queue with data members QNode front and rear
Enqueue Operation with parameter x:
Initialize QNode* temp with data = x
If the rear is set to NULL then set the front and rear to temp and return(Base
Case)
Else set rear next to temp and then move rear to temp
Dequeue Operation:
If the front is set to NULL return(Base Case)
Initialize QNode temp with front and set front to its next
If the front is equal to NULL then set the rear to NULL
Delete temp from the memory
#include <bits/stdc++.h>
using namespace std;
struct QNode {
int data;
QNode* next;
QNode(int d)
{
data = d;
next = NULL;
}
};
struct Queue {
QNode *front, *rear;
Queue() { front = rear = NULL; }
void enQueue(int x)
{
// Function to remove
// a key from given queue q
void deQueue()
{
// If queue is empty, return NULL.
if (front == NULL)
return;
delete (temp);
}
};
// Driver code
int main()
{
Queue q;
q.enQueue(10);
q.enQueue(20);
q.deQueue();
q.deQueue();
q.enQueue(30);
q.enQueue(40);
q.enQueue(50);
q.deQueue();
cout << "Queue Front : " << ((q.front != NULL) ? (q.front)->data : -1)<< endl;
cout << "Queue Rear : " << ((q.rear != NULL) ? (q.rear)->data : -1);
}
Time Complexity: O(1), The time complexity of both operations enqueue() and dequeue() is
O(1) as it only changes a few pointers in both operations
Auxiliary Space: O(1), The auxiliary Space of both operations enqueue() and dequeue() is
O(1) as constant extra space is required.
Advantages of Queue:
A large amount of data can be managed efficiently with ease.
Operations such as insertion and deletion can be performed with ease as it follows the first
in first out rule.
Queues are useful when a particular service is used by multiple consumers.
Queues are fast in speed for data inter-process communication.
Queues can be used in the implementation of other data structures.
Disadvantages of Queue:
The operations such as insertion and deletion of elements from the middle are time
consuming.
Limited Space.
In a classical queue, a new element can only be inserted when the existing elements are
deleted from the queue.
Searching an element takes O(N) time.
Maximum size of a queue must be defined prior.
Advantages of Stack:
Easy implementation: Stack data structure is easy to implement using arrays or linked
lists, and its operations are simple to understand and implement.
Efficient memory utilization: Stack uses a contiguous block of memory, making it more
efficient in memory utilization as compared to other data structures.
Fast access time: Stack data structure provides fast access time for adding and removing
elements as the elements are added and removed from the top of the stack.
Helps in function calls: Stack data structure is used to store function calls and their states,
which helps in the efficient implementation of recursive function calls.
Supports backtracking: Stack data structure supports backtracking algorithms, which are
used in problem-solving to explore all possible solutions by storing the previous states.
Used in Compiler Design: Stack data structure is used in compiler design for parsing and
syntax analysis of programming languages.
Enables undo/redo operations: Stack data structure is used to enable undo and redo
operations in various applications like text editors, graphic design tools, and software
development environments.
Disadvantages of Stack:
Limited capacity: Stack data structure has a limited capacity as it can only hold a fixed
number of elements. If the stack becomes full, adding new elements may result in stack
overflow, leading to the loss of data.
No random access: Stack data structure does not allow for random access to its elements,
and it only allows for adding and removing elements from the top of the stack. To access
an element in the middle of the stack, all the elements above it must be removed.
Memory management: Stack data structure uses a contiguous block of memory, which
can result in memory fragmentation if elements are added and removed frequently.
Not suitable for certain applications: Stack data structure is not suitable for applications
that require accessing elements in the middle of the stack, like searching or sorting
algorithms.
Stack overflow and underflow: Stack data structure can result in stack overflow if too
many elements are pushed onto the stack, and it can result in stack underflow if too many
elements are popped from the stack.
Recursive function calls limitations: While stack data structure supports recursive
function calls, too many recursive function calls can lead to stack overflow, resulting in the
termination of the program.