Data Structures - Lecture - 2
Data Structures - Lecture - 2
Data Structures - Lecture - 2
Data Structures
Introduction
Data Structures are essential components that help organize and store data efficiently in
computer memory. They provide a way to manage and manipulate data effectively,
enabling faster access, insertion, and deletion operations.
Common data structures include arrays, linked lists, stacks, queues, trees, and graphs,
each serving specific purposes based on the requirements of the problem. Understanding
data structures is fundamental for designing efficient algorithms and optimizing software
performance.
Data Structure
A data structure refers to organizing and storing data in a computer's memory in a way that
enables efficient access, manipulation, and retrieval of the data. Data structures are fundamental
concepts in computer science and are extensively used in programming and software
development to solve various computational problems.
There are two categories of data structure - linear data structure and non-linear data structure. In
real life, linear data structure is used to develop software, and non-linear data structure is used in
image processing and artificial intelligence.
In this article, we will understand the difference between linear and non-linear data structures.
A linear data structure is a type of data structure that stores the data linearly or sequentially. In
the linear data structure, data is arranged in such a way that one element is adjacent to its
previous and the next element. It includes the data at a single level such that we can traverse all
data into a single run.
The implementation of the linear data structure is always easy as it stores the data linearly. The
common examples of linear data types are Stack, Queue, Array, LinkedList, and Hashmap, etc.
a. Stack
Users can push/pop data from a single end of the stack. Users can insert the data into the stack
via push operation and remove data from the stack via pop operation. The stack follows the rule
of LIFO (last in first out). Users can access all the stack data from the top of the stack in a linear
manner.
In real-life problems, the stack data structure is used in many applications. For example, the All
web browser uses the stack to store the backward/forward operations.
b. Queue
Queue data structure stores the data in a linear sequence. Queue data structure follows the FIFO
rule, which means first-in-first-out. It is similar to the stack data structure, but it has two ends. In
the queue, we can perform insertion operations from the rear using the Enqueue method and
deletion operations from the front using the deque method.
c. Array
The array is the most used Linear data type. The array stores the objects of the same data type in
a linear fashion. Users can use an array to construct all linear or non-linear data structures. For
example, Inside the car management software to store the car names array of the strings is useful.
We can access the element of the array by the index of elements. In an array, the index always
starts at 0. To prevent memory wastage, users should create an array of dynamic sizes.
d. LinkedList
LinkedList data structure stores the data in the form of a node. Every linked list node contains
the element value and address pointer. The address pointer of the LinkedList consists of the
address of the next node. It can store unique or duplicate elements.
In a non-linear data structure, data is connected to its previous, next, and more elements like a
complex structure. In simple terms, data is not organized sequentially in such a type of data
structure.
It is one type of Non-primitive data structure. In non-linear data structures, data is not stored
linear manner. There are multiple levels of nonlinear data structures. It is also called a multilevel
data structure. It is important to note here that we can't implement non-linear data structures as
easily as linear data structures.
a. Tree
A tree data structure is an example of a nonlinear data structure. It is a collection of nodes where
each node consists of the data element. Every tree structure contains a single root node.
In the tree, every child node is connected to the parent node. Every parent and child node has a
parent-child node relationship. In the tree, Nodes remaining at the last level are called leaf nodes,
and other nodes are called internal nodes.
Types of trees:
1. Binary tree
2. Binary search tree
3. AVL tree
4. Red-black tree
The Heap data structure is also a non-linear tree-based data structure, and it uses the complete
binary tree to store the data.
b. Graph
The graph contains vertices and edges. Every vertex of the graph stores the data element. Edges
are used to build a vertices relationship. Social networks are real-life examples of graph data
structure.
Here, we can consider the user as a vertex, and the relation between him/her with other users is
called an edge. There is no limitation for building the relationship between any two vertexes of
the graph like a tree. We can implement the graph data structure using the array or LinkedList.
Simple Graph
Un-directed Graph
Directed Graph
Weighted Graph
c. Hashmap
Hashmaps store data as key-value pairs. It can be either linear or non-linear data structure. We
can use the hashmap to map the value with its keys and search value efficiently from it.
Here, we have explained the key difference between linear and non-linear data structure in the
table format.
Every element makes a connection to only Every element makes a connection to more
its previous and next elements. than two elements.
The main use of the data structure is to build efficient algorithms such as sorting algorithms.
Stack, queue, array, etc., have a linear relationship between the data elements, while graph, tree,
hashmap, etc., have a complex relationship with the data.
a. Binary Tree
Unlike Arrays, Linked Lists, Stack, and queues, which are linear data structures, trees are
hierarchical data structures.
A binary tree is a tree data structure in which each node has at most two children, which are
referred to as the left child and the right child. It is implemented mainly using Links.
Binary Tree Representation: A tree is represented by a pointer to the topmost node in the tree.
If the tree is empty, then the value of the root is NULL. A Binary Tree node contains the
following parts.
1. Data
2. Pointer to left child
3. Pointer to the right child
A Binary Tree can be traversed in two ways:
Depth First Traversal: Inorder (Left-Root-Right), Preorder (Root-Left-Right), and Postorder
(Left-Right-Root)
Breadth-First Traversal: Level Order Traversal
Examples: One reason to use binary trees or trees, in general, is for the things that form a
hierarchy. They are useful in File structures where each file is located in a particular directory
and there is a specific hierarchy associated with files and directories. Another example where
Trees are useful is storing hierarchical objects like JavaScript Document Object Model considers
HTML page as a tree with nesting of tags as parent-child relations.
Time Complexities:
Search : O(h)
Insertion : O(h)
Deletion : O(h)
Extra Space : O(n) for pointers
h: Height of BST
n: Number of nodes in BST
If Binary Search Tree is Height Balanced,
then h = O(Log n)
Self-Balancing BSTs such as AVL Tree, Red-Black
Tree and Splay Tree make sure that height of BST
remains O(Log n)
BST provides moderate access/search (quicker than Linked List and slower than arrays).
BST provides moderate insertion/deletion (quicker than Arrays and slower than Linked Lists).
Examples: Its main use is in search applications where data is constantly entering/leaving and
data needs to be printed in sorted order. For example in implementation in E-commerce websites
where a new product is added or product goes out of stock and all products are listed in sorted
order.
c. Binary Heap
A Binary Heap is a Binary Tree with the following properties.
1) It’s a complete tree (All levels are completely filled except possibly the last level and the last
level has all keys as left as possible). This property of Binary Heap makes them suitable to be
stored in an array.
2) A Binary Heap is either Min Heap or Max Heap. In a Min Binary Heap, the key at the root
must be minimum among all keys present in Binary Heap. The same property must be
recursively true for all nodes in Binary Tree. Max Binary Heap is similar to Min Heap. It is
mainly implemented using an array.
Get Minimum in Min Heap: O(1) [Or Get Max in Max Heap]
Extract Minimum Min Heap: O(Log n) [Or Extract Max in Max Heap]
Decrease Key in Min Heap: O(Log n) [Or Decrease Key in Max Heap]
Insert: O(Log n)
Delete: O(Log n)
d. Hashing: Hashing is a popular technique for storing and retrieving data as fast as
possible. The main reason behind using hashing is that it gives optimal results as it performs
optimal searches.
class HashFunction {
public:
static int hash_function(std::string input_string)
{
int main()
{
// Given input string
std::string input_string = "Hello, World!";
// Function call
int hash_value =
HashFunction::hash_function(input_string);
return 0;
}
Output
18
Irreversible: It should be difficult to generate the original input given only the hash value.
Hash Function: A function that converts a given big phone number to a small practical integer
value. The mapped integer value is used as an index in hash table. So, in simple terms we can say
that a hash function is used to transform a given key into a specific slot index. Its main job is to
map each and every possible key into a unique slot index. If every key is mapped into a unique
slot index, then the hash function is known as a perfect hash function. It is very difficult to create
a perfect hash function but our job as a programmer is to create such a hash function with the
help of which the number of collisions are as few as possible. Collision is discussed ahead.
Hash Table: An array that stores pointers to records corresponding to a given phone number. An
entry in hash table is NIL if no existing phone number has hash function value equal to the index
for the entry. In simple terms, we can say that hash table is a generalization of array. Hash table
gives the functionality in which a collection of data is stored in such a way that it is easy to find
those items later if required. This makes searching of an element very efficient.
Collision Handling: Since a hash function gets us a small number for a key which is a big
integer or string, there is the possibility that two keys result in the same value. The situation
where a newly inserted key maps to an already occupied slot in the hash table is called collision
and must be handled using some collision handling technique. Following are the ways to handle
collisions:
Chaining: The idea is to make each cell of the hash table point to a linked list of records that
have the same hash function value. Chaining is simple but requires additional memory outside
the table.
Open Addressing: In open addressing, all elements are stored in the hash table itself. Each table
entry contains either a record or NIL. When searching for an element, we one by one examine
table slots until the desired element is found or it is clear that the element is not in the table.
Space : O(n)
Search : O(1) [Average] O(n) [Worst case]
Insertion : O(1) [Average] O(n) [Worst Case]
Deletion : O(1) [Average] O(n) [Worst Case]
Hashing seems better than BST for all the operations. But in hashing, elements are unordered
and in BST elements are stored in an ordered manner. Also, BST is easy to implement but hash
functions can sometimes be very complex to generate. In BST, we can also efficiently find floor
and ceil of values.
Example: Hashing can be used to remove duplicates from a set of elements. Can also be used to
find the frequency of all items. For example, in web browsers, we can check visited URLs using
hashing. In firewalls, we can use hashing to detect spam. We need to hash IP addresses. Hashing
can be used in any situation where want search() insert() and delete() in O(1) time.
1.9 Overview of Graph, Trie, Segment Tree and Suffix Tree Data
Structures
Introduction:
a. Graph: A graph is a collection of vertices (nodes) and edges that represent relationships
between the vertices. Graphs are used to model and analyze networks, such as social
networks or transportation networks.
b. Trie: A trie, also known as a prefix tree, is a tree-like data structure that stores a
collection of strings. It is used for efficient searching and retrieval of strings, especially in
the case of a large number of strings.
c. Segment Tree: A segment tree is a tree-like data structure that stores information about
ranges of values. It is used for range queries and range updates, such as finding the sum
of an array or finding the minimum or maximum value in an array.
d. Suffix Tree: A suffix tree is a tree-like data structure that stores all suffixes of a given
string. It is used for efficient string search and pattern matching, such as finding the
longest repeated substring or the longest common substring.
a. Graph: Graph is a data structure that consists of the following two components:
1. A finite set of vertices is also called nodes.
2. A finite set of ordered pairs of the form (u, v) is called an edge. The pair is ordered because (u,
v) is not the same as (v, u) in the case of a directed graph(di-graph). The pair of forms (u, v)
indicates that there is an edge from vertex u to vertex v. The edges may contain
weight/value/cost.
V -> Number of Vertices. E -> Number of Edges. The graph can be classified on the basis of
many things, below are the two most common classifications :
1. Direction: Undirected Graph: The graph in which all the edges are bidirectional.Directed
Graph: The graph in which all the edges are unidirectional.
2. Weight: Weighted Graph: The Graph in which weight is associated with the
edges.Unweighted Graph: The Graph in which there is no weight associated with the edges.
Algorithm to implement graph –
The general algorithmic steps to implement a graph data structure:
Create a class for the graph: Start by creating a class that represents the graph data structure.
This class should contain variables or data structures to store information about the vertices
and edges in the graph.
Represent vertices: Choose a data structure to represent the vertices in the graph. For
example, you could use an array, linked list, or dictionary.
Represent edges: Choose a data structure to represent the edges in the graph. For example,
you could use an adjacency matrix or an adjacency list.
Add vertices: Implement a method to add vertices to the graph. You should store information
about the vertex, such as its name or identifier.
Add edges: Implement a method to add edges between vertices in the graph. You should store
information about the edge, such as its weight or direction.
Search for vertices: Implement a method to search for a specific vertex in the graph.
Search for edges: Implement a method to search for a specific edge in the graph.
Remove vertices: Implement a method to remove vertices from the graph.
Remove edges: Implement a method to remove edges from the graph.
Traverse the graph: Implement a method to traverse the graph and visit each vertex. You
could use algorithms like depth-first search or breadth-first search.
This algorithm provides a basic structure for implementing a graph data structure in any
programming language. You can adjust the implementation to meet the specific needs of your
application.
Graphs can be represented in many ways, below are the two most common representations: Let
us take the below example graph to see two representations of the graph.
root
/ \ \
t a b
| | |
h n y
| | \ |
e s y e
/ | |
i r w
| | |
r e e
|
r
c. Segment Tree
This data structure is usually implemented when there are a lot of queries on a set of values.
These queries involve minimum, maximum, sum, .. etc on an input range of a given set. Queries
also involve updating values in the given set. Segment Trees are implemented using an array.
d. Suffix Tree
The suffix tree is mainly used to search for a pattern in a text. The idea is to preprocess the text
so that the search operation can be done in time linear in terms of pattern length. The pattern
searching algorithms like KMP, Z, etc take time proportional to text length. This is really a great
improvement because the length of the pattern is generally much smaller than the text. Imagine
we have stored the complete work of William Shakespeare and preprocessed it. You can search
any string in the complete work in time just proportional to the length of the pattern. But using
Suffix Tree may not be a good idea when text changes frequently like text editor, etc. A suffix
tree is a compressed trie of all suffixes, so the following are very abstract steps to build a suffix
tree from given text. 1) Generate all suffixes of the given text. 2) Consider all suffixes as
individual words and build a compressed trie.
Example: Used to find all occurrences of the pattern in a string. It is also used to find the longest
repeated substring (when the text doesn’t change often), the longest common substring and the
longest palindrome in a string.
Graphs:
Advantages:
Disadvantages:
Space complexity: Storing the information about vertices and edges in a graph can be
memory-intensive.
More complex algorithms: The algorithms used to traverse a graph and search for
specific vertices or edges can be more complex than other data structures like arrays or
linked lists.
Slower operations: Operations like adding or removing vertices or edges can be slower
than with other data structures.
Difficult to implement: Implementing a graph data structure can be more difficult than
other data structures, requiring a good understanding of graph theory and algorithms.
May not be suitable for some use cases: For certain use cases, a graph data structure
may not be the best option and another data structure like an array or linked list may be
more suitable.
May require significant computational resources
Finding the optimal path between nodes can be a challenging problem
Tries:
Advantages:
Disadvantages:
Advantages:
Disadvantages:
Advantages:
Disadvantages:
1. Array:
Array is a linear data structure that stores a collection of elements of the same data type. Elements
are allocated contiguous memory, allowing for constant-time access. Each element has a unique
index number.
Search Operation:
In an unsorted array, the search operation can be performed by linear traversal from the first
element to the last element.
Coding implementation of the search operation:
C++
C
Java
Python3
C#
Javascript
PHP
// Driver's Code
int main()
{
int arr[] = { 12, 34, 10, 6, 40 };
int n = sizeof(arr) / sizeof(arr[0]);
// Function call
int position = findElement(arr, n, key);
if (position == -1)
cout << "Element not found";
else
cout << "Element Found at Position: "
<< position + 1;
return 0;
}
Output
Element Found at Position: 5
C++
C
Java
Python3
C#
Javascript
PHP
#include <iostream>
using namespace std;
arr[n] = key;
return (n + 1);
}
int main()
{
int arr[20] = { 12, 16, 20, 40, 50, 70 };
int capacity = sizeof(arr) / sizeof(arr[0]);
int n = 6;
int i, key = 26;
// Inserting key
n = insertSorted(arr, n, key, capacity);
return 0;
}
Output
Before Insertion: 12 16 20 40 50 70
After Insertion: 12 16 20 40 50 70 26
#include <bits/stdc++.h>
using namespace std;
arr[pos] = x;
}
// Driver's code
int main()
{
int arr[15] = { 2, 4, 1, 8, 5 };
int n = 5;
cout<<endl;
// Function call
insertElement(arr, n, x, pos);
n++;
return 0;
}
Output
Before insertion : 2 4 1 8 5
After insertion : 2 4 10 1 8 5
C++
C
Java
Python3
C#
Javascript
PHP
if (pos == -1) {
cout << "Element not found";
return n;
}
// Deleting element
int i;
for (i = pos; i < n - 1; i++)
arr[i] = arr[i + 1];
return n - 1;
}
return -1;
}
// Driver's code
int main()
{
int i;
int arr[] = { 10, 50, 30, 40, 20 };
// Function call
n = deleteElement(arr, n, key);
return 0;
}
C++
C
Java
Python3
C#
PHP
Javascript
// C++ program to implement binary search in sorted
array
#include <bits/stdc++.h>
using namespace std;
/* Driver code */
int main()
{
// Let us search 3 in below array
int arr[] = { 5, 6, 7, 8, 9, 10 };
int n, key;
n = sizeof(arr) / sizeof(arr[0]);
key = 10;
// Function call
cout << "Index: " << binarySearch(arr, 0, n - 1, key)
<< endl;
return 0;
}
Index: 5
Time Complexity: O(log(n)) Using Binary Search
Auxiliary Space: O(log(n)) due to recursive calls, otherwise iterative version uses Auxiliary
Space of O(1).
In a sorted array, a search operation is performed for the possible position of the given element
by using Binary search, and then an insert operation is performed followed by shifting the
elements. And in an unsorted array, the insert operation is faster as compared to the sorted array
because we don’t have to care about the position at which the element is placed.
Below is the implementation of the above approach:
C++
C
Java
Python3
C#
Javascript
// C++ program to implement insert operation in
// an sorted array.
#include <bits/stdc++.h>
using namespace std;
int i;
for (i = n - 1; (i >= 0 && arr[i] > key); i--)
arr[i + 1] = arr[i];
arr[i + 1] = key;
return (n + 1);
}
/* Driver code */
int main()
{
int arr[20] = { 12, 16, 20, 40, 50, 70 };
int capacity = sizeof(arr) / sizeof(arr[0]);
int n = 6;
int i, key = 26;
// Function call
n = insertSorted(arr, n, key, capacity);
return 0;
}
Before Insertion: 12 16 20 40 50 70
After Insertion: 12 16 20 26 40 50 70
Time Complexity: O(N) [In the worst case all elements may have to be moved]
Auxiliary Space: O(1)
How to Delete in a Sorted Array?
In the delete operation, the element to be deleted is searched using binary search, and then the
delete operation is performed followed by shifting the elements.
C++
C
Java
Python3
C#
Javascript
// C++ program to implement delete operation in a
// sorted array
#include <bits/stdc++.h>
using namespace std;
if (pos == -1) {
cout << "Element not found";
return n;
}
// Deleting element
int i;
for (i = pos; i < n - 1; i++)
arr[i] = arr[i + 1];
return n - 1;
}
// Driver code
int main()
{
int i;
int arr[] = { 10, 20, 30, 40, 50 };
// Function call
n = deleteElement(arr, n, key);
Time Complexity: O(N). In the worst case all elements may have to be moved
Auxiliary Space: O(log N). An implicit stack will be used
Array reverse or reverse a array means changing the position of each number of the given
array to its opposite position from end, i.e. if a number is at position 1 then its new position will
be Array.length, similarly if a number is at position 2 then its new position will be Array.length –
1, and so on.
#include <iostream>;
using namespace std;
int main()
{
int originalArr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(originalArr) / sizeof(originalArr[0]);
reverseArrayExtraArray(originalArr, size);
}
Output
Reversed Array: 5 4 3 2 1
Time Complexity: O(n)
Copying elements to a new array is a linear operation.
Auxiliary Space Complexity: O(n)
Additional space is used to store the new array.
2. Array Reverse Using a Loop (In-place):
Iterate through the array using two pointers (start and end).
Swap elements at the start and end pointers.
Move the start pointer towards the end and the end pointer towards the start until they meet or
cross each other.
Below is the implementation of the above approach :
C++CJavaPython3C#JavaScriptPHP
// Iterative C++ program to reverse an array
#include <bits/stdc++.h>;
using namespace std;
return 0;
}
Output
1 2 3 4 5 6
Reversed array is
6 5 4 3 2 1
int main()
{
int originalArray[] = { 1, 2, 3, 4, 5 };
int length
= sizeof(originalArray) / sizeof(originalArray[0]);
return 0;
}
Output
5 4 3 2 1
Time Complexity: O(n) The reverse method typically has linear time complexity.
Auxiliary Space Complexity: O(n)
Additional space is used to store the reversed array.
4. Array Reverse Recursion (In-place or Non In-place):
Define a recursive function that takes an array as input.
Swap the first and last elements.
Recursively call the function with the remaining subarray.
Below is the implementation of the above approach :
C++CJavaPython3C#JavaScriptPHP
// Function calling
reverseArray(arr, 0, 5);
return 0;
}
Output
1 2 3 4 5 6
Reversed array is
6 5 4 3 2 1
Time Complexity: O(n). The recursion goes through each element once, so it’s linear.
Auxiliary Space Complexity: O(n) for non in-place, O(log n) for in-place (due to recursion
stack).
5. Array Reverse Stack (Non In-place):
Push each element of the array onto a stack.
Pop elements from the stack to form the reversed array.
Below is the implementation of the above approach :
C++CJavaPython3C#JavaScript
#include <iostream>;
#include <stack>;
#include <vector>;
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int size = sizeof(arr) / sizeof(arr[0]);
reverseArrayUsingStack(arr, size);
return 0;
}
Output
Reversed Array: 5 4 3 2 1
Time Complexity: O(n)
Pushing and popping each element onto/from the stack requires linear time.
Auxiliary Space Complexity: O(n)
Additional space is used to store the stack.
Leaders in an array
Write a program to print all the LEADERS in the array. An element is a leader if it is greater
than all the elements to its right side. And the rightmost element is always a leader.
For example:
Input: arr[] = {16, 17, 4, 3, 5, 2},
Output: 17, 5, 2
Input: arr[] = {1, 2, 3, 4, 5, 2},
Output: 5, 2
Recommended Problem
Naive Approach: The problem can be solved based on the idea mentioned below:
Use two loops. The outer loop runs from 0 to size – 1 and one by one pick all elements from left
to right. The inner loop compares the picked element to all the elements on its right side. If the
picked element is greater than all the elements to its right side, then the picked element is the
leader.
Follow the below steps to implement the idea:
We run a loop from the first index to the 2nd last index.
And for each index, we run another loop from the next index to the last index.
If all the values to the right of that index are smaller than the index, we simply add
the value in our answer data structure.
Below is the implementation of the above approach.
C
C++
Java
Python3
C#
PHP
Javascript
/*C Function to print leaders in an array */
#include <stdio.h>
17 5 2
Time Complexity: O(N * N)
Auxiliary Space: O(1)
Find Leader by finding suffix maximum:
The idea is to scan all the elements from right to left in an array and keep track of the maximum
till now. When the maximum changes its value, print it.
Follow the below illustration for a better understanding
Illustration:
Let the array be arr[] = {16, 17, 4, 3, 5, 2}
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 2 , ans[] = { 2 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 5 , ans[] = { 2, 5 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 5 , ans[] = { 2, 5 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 5 , ans[] = { 2, 5 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 17 , ans[] = { 2, 5, 17 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 17 , ans[] = { 2, 5, 17 }
Follow the steps mentioned below to implement the idea:
We start from the last index position. The last position is always a leader, as there are no
elements towards its right.
And then we iterate on the array till we reach index position = 0.
Each time we keep a check on the maximum value
Every time we encounter a maximum value than the previous maximum value
encountered, we either print or store the value as it is the leader
Below is the implementation of the above approach.
C
C++
Java
Python3
C#
PHP
Javascript
#include <stdio.h>
2 5 17
Time Complexity: O(n)
Auxiliary Space: O(1)
Find leaders and print them in the same order as they are:
In the previous method, we get time linear complexity, but the output we get is not in the same
order as the elements that appear in our input array, so to get out output in the same order as in
the input array, we can use stack data structure.
Illustration:
Let the array be arr[] = {16, 17, 4, 3, 5, 2}, we will store the ans in a stack to print in the same
order.
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 2 , ans[] = { 2 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 5 , ans[] = { 5, 2 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 5 , ans[] = { 5, 2 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 5 , ans[] = { 5, 2 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 17 , ans[] = { 17, 5, 2 }
arr[] = {16, 17, 4, 3, 5, 2} , max_from_right = 17 , ans[] = { 17, 5, 2 }
Follow the below steps to implement the idea:
We start from the last index position. The last position is always a leader, as there are no
elements towards its right.
And then we iterate on the array till we reach index position = 0.
Each time we keep a check on the maximum value
Every time we encounter a maximum value than the previous maximum value
encountered, we will store the value in the stack as it is the leader
We will iterate on the stack and print the values
Below is the implementation of the above approach.
C
C++
Java
Python3
C#
Javascript
#include <stdio.h>
#include <stdlib.h>
17 5 2
Time complexity: O(n)
Auxiliary space: O(n)
Given an array A[] and a number x, check for pair in A[] with sum as x
Check if pair with given Sum exists in Array (Two Sum)
Given an array A[] of n numbers and another number x, the task is to check whether or not there
exist two elements in A[] whose sum is exactly x.
Examples:
Input: arr[] = {0, -1, 2, -3, 1}, x= -2
Output: Yes
Explanation: If we calculate the sum of the output,1 + (-3) = -2
Input: arr[] = {1, -2, 1, 0, 5}, x = 0
Output: No
Recommended Problem
return 0;
}
// Driver code
int main()
{
int A[] = { 0, -1, 2, -3, 1 };
int x = -2;
int size = sizeof(A) / sizeof(A[0]);
return 0;
}
Yes
Time Complexity: O(N2), Finding pair for every element in the array of size N.
Auxiliary Space: O(1)
Two Sum using Sorting and Two-Pointers technique:
The idea is to use the two-pointer technique. But for using the two-pointer technique, the array
must be sorted. Once the array is sorted the two pointers can be taken which mark the beginning
and end of the array respectively. If the sum is greater than the sum of those two elements, shift
the right pointer to decrease the value of the required sum and if the sum is lesser than the
required value, shift the left pointer to increase the value of the required sum.
Illustration:
Let an array be {1, 4, 45, 6, 10, -8} and sum to find be 16
After sorting the array
A = {-8, 1, 4, 6, 10, 45}
Now, increment ‘l’ when the sum of the pair is less than the required sum and decrement ‘r’
when the sum of the pair is more than the required sum.
This is because when the sum is less than the required sum then to get the number which could
increase the sum of pair, start moving from left to right(also sort the array) thus “l++” and vice
versa.
Initialize l = 0, r = 5
A[l] + A[r] ( -8 + 45) > 16 => decrement r. Now r = 4
A[l] + A[r] ( -8 + 10) increment l. Now l = 1
A[l] + A[r] ( 1 + 10) increment l. Now l = 2
A[l] + A[r] ( 4 + 10) increment l. Now l = 3
A[l] + A[r] ( 6 + 10) == 16 => Found candidates (return 1)
Note: If there is more than one pair having the given sum then this algorithm reports only one.
Can be easily extended for this though.
Follow the steps below to solve the problem:
hasArrayTwoCandidates (A[], ar_size, sum)
Sort the array in non-decreasing order.
Initialize two index variables to find the candidate
elements in the sorted array.
#include <bits/stdc++.h>
using namespace std;
// Function calling
if (hasArrayTwoCandidates(A, arr_size, n))
cout << "Yes";
else
cout << "No";
return 0;
}
Output
Yes
Time Complexity: O(NlogN), Time complexity for sorting the array
Auxiliary Space: O(1)
Two Sum using Binary Search:
Sort the array, then traverse the array elements and perform binary search for (target – a[i]) on
the remaining part
Follow the below steps to solve the problem:
Sort the array in non-decreasing order.
Traverse from 0 to N-1
Initialize searchKey = sum – A[i]
If(binarySearch(searchKey, A, i + 1, N) == True
Return True
Return False
Below is the implementation of the above approach:
C++JavaPython3C#JavaScript
// C++ program to check if given array
// has 2 elements whose sum is equal
// to the given value
#include <bits/stdc++.h>
using namespace std;
// Function calling
if (checkTwoSum(A, arr_size, n))
cout << "Yes";
else
cout << "No";
return 0;
}
Output
Yes
Time Complexity: O(NlogN)
Auxiliary Space: O(1)
Two Sum using Hashing:
This problem can be solved efficiently by using the technique of hashing. Use a hash_map to
check for the current array value x(let), if there exists a value target_sum-x which on adding to
the former gives target_sum. This can be done in constant time.
Illustration:
arr[] = {0, -1, 2, -3, 1}
sum = -2
Now start traversing:
Step 1: For ‘0’ there is no valid number ‘-2’ so store ‘0’ in hash_map.
Step 2: For ‘-1’ there is no valid number ‘-1’ so store ‘-1’ in hash_map.
Step 3: For ‘2’ there is no valid number ‘-4’ so store ‘2’ in hash_map.
Step 4: For ‘-3’ there is no valid number ‘1’ so store ‘-3’ in hash_map.
Step 5: For ‘1’ there is a valid number ‘-3’ so answer is 1, -3
unordered_set s
for(i=0 to end)
if(s.find(target_sum – arr[i]) == s.end)
insert(arr[i] into s)
else
print arr[i], target-arr[i]
Follow the steps below to solve the problem:
Initialize an empty hash table s.
Do the following for each element A[i] in A[]
if (s.find(temp) != s.end()) {
cout << "Yes" << endl;
return;
}
s.insert(arr[i]);
}
cout << "No" << endl;
}
/* Driver Code */
int main()
{
int A[] = { 1, 4, 45, 6, 10, 8 };
int n = 16;
int arr_size = sizeof(A) / sizeof(A[0]);
// Function calling
printPairs(A, arr_size, n);
return 0;
}
Output
Yes
Time Complexity: O(N), As the whole array is needed to be traversed only once.
Auxiliary Space: O(N), A hash map has been used to store array elements.
Note: The solution will work even if the range of numbers includes negative numbers + if the
pair is formed by numbers recurring twice in array eg: array = [3,4,3]; pair = (3,3); target sum =
6.
Two Sum Using remainders of the elements less than x:
The idea is to count the elements with remainders when divided by x, i.e 0 to x-1, each remainder
separately. Suppose we have x as 6, then the numbers which are less than 6 and have remainders
which add up to 6 gives sum as 6 when added. For example, we have elements, 2,4 in the array
and 2%6 = 2 and 4%6 =4, and these remainders add up to give 6. Like that we have to check for
pairs with remainders (1,5),(2,4),(3,3). if we have one or more elements with remainder 1 and
one or more elements with remainder 5, then surely we get a sum as 6. Here we do not consider
(0,6) as the elements for the resultant pair should be less than 6. when it comes to (3,3) we have
to check if we have two elements with remainder 3, then we can say that “There exists a pair
whose sum is x”.
Follow the steps below to solve the problem:
1. Create an array with size x.
2. Initialize all rem elements to zero.
3. Traverse the given array
Do the following if arr[i] is less than x:
r=arr[i]%x which is done to get the remainder.
rem[r]=rem[r]+1 i.e. increasing the count of elements that have remainder r when
divided with x.
4. Now, traverse the rem array from 1 to x/2.
If(rem[i]> 0 and rem[x-i]>0) then print “YES” and come out of the loop. This means that we
have a pair that results in x upon doing.
5. Now when we reach at x/2 in the above loop
If x is even, for getting a pair we should have two elements with remainder x/2.
If rem[x/2]>1 then print “YES” else print “NO”
If it is not satisfied that is x is odd, it will have a separate pair with x-x/2.
If rem[x/2]>0 and rem[x-x/2]>0 , then print “Yes” else, print”No”;
Below is the implementation of the above approach:
C++CJavaPython3C#JavaScript
// Code in cpp to tell if there exists a pair in array whose
// sum results in x.
#include <iostream>
using namespace std;
/* Driver Code */
int main()
{
int A[] = { 1, 4, 45, 6, 10, 8 };
int n = 16;
int arr_size = sizeof(A) / sizeof(A[0]);
// Function calling
printPairs(A, arr_size, n);
return 0;
}
Yes
Time Complexity: O(N+X), Traversing over the array of size N and Checking for remainders
till X
Auxiliary Space: O(X), Space for storing remainders
Related Problems:
Given two unsorted arrays, find all pairs whose sum is x
Count pairs with given sum
Count all distinct pairs with difference equal to k
Please write comments if you find any of the above codes/algorithms incorrect, or find other
ways to solve the same problem.
Majority Element
Find the majority element in the array. A majority element in an array A[] of size n is an
element that appears more than n/2 times (and hence there is at most one such element).
Examples :
Input : A[]={3, 3, 4, 2, 4, 4, 2, 4, 4}
Output : 4
Explanation: The frequency of 4 is 5 which is greater than the half of the size of the array size.
Input : A[] = {3, 3, 4, 2, 4, 4, 2, 4}
Output : No Majority Element
Explanation: There is no element whose frequency is greater than the half of the size of the
array size.
Recommended Problem
Naive Approach:
The basic solution is to have two loops and keep track of the maximum count for all different
elements. If the maximum count becomes greater than n/2 then break the loops and return the
element having the maximum count. If the maximum count doesn’t become more than n/2 then
the majority element doesn’t exist.
Illustration:
arr[] = {3, 4, 3, 2, 4, 4, 4, 4}, n = 8
For i = 0:
count = 0
Loop over the array, whenever an element is equal to arr[i] (is 3), increment count
count of arr[i] is 2, which is less than n/2, hence it can’t be majority element.
For i = 1:
count = 0
Loop over the array, whenever an element is equal to arr[i] (is 4), increment count
count of arr[i] is 5, which is greater than n/2 (i.e 4), hence it will be majority element.
Hence, 4 is the majority element.
Follow the steps below to solve the given problem:
Create a variable to store the max count, count = 0
Traverse through the array from start to end.
For every element in the array run another loop to find the count of similar elements in the
given array.
If the count is greater than the max count update the max count and store the index in another
variable.
If the maximum count is greater than half the size of the array, print the element. Else print
there is no majority element.
else
cout << "No Majority Element" << endl;
}
// Driver code
int main()
{
int arr[] = { 1, 1, 2, 1, 3, 5, 1 };
int n = sizeof(arr) / sizeof(arr[0]);
// Function calling
findMajority(arr, n);
return 0;
}
Output
1
Time Complexity: O(n*n), A nested loop is needed where both the loops traverse the array from
start to end.
Auxiliary Space: O(1), No extra space is required.
Majority Element using Binary Search Tree
Insert elements in BST one by one and if an element is already present then increment the count
of the node. At any stage, if the count of a node becomes more than n/2 then return.
Illustration:
Follow the steps below to solve the given problem:
Create a binary search tree, if the same element is entered in the binary search tree the
frequency of the node is increased.
traverse the array and insert the element in the binary search tree.
If the maximum frequency of any node is greater than half the size of the array, then perform
an inorder traversal and find the node with a frequency greater than half
Else print No majority Element.
Below is the implementation of the above idea:
CJavaPython3C#JavascriptC++14
#include <stdio.h>
#include <stdlib.h>
struct node {
int key;
int c;
struct node* left;
struct node* right;
};
return newNode(key);
}
inorder(root->right, s);
}
}
int main() {
int a[] = { 1, 3, 3, 3, 2 };
int size = sizeof(a) / sizeof(a[0]);
return 0;
}
Output
Time Complexity: If a Binary Search Tree is used then time complexity will be O(n²). If a self-
balancing-binary-search tree is used then it will be O(nlogn)
Auxiliary Space: O(n), As extra space is needed to store the array in the tree.
Majority Element Using Moore’s Voting Algorithm:
This is a two-step process:
The first step gives the element that may be the majority element in the array. If there is a
majority element in an array, then this step will definitely return majority element, otherwise,
it will return candidate for majority element.
Check if the element obtained from the above step is the majority element. This step is
necessary as there might be no majority element.
Illustration:
arr[] = {3, 4, 3, 2, 4, 4, 4, 4}, n = 8
maj_index = 0, count = 1
At i = 1: arr[maj_index] != arr[i]
count = count – 1 = 1 – 1 = 0
now count == 0 then:
maj_index = i = 1
count = count + 1 = 0 + 1 = 1
At i = 2: arr[maj_index] != arr[i]
count = count – 1 = 1 – 1 = 0
now count == 0 then:
maj_index = i = 2
count = count + 1 = 0 + 1 = 1
At i = 3: arr[maj_index] != arr[i]
count = count – 1 = 1 – 1 = 0
now count == 0 then:
maj_index = i = 3
count = count + 1 = 0 + 1 = 1
At i = 4: arr[maj_index] != arr[i]
count = count – 1 = 1 – 1 = 0
now count == 0 then:
maj_index = i = 4
count = count + 1 = 0 + 1 = 1
At i = 5: arr[maj_index] == arr[i]
count = count + 1 = 1 + 1 = 2
At i = 6: arr[maj_index] == arr[i]
count = count + 1 = 2 + 1 = 3
At i = 7: arr[maj_index] == arr[i]
count = count + 1 = 3 + 1 = 4
Therefore, the arr[maj_index] may be the possible candidate for majority element.
Now, Again traverse the array and check whether arr[maj_index] is the majority element or not.
arr[maj_index] is 4
4 occurs 5 times in the array therefore 4 is our majority element.
Follow the steps below to solve the given problem:
Loop through each element and maintains a count of the majority element, and a majority
index, maj_index
If the next element is the same then increment the count if the next element is not the same
then decrement the count.
if the count reaches 0 then change the maj_index to the current element and set the count again
to 1.
Now again traverse through the array and find the count of the majority element found.
If the count is greater than half the size of the array, print the element
Else print that there is no majority element
Below is the implementation of the above idea:
C++CJavaPython3C#JavascriptPHP
if (a[i] == cand)
count++;
else
return 0;
}
else
cout << "No Majority Element";
}
/* Driver code */
int main()
{
int a[] = { 1, 3, 3, 1, 2 };
int size = (sizeof(a)) / sizeof(a[0]);
// Function calling
printMajority(a, size);
return 0;
}
Output
No Majority Element
Time Complexity: O(n), As two traversal of the array, is needed, so the time complexity is
linear.
Auxiliary Space: O(1), As no extra space is required.
Majority Element Using Hashing:
In Hashtable(key-value pair), at value, maintain a count for each element(key), and whenever
the count is greater than half of the array length, return that key(majority element).
Illustration:
arr[] = {3, 4, 3, 2, 4, 4, 4, 4}, n = 8
Create a hashtable for the array
3 -> 2
4 -> 5
2 -> 1
Now traverse the hashtable
Count for 3 is 2, which is less than n/2 (4) therefore it can’t be the majority element.
Count for 4 is 5, which is greater than n/2 (4) therefore 4 is the majority element.
Hence, 4 is the majority element.
Follow the steps below to solve the given problem:
Create a hashmap to store a key-value pair, i.e. element-frequency pair.
Traverse the array from start to end.
For every element in the array, insert the element in the hashmap if the element does not exist
as a key, else fetch the value of the key ( array[i] ), and increase the value by 1
If the count is greater than half then print the majority element and break.
If no majority element is found print “No Majority element”
Below is the implementation of the above idea:
C++CJavaPython3C#Javascript
// Driver code
int main()
{
int arr[] = {2, 2, 2, 2, 5, 5, 2, 3, 3};
int n = sizeof(arr) / sizeof(arr[0]);
// Function calling
findMajority(arr, n);
return 0;
}
Output
Majority found :- 2
Time Complexity: O(n), One traversal of the array is needed, so the time complexity is linear.
Auxiliary Space: O(n), Since a hashmap requires linear space.
Majority Element Using Sorting:
The idea is to sort the array. Sorting makes similar elements in the array adjacent, so traverse
the array and update the count until the present element is similar to the previous one. If the
frequency is more than half the size of the array, print the majority element.
Illustration:
arr[] = {3, 4, 3, 2, 4, 4, 4, 4}, n = 8
Array after sorting => arr[] = {2, 3, 3, 4, 4, 4, 4, 4}, count = 1
At i = 1:
arr[i] != arr[i – 1] => arr[1] != arr[0]
count is not greater than n/2, therefore reinitialise count with, count = 1
At i = 2:
arr[i] == arr[i – 1] => arr[2] == arr[1] = 3
count = count + 1 = 1 + 1 = 2
At i = 3
arr[i] != arr[i – 1] => arr[3] != arr[2]
count is not greater than n/2, therefore reinitialise count with, count = 1
At i = 4
arr[i] == arr[i – 1] => arr[4] == arr[3] = 4
count = count + 1 = 1 + 1 = 2
At i = 5
arr[i] == arr[i – 1] => arr[5] == arr[4] = 4
count = count + 1 = 2 + 1 = 3
At i = 6
arr[i] == arr[i – 1] => arr[6] == arr[5] = 4
count = count + 1 = 3 + 1 = 4
At i = 7
arr[i] == arr[i – 1] => arr[7] == arr[6] = 4
count = count + 1 = 4 + 1 = 5
Therefore, the count of 4 is now greater than n/2.
Hence, 4 is the majority element.
Follow the steps below to solve the given problem:
Sort the array and create a variable count and previous, prev = INT_MIN.
Traverse the element from start to end.
If the current element is equal to the previous element increase the count.
Else set the count to 1.
If the count is greater than half the size of the array, print the element as the majority element
and break.
If no majority element is found, print “No majority element”
Below is the implementation of the above idea:
C++JavaPython3C#Javascript
int cnt = 1;
// sort the array, o(nlogn)
sort(arr, arr + n);
for (int i = 1; i <= n; i++){
if (arr[i - 1] == arr[i]){
cnt++;
}
else{
if (cnt > n / 2){
return arr[i - 1];
}
cnt = 1;
}
}
// if no majority element, return -1
return -1;
}
// Driver code
int main()
{
int arr[] = {1, 1, 2, 1, 3, 5, 1};
int n = sizeof(arr) / sizeof(arr[0]);
// Function calling
cout<<majorityElement(arr, n);
return 0;
}
Output
In this recursive approach, we divide the array into halves until we reach base cases where the
array has only one element. Then, we combine the majority elements obtained from the left and
right halves to find the overall majority element of the entire array.
Illustration:
Split the array into two halves: left half arr_left[] = {3, 4, 3, 2} and right half arr_right[] = {4,
4, 4, 4}.
Recursively find the majority elements of both halves.
Combine the majority elements obtained from both halves.
Check if the combined majority element appears more than n/2 times in the entire array.
Follow the steps below to solve the given problem using a recursive approach:
Define a recursive function findMajorityUtil that takes an array arr[], its lower index low, and
upper index high as input parameters.
If the array has only one element (low == high), return that element as the majority element.
Divide the array into two halves and recursively find the majority elements of both halves.
Combine the majority elements obtained from both halves.
Count the occurrences of the combined majority element in the entire array.
Return the combined majority element if it appears more than n/2 times; otherwise, return -1.
Follow the steps below to solve the given problem:
Define a function “countOccurrences” which takes an array “arr”, its size “n”, and an
integer “num” as input parameters. This function counts the number of occurrences of the
given integer in the array and returns the count. And the initial count is 0.
Then we’ll define a recursive function “findMajorityUtil” that takes an array, its
lower “low” and upper “high” indices as input parameters. This function has the following
steps:
Check if the array has only one element (base case). If yes, return that element.
Divide the array into two equal parts, left and right by finding the mid index.
Recursively find the majority element in the left part and right part.
If both the left and right parts have the same majority elements, then return that
element.
Count the total number of occurrences of the left and right majority elements in the
entire array.
Return the element that occurs more than n/2 times.
Define a function “findMajority” which takes an array “arr” and its size “n” as input
parameters. This function calls the “findMajorityUtil” function with the array and its indices
and prints the majority element if it exists; otherwise, it will print “No Majority Element”.
The “main” function will initialize an array “arr” and size “n”. It will call
the “findMajority” function with these parameters.
C++CJavaPython3C#Javascript
// No majority element
return -1;
}
// Driver Code
int main() {
int arr[] = {1, 3, 3, 3, 2};
int n = sizeof(arr) / sizeof(arr[0]);
findMajority(arr, n);
return 0;
}
Output
Iterate through each element in the array and count the occurrences of each bit position.
Reconstruct the majority element from the majority bits obtained.
Check if the reconstructed majority element appears more than n/2 times in the array.
Follow the steps below to solve the given problem:
Iterate through the array and count the occurrences of each bit position using bitwise
operations.
Reconstruct the majority element from the majority bits obtained.
Check if the reconstructed majority element appears more than n/2 times in the array.
#include <iostream>
#include <vector>
using namespace std;
int count_majority = 0;
for (int num : nums) {
if (num == majority_element)
count_majority++;
}
int main() {
vector<int> nums = {3, 3, 4, 2, 4, 4, 2, 4, 4};
int result = majorityElement(nums);
if (result != -1)
cout << "Majority Element: " << result << endl;
else
cout << "No Majority Element" << endl;
return 0;
}
Output:
Majority Element: 4
Time Complexity: O(n * k), where n is the number of elements in the array and k is the number
of bits in an integer.
Auxiliary Space: O(1), As no extra space is required.
A Simple Solution is to run two nested loops. The outer loop picks all elements one by one and
the inner loop counts the number of occurrences of the element picked by the outer loop.
Below is the implementation of the brute force approach :
C++JavaC#JavascriptPHPPython3
int count = 0;
// driver code
int main()
{
int arr[] = { 2, 3, 5, 4, 5, 2,
4, 3, 5, 2, 4, 4, 2 };
int n = sizeof(arr) / sizeof(arr[0]);
// Function calling
cout << getOddOccurrence(arr, n);
return 0;
}
Output :
5
// Driver code
int main()
{
int arr[] = { 2, 3, 5, 4, 5, 2, 4,
3, 5, 2, 4, 4, 2 };
int n = sizeof(arr) / sizeof(arr[0]);
// Function calling
cout << getOddOccurrence(arr, n);
return 0;
}
return res;
}
// Function calling
cout << getOddOccurrence(ar, n);
return 0;
}
Output :
5
Given an array arr[] of size N. The task is to find the sum of the contiguous subarray within
a arr[] with the largest sum.
The idea of Kadane’s algorithm is to maintain a variable max_ending_here that stores the
maximum sum contiguous subarray ending at current index and a variable max_so_far stores
the maximum sum of contiguous subarray found so far, Everytime there is a positive-sum value
in max_ending_here compare it with max_so_far and update max_so_far if it is greater
than max_so_far.
So the main Intuition behind Kadane’s Algorithm is,
The subarray with negative sum is discarded (by assigning max_ending_here = 0 in code).
We carry subarray till it gives positive sum.
Pseudocode of Kadane’s algorithm:
Initialize:
max_so_far = INT_MIN
max_ending_here = 0
Loop for each element of the array
(a) max_ending_here = max_ending_here + a[i]
(b) if(max_so_far < max_ending_here)
max_so_far = max_ending_here
(c) if(max_ending_here < 0)
max_ending_here = 0
return max_so_far
if (max_ending_here < 0)
max_ending_here = 0;
}
return max_so_far;
}
// Driver Code
int main()
{
int a[] = { -2, -3, 4, -1, -2, 1, 5, -3 };
int n = sizeof(a) / sizeof(a[0]);
// Function Call
int max_sum = maxSubArraySum(a, n);
cout << "Maximum contiguous sum is " << max_sum;
return 0;
}
Output
#include <climits>
#include <iostream>
using namespace std;
if (max_ending_here < 0) {
max_ending_here = 0;
s = i + 1;
}
}
cout << "Maximum contiguous sum is " << max_so_far
<< endl;
cout << "Starting index " << start << endl
<< "Ending index " << end << endl;
}
Output
Output
7
Practice Problem:
Given an array of integers (possibly some elements negative), write a C program to find out the
*maximum product* possible by multiplying ‘n’ consecutive integers in the array where n ?
ARRAY_SIZE. Also, print the starting point of the maximum product subarray.
Given an array arr[] of size N-1 with integers in the range of [1, N], the task is to find the
missing number from the first N integers.
Note: There are no duplicates in the list.
Examples:
Input: arr[] = {1, 2, 4, 6, 3, 7, 8}
Output: 5
Explanation: Here the size of the array is 8, so the range will be [1, 8]. The missing number
between 1 to 8 is 5
Input: arr[] = {1, 2, 3, 5}, N = 5
Output: 4
Explanation: Here the size of the array is 4, so the range will be [1, 5]. The missing number
between 1 to 5 is 4
#include <bits/stdc++.h>
using namespace std;
int ans;
for (i = 0; i <= N ; i++) {
if (temp[i] == 0)
ans = i + 1;
}
cout << ans;
}
/* Driver code */
int main()
{
int arr[] = { 1, 3, 7, 5, 6, 2 };
int n = sizeof(arr) / sizeof(arr[0]);
findMissing(arr, n);
}
Output
Approach 2 (Using summation of first N natural numbers): The idea behind the approach is
to use the summation of the first N numbers.
Find the sum of the numbers in the range [1, N] using the formula N * (N+1)/2. Now find the
sum of all the elements in the array and subtract it from the sum of the first N natural numbers.
This will give the value of the missing element.
Follow the steps mentioned below to implement the idea:
Calculate the sum of the first N natural numbers as sumtotal= N*(N+1)/2.
Traverse the array from start to end.
Find the sum of all the array elements.
Print the missing number as SumTotal – sum of array
Below is the implementation of the above approach:
C++CJavaPythonC#JavaScriptPHP
#include <bits/stdc++.h>
using namespace std;
// Driver Code
int main()
{
int arr[] = { 1, 2, 3, 5 };
int N = sizeof(arr) / sizeof(arr[0]);
// Function call
int miss = getMissingNo(arr, N);
cout << miss;
return 0;
}
Output
Modification for Overflow: The approach remains the same but there can be an overflow if N is
large.
In order to avoid integer overflow, pick one number from the range [1, N] and subtract a
number from the given array (don’t subtract the same number twice). This way there won’t be
any integer overflow.
Algorithm:
Create a variable total = 1 which will store the total sum of first n elements.
Traverse the array from start to end.
Update the value of total as total += i , now decrease value of total by current array
element.
Print the missing number , which will be present in the total variable.
Below is the implementation of the above approach:
C++CJavaPythonC#JavaScript
#include <bits/stdc++.h>
using namespace std;
// Driver Program
int main()
{
int arr[] = { 1, 2, 3, 5 };
int N = sizeof(arr) / sizeof(arr[0]);
// Function call
cout << getMissingNo(arr, N);
return 0;
}
Output
4
Time Complexity: O(N). Only one traversal of the array is needed.
Auxiliary Space: O(1). No extra space is needed
Approach 3 (Using binary operations): This method uses the technique of XOR to solve the
problem.
XOR has certain properties
Assume a1 ⊕ a2 ⊕ a3 ⊕ . . . ⊕ an = a and a1 ⊕ a2 ⊕ a3 ⊕ . . . ⊕ an-1 = b
Then a ⊕ b = an
Follow the steps mentioned below to implement the idea:
Create two variables a = 0 and b = 0
Run a loop from i = 1 to N:
For every index, update a as a = a ^ i
Now traverse the array from i = start to end.
For every index, update b as b = b ^ arr[i].
The missing number is a ^ b.
Below is the implementation of the above approach:
C++CJavaPythonC#JavaScriptPHP
class Main {
// Driver code
public static void main(String args[])
{
int arr[] = { 1, 2, 3, 5 };
int N = arr.length;
// Function call
int miss = getMissingNo(arr, N);
System.out.println(miss);
}
}
Output
#include <bits/stdc++.h>
using namespace std;
return n;
}
// Driver code
int main()
{
int arr[] = { 1, 2, 3, 5 };
int N = sizeof(arr) / sizeof(arr[0]);
// Function call
int miss = getMissingNo(arr, N);
cout << (miss);
return 0;
}
Output
4
Time Complexity: O(N), requires (N-1) comparisons
Auxiliary Complexity: O(1)
Approach 5 (Use elements as Index and mark the visited places as negative): Use the below
idea to get the approach
Traverse the array. While traversing, use the absolute value of every element as an index and
make the value at this index as negative to mark it visited. To find missing, traverse the array
again and look for a positive value.
Follow the steps to solve the problem:
Traverse the given array
If the absolute value of current element is greater than size of the array, then
continue.
else multiply the (absolute value of (current element) – 1)th index with -1.
Initialize a variable ans = size + 1.
Traverse the array and follow the steps:
if the value is positive assign ans = index + 1
Print ans as the missing value.
Below is the implementation of the above approach:
C++CJavaPythonC#JavaScript
#include <bits/stdc++.h>
using namespace std;
/* Driver code */
int main()
{
int arr[] = { 1, 3, 7, 5, 6, 2 };
int n = sizeof(arr) / sizeof(arr[0]);
findMissing(arr, n);
}
Output
4
Time Complexity: O(N)
Auxiliary Space: O(1)
Given a sorted and rotated array arr[] of size N and a key, the task is to find the key in the array.
Note: Find the element in O(logN) time and assume that all the elements are distinct.
Example:
Input : arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3}, key = 3
Output : Found at index 8
Input : arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3}, key = 30
Output : Not found
Input : arr[] = {30, 40, 50, 10, 20}, key = 10
Output : Found at index 3
Approach 1 (Finding Pivot where rotation has happened): The primary idea to solve the
problem is as follows.
The idea is to find the pivot point, divide the array into two sub-arrays and perform a binary
search.
The main idea for finding a pivot is –
For a sorted (in increasing order) and rotated array, the pivot element is the only element for
which the next element to it is smaller than it.
Using binary search based on the above idea, pivot can be found.
It can be observed that for a search space of indices in range [l, r] where the middle
index is mid,
If rotation has happened in the left half, then obviously the element at l will
be greater than the one at mid.
Otherwise the left half will be sorted but the element at mid will be greater
than the one at r.
After the pivot is found divide the array into two sub-arrays.
Now the individual sub-arrays are sorted so the element can be searched using Binary Search.
Follow the steps mentioned below to implement the idea:
Find out the pivot point using binary search. We will set the low pointer as the first array index
and high with the last array index.
From the high and low we will calculate the mid value.
If the value at mid-1 is greater than the one at mid, return that value as the pivot.
Else if the value at the mid+1 is less than mid, return mid value as the pivot.
Otherwise, if the value at low position is greater than mid position, consider the left
half. Otherwise, consider the right half.
Divide the array into two sub-arrays based on the pivot that was found.
Now call binary search for one of the two sub-arrays.
If the element is greater than the 0th element then search in the left array
Else search in the right array.
If the element is found in the selected sub-array then return the index
Else return -1.
Follow the below illustration for a better understanding
Illustration:
Consider arr[] = {3, 4, 5, 1, 2}, key = 1
Pivot finding:
low = 0, high = 4:
=> mid = 2
=> arr[mid] = 5, arr[mid + 1] = 1
=> arr[mid] > arr[mid +1],
=> Therefore the pivot = mid = 2
Array is divided into two parts {3, 4, 5}, {1, 2}
Now according to the conditions and the key, we need to find in the part {1, 2}
Key Finding:
We will apply Binary search on {1, 2}.
low = 3 , high = 4.
=> mid = 3
=> arr[mid] = 1 , key = 1, hence arr[mid] = key matches.
=> The required index = mid = 3
So the element is found at index 3.
Below is the implementation of the above approach:
C++
C
Java
Python3
C#
Javascript
PHP
/* Java program to search an element
in a sorted and pivoted array*/
import java.io.*;
class Main {
// main function
public static void main(String args[])
{
// Let us search 3 in below array
int arr1[] = { 5, 6, 7, 8, 9, 10, 1, 2, 3 };
int n = arr1.length;
int key = 3;
System.out.println(
"Index of the element is : "
+ pivotedBinarySearch(arr1, n, key));
}
}
Output
Time Complexity: O(log N) Binary Search requires log n comparisons to find the element.
Auxiliary Complexity: O(1)
Thanks to Ajay Mishra for providing the above solution.
class Main {
// Returns index of key in arr[l..h]
// if key is present, otherwise returns -1
static int search(int arr[], int l, int h, int key)
{
if (l > h)
return -1;
int mid = (l + h) / 2;
if (arr[mid] == key)
return mid;
// main function
public static void main(String args[])
{
int arr[] = { 4, 5, 6, 7, 8, 9, 1, 2, 3 };
int n = arr.length;
int key = 3;
int i = search(arr, 0, n - 1, key);
if (i != -1)
System.out.println("Index: " + i);
else
System.out.println("Key not found");
}
}
Index: 8
Time Complexity: O(log N). Binary Search requires log n comparisons to find the element. So
time complexity is O(log n).
Auxiliary Space: O(1). As no extra space is required.
Thanks to Gaurav Ahirwar for suggesting the above solution.
import java.io.*;
Time complexity of this algorithm is O(n), where n is the length of the input array.
Space complexity is O(1), as the program only uses a constant amount of extra space to store the
loop index and the return value.
There are two sorted arrays. First one is of size m+n containing only m elements. Another one is
of size n and contains n elements. Merge these two arrays into the first array of size m+n such
that the output is sorted.
Input: array with m+n elements (mPlusN[]).
NA => Value is not filled/available in array mPlusN[]. There should be n such array blocks.
Input: array with n elements (N[]).
Output: N[] merged into mPlusN[] (Modified mPlusN[])
Algorithm:
Let first array be mPlusN[] and other array be N[]
1) Move m elements of mPlusN[] to end.
2) Start from nth element of mPlusN[] and 0th
element of N[] and merge them into mPlusN[].
Below is the implementation of the above algorithm :
C++
C
Java
Python3
C#
PHP
Javascript
class MergeArrays {
/* Function to move m
elements at the end of array
* mPlusN[] */
void moveToEnd(int mPlusN[], int size)
{
int i, j = size - 1;
for (i = size - 1; i >= 0; i--) {
if (mPlusN[i] != -1) {
mPlusN[j] = mPlusN[i];
j--;
}
}
}
System.out.println("");
}
// Driver Code
public static void main(String[] args)
{
MergeArrays mergearray = new MergeArrays();
/* Initialize arrays */
int mPlusN[] = { 2, 8, -1, -1, -1, 13, -1, 15, 20 };
int N[] = { 5, 7, 9, 25 };
int n = N.length;
int m = mPlusN.length - n;
Given two sorted arrays, a[] and b[], the task is to find the median of these sorted arrays,
where N is the number of elements in the first array, and M is the number of elements in the
second array.
This is an extension of Median of two sorted arrays of equal size problem. Here we handle
arrays of unequal size also.
Examples:
Input: a[] = {-5, 3, 6, 12, 15}, b[] = {-12, -10, -6, -3, 4, 10}
Output: The median is 3.
Explanation: The merged array is: ar3[] = {-12, -10, -6, -5 , -3, 3, 4, 6, 10, 12, 15}.
So the median of the merged array is 3
Input: a[] = {2, 3, 5, 8}, b[] = {10, 12, 14, 16, 18, 20}
Output: The median is 11.
Explanation : The merged array is: ar3[] = {2, 3, 5, 8, 10, 12, 14, 16, 18, 20}
If the number of the elements are even. So there are two middle elements.
Take the average between the two: (10 + 12) / 2 = 11.
int ans = (e + q) / 2;
return ans;
}
// Driver Code
public static void main(String[] args)
{
int[] arr1 = { -5, 3, 6, 12, 15 };
int[] arr2 = { -12, -10, -6, -3, 4, 10 };
int i = arr1.length;
int j = arr2.length;
Time Complexity: O((N + M) * Log (N + M)), Time required to sort the array of size N + M
Auxiliary Space: O(N + M), Creating a new array of size N+M.
Median of two sorted arrays of different sizes by Merging Arrays efficiently:
The given arrays are sorted, so merge the sorted arrays in an efficient way and keep the count of
elements inserted in the output array or printed form. So when the elements in the output array
are half the original size of the given array print the element as a median element. There are two
cases:
Case 1: M+N is odd, the median is at (M+N)/2th index in the array obtained after merging
both the arrays.
Case 2: M+N is even, the median will be the average of elements at index ((M+N)/2 – 1) and
(M+N)/2 in the array obtained after merging both the arrays
Illustration:
Given two array ar1[ ]= { 900 } and ar2[ ] = { 5, 8, 10, 20 } , n => Size of ar1 = 1 and m
=> Size of ar2 = 4
Loop will run from 0 till 2.
First iteration : { 900 } { 5, 8, 10, 20 } , m1 = 5
Second iteration : { 900 } { 5, 8, 10, 20 }, m1 = 8
Third iteration : { 900 } { 5, 8, 10, 20 }, m1 = 10
As size of ar1 + ar2 = odd , hence we return m1 = 10 as the median
import java.io.*;
class GFG {
// Driver code
public static void main(String[] args)
{
int ar1[] = { 900 };
int ar2[] = { 5, 8, 10, 20 };
int n1 = ar1.length;
int n2 = ar2.length;
Time Complexity: O(M + N). To merge both arrays O(M+N) time is needed.
Auxiliary Space: O(1). No extra space is required.
Median of two sorted arrays of different sizes using Binary Search:
The given two arrays are sorted, so we can utilize the ability of Binary Search to divide the
array and find the median.
Median means the point at which the whole array is divided into two parts. Hence since the two
arrays are not merged so to get the median we require merging which is costly.
Hence instead of merging, we will use a modified binary search algorithm to efficiently find the
median.
Below is the implementation:
C++
Java
Python3
C#
Javascript
public class GFG {
int start = 0;
int end = n;
int realmidinmergedarray = (n + m + 1) / 2;
// Driver code
public static void main(String[] args)
{
int[] arr1 = { -5, 3, 6, 12, 15 };
int[] arr2 = { -12, -10, -6, -3, 4, 10 };
System.out.println("Median of the two arrays are");
System.out.println(Median(arr1, arr2));
}
}
Time Complexity: O(min(log M, log N)): Since binary search is being applied on the smaller of
the 2 arrays
Auxiliary Space: O(1)
Median of two sorted arrays of different sizes using Priority Queue:
In this Approach we have used Priority Queue (min Heap) to find out the median.
The Idea is simple, just push the elements into a single Priority Queue from both arrays. Now we
have to find median from priority queue by performing a simple traversal through it upto
median.
Illustration:
A[ ] = {-2, 3, 4, 5}, N = 4 & B[ ] = {-4, -1, 7, 8, 9}, M = 5
Step 1: Adding elements to priority queue(pq) from array A
pq.push(-2)
pq.push(3)
pq.push(4)
pq.push(5)
After adding array A elements to priority queue it will look as pq = {-2, 3, 4, 5}
Step 2: Adding elements to priority queue(pq) from array B
pq.push(-4)
pq.push(-1)
pq.push(7)
pq.push(8)
pq.push(9)
After adding array B elements to priority queue it will look as pq = {-4, -2, -1, 3, 4, 5, 7, 8, 9}
Step 3: Now we have to find median from Priority Queue based on following conditions:
if N+M is odd
then traverse priority queue upto (n+m)/2 by popping element by element
Then display median as pq.top()
if N+M is even
then traverse priority queue upto (n+m)/2 && ((n+m)/2)-1
Then median = average of both top values of priority queue
In this case the median is 4
Below is the implementation of the above problem:
C++
Java
Python3
C#
Javascript
import java.util.PriorityQueue;
import java.util.Scanner;
import java.util.Vector;
// Driver code
public static void main(String[] args)
{
Vector<Integer> arr1 = new Vector<Integer>();
arr1.add(-2);
arr1.add(3);
arr1.add(4);
arr1.add(5);
Time Complexity: O((n + m) log(n + m)), Since the priority queue is implemented from two
arrays
Auxiliary Space: O(N+M): for storing two array values in the priority queue.
2. Matrix:
A matrix is a two-dimensional array of elements, arranged in rows and columns. It is represented
as a rectangular grid, with each element at the intersection of a row and column.
Important articles on Matrix:
Search in a row wise and column wise sorted matrix
Print a given matrix in spiral form
A Boolean Matrix Question
Print unique rows in a given boolean matrix
Maximum size square sub-matrix with all 1s
Print unique rows in a given boolean matrix
Inplace M x N size matrix transpose | Updated
Dynamic Programming | Set 27 (Maximum sum rectangle in a 2D matrix)
Strassen’s Matrix Multiplication
Create a matrix with alternating rectangles of O and X
Print all elements in sorted order from row and column wise sorted matrix
Given an n x n square matrix, find sum of all sub-squares of size k x k
Count number of islands where every island is row-wise and column-wise separated
Find a common element in all rows of a given row-wise sorted matrix
3. Linked List:
A linear data structure where elements are stored in nodes linked together by pointers. Each node
contains the data and a pointer to the next node in the list. Linked lists are efficient
for inserting and deleting elements, but they can be slower for accessing elements than arrays.
b) Circular Linked List: The last node points back to the first node, forming a circular loop.
Important articles on Circular Linked List:
Circular Linked List Introduction and Applications,
Circular Singly Linked List Insertion
Circular Linked List Traversal
Split a Circular Linked List into two halves
Sorted insert for circular linked list
c) Doubly Linked List: Each node points to both the next and previous nodes in the list.
Important articles on Doubly Linked List:
Doubly Linked List Introduction and Insertion
Delete a node in a Doubly Linked List
Reverse a Doubly Linked List
The Great Tree-List Recursion Problem.
QuickSort on Doubly Linked List
Merge Sort for Doubly Linked List
4. Stack:
Stack is a linear data structure that follows a particular order in which the operations are
performed. The order may be LIFO(Last In First Out) or FILO(First In Last
Out). LIFO implies that the element that is inserted last, comes out first and FILO implies that the
element that is inserted first, comes out last.
Important articles on Stack:
Introduction to Stack
Infix to Postfix Conversion using Stack
Evaluation of Postfix Expression
Reverse a String using Stack
Implement two stacks in an array
Check for balanced parentheses in an expression
Next Greater Element
Reverse a stack using recursion
Sort a stack using recursion
The Stock Span Problem
Design and Implement Special Stack Data Structure
Implement Stack using Queues
Design a stack with operations on middle element
How to efficiently implement k stacks in a single array?
Sort a stack using recursion
5. Queue:
A Queue Data Structure is a fundamental concept in computer science used for storing and
managing data in a specific order. It follows the principle of “First in, First out” (FIFO), where
the first element added to the queue is the first one to be removed
6. Binary Tree:
Binary Tree is a hierarchical data structure where each node has at most two child nodes, referred
to as the left child and the right child. Binary trees are mostly used to represent hierarchical data,
such as file systems or family trees.
8. Heap:
A Heap is a complete binary tree data structure that satisfies the heap property: for every node, the
value of its children is less than or equal to its own value. Heaps are usually used to
implement priority queues, where the smallest (or largest) element is always at the root of the
tree.
10. Graph:
Graph is a collection of nodes connected by edges. Graphs are mostly used to represent networks,
such as social networks or transportation networks.