Linked List Exclusive Notes 1654248048
Linked List Exclusive Notes 1654248048
TOPICS COVERED-
▪ Linked List Introduction
▪ Insertion in Singly Linked Lists
▪ Deletion in Singly Linked Lists
▪ Doubly Linked Lists
▪ XOR Linked Lists
▪ Circular Linked Lists
▪ Sample Problems on Linked List
• Data: This part stores the data value of the node. That is the information to
be stored at the current node.
• Next Pointer: This is the pointer variable or any other variable which stores
the address of the next node in the memory.
Advantages of Linked Lists over Arrays: Arrays can be used to store linear data of
similar types, but arrays have the following limitations:
1. The size of the arrays is fixed, so we must know the upper limit on the
number of elements in advance. Also, generally, the allocated memory is
equal to the upper limit irrespective of the usage. On the other hand, linked
lists are dynamic and the size of the linked list can be incremented or
decremented during runtime.
2. Inserting a new element in an array of elements is expensive, because a
room has to be created for the new elements, and to create room, existing
elements have to shift.
And if we want to insert a new ID 1005, then to maintain the sorted order,
we have to move all the elements after 1000 (excluding 1000). Deletion is
also expensive with arrays unless some special techniques are used. For
example, to delete 1010 in id[], everything after 1010 has to be moved.
On the other hand, nodes in linked lists can be inserted or deleted without
any shift operation and is efficient than that of arrays.
A linked list is represented by a pointer to the first node of the linked list. The first
node is called the head node of the list. If the linked list is empty, then the value of
the head node is NULL.
1. data
2. Pointer (Or Reference) to the next node
struct Node
{
int data;
struct Node* next;
};
In Java, LinkedList can be represented as a class, and the Node as a separate class.
The LinkedList class contains a reference of the Node class type.
class LinkedList
{
Node head; // head of list
// as null
Node(int d) {data = d;}
}
}
Below is a sample program in both C/C++ and Java to create a simple linked list with 3
Nodes:-
C++ :-
// A simple C/C++ program to introduce | | |
// Program to create a simple linked // Link first node with the second node
/* data has been assigned to data part of second that the linked list is terminated here.
block (block pointed by second). And next We have the linked list ready.
+---+---+ +---+---+ +----+----+ */ the whole list. We can traverse the complete
JAVA-
// A simple Java program to introduce a linked list | | |
class LinkedList | | |
public static void main(String[] args) second.next = third; // Link second node with the
third node
{
/* Now next of second Node refers to third. So all
/* Start with the empty list. */
three
LinkedList llist = new LinkedList();
nodes are linked.
Linked List Traversal: In the previous program, we have created a simple linked list with
three nodes. Let us traverse the created list and print the data of each node. For traversal,
let us write a general purpose function printList() that prints any given list.
C++
// A simple C/C++ program to introduce /* Three blocks have been allocated dynamically.
struct Node | | |
// This function prints contents of linked list Data is random because we haven’t assigned
{ | 1 | o----->| # | # | |#|#|
head = new Node; // Link second node with the third node
/* data has been assigned to data part of second of the third block is made NULL to indicate
block (block pointed by second). And next that the linked list is terminated here.
pointer of the second block points to third We have the linked list ready.
+---+---+ +---+---+ +----+----+ */ the whole list. We can traverse the complete
JAVA-
There can be many different situations that may arise while inserting a node in a
linked list. Three most frequent situations are:
We have seen that a linked list node can be represented using structures and
classes as:
C++ : - JAVA :-
// A linked list node // Linked List Class
{ {
int data;
Node next;
Let us now take a look at each of the above three listed methods of inserting a node
in the linked list:
Let us call the function that adds a new node at the front of the list as push().
The push() function must receive a pointer to the head node because the
function must change the head pointer to point to the new node.
Below is the 4 step process of adding a new node at the front of Linked List
declared at the beginning of this post:
/* 1. allocate node */
new_node->data = new_data;
• Inserting a Node after given Node: Inserting a Node after a given Node is
also similar to the above process. One has to first allocate the new Node and
change the next pointer of the newly created node to the next of the
previous node and the next pointer of the previous node to point to the
newly created node.
Let us call the function that adds a new node after a given node in the list as
insertAfter(). The insertAfter() function must receive a pointer to the previous
node after which the new node is to be inserted.
Below is the complete process of adding a new node after a given node in
the Linked List declared at the beginning of this post:
C++ :- JAVA-
/* This function is in LinkedList class.
/* Given a node prev_node, insert a new node
Inserts a new node after the given prev_node.
after the given prev_node */
This method is defined inside LinkedList class
void insertAfter(struct Node* prev_node, int
new_data) shown above */
}
/* 2. allocate new node */
/* 2. Allocate the Node &
Node* new_node = new Node;
3. Put in the data*/
/* 3. put in the data */
Node new_node = new Node(new_data);
new_node->data = new_data;
Below is the complete 6 step process of adding a new Node at the end of the list:
C++ :- {
*head_ref = new_node;
/* Given a reference (pointer to pointer) to
return;
the head of a list and an int, appends a new
}
node at the end */
last->next = new_node;
struct Node *last = *head_ref; /* used in step 5*/
return;
}
/* 2. put in the data */
new_node->data = new_data;
new_node->next = NULL;
if (*head_ref == NULL)
return;
/* Appends a new node at the end. This method is
}
defined inside LinkedList class declared at the
/* 4. This new node is going to be the last node,
top of the Post */ so
public void append(int new_data) make next of it as null */
{ new_node.next = null;
/* 1. Allocate the Node & /* 5. Else traverse till the last node */
2. Put in the data Node last = head;
3. Set next as null */ while (last.next != null)
Node new_node = new Node(new_data); last = last.next;
/* 4. If the Linked List is empty, then make the /* 6. Change the next of last node */
new node as head */ last.next = new_node;
if (head == null) return;
{ }
The time complexity of this operation is O(N) where N is the number of nodes in the Linked
List as one has to traverse the complete list in order to find the last node.
Like inserting a node in a linked list, there can be many situations when it comes to
deleting a node from a Linked List. Some of the most frequent situations are:
• Given the data value of a node, delete the first occurrence of that data in the
list.
• Given the position of a node, delete the node present at the given position in
the list.
• Given a pointer to the node to be deleted, delete the node.
Let us look at each one of these situations and their solutions with complete
explanations:
• Deleting the first occurrence of a given data value: Deleting a node by its
value can be done by traversing the list till the node just before the node
with the value as the given key. Once the node just before the node to be
deleted is found. Update its next pointer to point to the next of its next node.
That is:
Deleting a node at a given position: If the node to be deleted is the root node, we can
simply delete it by updating the head pointer to point to the next of the root node. To
delete a node present somewhere in between, we must have the pointer to the node
previous to the node to be deleted. So if the position is not zero, run a loop position-1 times
and get the pointer to the previous node and follow the method discussed in the first
situation above to delete the node.
Deleting a node whose pointer is given: In this case, a pointer is given which is
pointing to a particular node in the linked list and the task is to delete that particular
node.
This can be done by following a similar approach as in the above two cases, by first
finding the node just previous to it and updating its next pointer. The time
complexity of this would be again O(N).
However, this particular case can be solved with O(1) time complexity if the pointer
to the node to be deleted is given.
The efficient solution is to copy the data from the next node to the node to be
deleted and delete the next node. Suppose the node to be deleted is node_ptr, it
can be deleted as:
Note: This solution fails if the node to be deleted is the last node of the List.
• Each node contains two pointers, one pointing to the next node and the other
pointing to the previous node.
• The prev of Head node is NULL, as there is no previous node of Head.
• The next of last node is NULL, as there is no node after the last node.
Below is the sample Doubly Linked List node in C++ and Java:
C++ :- Java-
// Class for Doubly Linked List
}; Node next;
Node(int d) { data = d; }
Below is the complete program to create and traverse a Doubly Linked List in both
C++ and Java:
Consider the above Doubly Linked List. Following are the Ordinary and XOR (or
Memory Efficient) representations of the Doubly Linked List.
Ordinary Representation:
• Node A: prev = NULL, next = add(B) // previous is NULL and next is address
of B
• Node B: prev = add(A), next = add(C) // previous is address of A and next is
address of C
• Node C: prev = add(B), next = add(D) // previous is address of B and next is
address of D
• Node D: prev = add(C), next = NULL // previous is address of C and next is
NULL
XOR List Representation: Let us call the address variable in XOR representation
as npx (XOR of next and previous)
• Node A:
npx = 0 XOR add(B) // bitwise XOR of zero and address of B
• Node B:
npx = add(A) XOR add(C) // bitwise XOR of address of A and address of C
• Node C:
npx = add(B) XOR add(D) // bitwise XOR of address of B and address of D
• Node D:
npx = add(C) XOR 0 // bitwise XOR of address of C and 0
Traversal of XOR Linked List: We can traverse the XOR list in both forward and
reverse directions. While traversing the list we need to remember the address of
the previously accessed node in order to calculate the next node’s address. For
example, when we are at node C, we must have the address of B. XOR of add(B)
and npx of C gives us the add(D). The reason is simple: npx(C) is “add(B) XOR
add(D)”. If we do xor of npx(C) with add(B), we get the result as “add(B) XOR
add(D) XOR add(B)” which is “add(D) XOR 0” which is “add(D)”. So we have the
address of the next node. Similarly, we can traverse the list in a backward direction.
Implementation:
To implement a circular singly linked list, we take an external pointer that points to
the last node of the list. If we have a pointer last pointing to the last node, then last
-> next will point to the first node.
The pointer last points to node Z and last -> next points to the node P.
Why have we taken a pointer that points to the last node instead of first node?
For insertion of node in the beginning we need to traverse the whole list. Also, for
insertion at the end, the whole list has to be traversed. If instead of start pointer we
take a pointer to the last node then in both the cases there won’t be any need to
traverse the whole list. So, insertion in the begging or at the end takes constant
time irrespective of the length of the list.
Below is a sample program to create and traverse in a Circular Linked List in both
Java and C++:
1. 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.
2. 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 the next of last.
3. 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 to
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.
4. Circular Doubly Linked Lists are used for implementation of advanced data
structures like Fibonacci Heap.
Description - Given a pointer to the head node of a linked list, the task is to reverse
the linked list. We need to reverse the list by changing links between nodes.
Three Pointers Solution : We will be using three auxiliary three pointers prev,
current and next to reverse the links of the linked list. The solution can be
understood by the below animation, how links are reversed.
Pseudo Code
void reverse(Node* head)
{
// Initialize current, previous and
// next pointers
Node *current = head;
Node *prev = NULL, *next = NULL
while (current != NULL)
{
// Store next
next = current->next
Two Pointers Solution : We will be using auxiliary two pointers current and
next to reverse the links of the linked list. This is little bit tricky solution. Try out
with examples-
Pseudo Code
1. We return the pointer of next node to his previous(current) node and then
make the previous node as the next node of returned node and then
returning the current node.
2. We first traverse till the last node and making the last node as the head
node of reversed linked list and then applying the above procedure in the
recursive manner.
Pseudo Code
Node* reverse(Node* node)
{
if (node == NULL) :
return NULL
if (node->next == NULL) :
head = node
return node
Node* temp = reverse(node->next)
temp->next = node
node->next = NULL
return node
}
Pseudo Code
bool detectLoop(Node* h)
{
seen //HashMap
while (h != NULL)
{
// If this node is already present
// in hashmap it means there is a cycle
// (Because you we encountering the
// node for the second time).
if (seen.find(h) == True)
return true
// If we are seeing the node for
// the first time, insert it in hash
seen.insert(h)
h = h->next
}
return false
}
Floyd’s Cycle-Finding Algorithm: This is the fastest method. Traverse linked list
using two pointers. Move one pointer by one step and another pointer by two-step.
If these pointers meet at the same node then there is a loop. If pointers do not meet
then linked list doesn’t have a loop.
You may visualize the solution as two runners are running on a circular track, If they
are having different speeds they will definitely meet up on circular track itself.
Pseudo Code
Above diagram shows an example with two linked list having 15 as intersection
point.
Naive Solutions : This solution requires modifications to basic linked list data
structure. Have a visited flag with each node. Traverse the first linked list and keep
marking visited nodes. Now traverse the second linked list, If you see a visited node
again then there is an intersection point, return the intersecting node. This solution
works in O(m+n) but requires additional information with each node. A variation of
this solution that doesn’t require modification to the basic data structure can be
implemented using a hash. Traverse the first linked list and store the addresses of
visited nodes in a hash. Now traverse the second linked list and if you see an
address that already exists in the hash then return the intersecting node.
Node Count Difference Solution : Problem can be solved following these steps -
1. Get count of the nodes in the first list, let count be c1.
2. Get a count of the nodes in the second list, let count be c2.
3. Get the difference of counts d = abs(c1 – c2).
4. Now traverse the bigger list from the first node till d nodes so that from here
onwards both the lists have equal no of nodes.
5. Then we can traverse both the lists in parallel till we come across a common
node. (Note that getting a common node is done by comparing the address
of the nodes)
Pseudo Code
HIMANSHU KUMAR(LINKEDIN)
https://www.linkedin.com/in/himanshukumarmahuri
CREDITS- INTERNET
DISCLOSURE- ALL THE DATA AND IMAGES ARE TAKEN FROM GOOGLE AND INTERNET.