Data Structure Basic
Data Structure Basic
--------------------------------------------------------------------------------
Time Complexity
Time complexity can NOT be defined as actual time taken by the program. Actual running time depends on many external factors:
Hardware speed (old vs new machine)
CPU architecture
Memory and cache
Compiler optimizations
Programming language implementation
Operating system load
Because of these variables, the same program may run 1 second on a fast machine, and 5 seconds on a slower machine. So,
measuring real time in seconds is unreliable for algorithm analysis.
Time Complexity measures how the number of operations grows as input size increases. It focuses on growth of operations relative
to input size (n).
Operations grow proportional to n, so the time complexity is O(n). This means if input doubles, operations roughly double.
It’s expressed in Big O Notation (O) to show the upper bound of growth rate — i.e., worst-case scenario.
Common Time Complexities
Before solving particular problem I advice to think about two questions for a few minutes:
bool answer = is it possible to apply HASH TABLE or MAP ?;
if(answer == true){
//Think: HOW YOU CAN APPLY ?
}
Helps in finding fast and easy solution for a problem.
HASH TABLE and MAP are the types of containers which store data with key-value combination. It means that particular data will
have an special key and the value associated (mapped value) with this key.
<key, value>
For solving most of the problems knowing <key, value> combiantion is really enough.
In C++, the unordered_map<> is used to implement a hash table, while map<> is implemented as a balanced binary search tree.
So, if you need the keys to be sorted, use TreeMap. If you just need fast access and insertion, use HashMap.
Star Pattern 1
Logic:
As the pattern contains equal number of rows and columns, so we need to take the input for row only, that can be used for column also.
As we need to only print stars, simply iterate through each row and column, and print star there, using nested for loops.
TC: O(N²), since we print N stars for each of the N rows.
SC: O(1), no additional space is used apart from loop variables.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Star Pattern 2
Logic:
Take an input for row.
Iterate through each row using outer for loop, and print stars as the current row index.
TC: O(N²), since we print N stars for each of the N rows.
SC: O(1), no additional space is used apart from loop variables.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Star Pattern 3
Algorithm:
Take an input size.
To iterate through the row: 1 to size.
To iterate through column: for blank spaces (size-row), and for stars (=row)
TC: O(N²)
SC: O(1), no additional space is used apart from loop variables.
for(int i=1; i<=size; i++) {
for(int j=size-i; j>=1; j--) {
[Link](" ");
}
for(int j=1; j<=i; j++) {
[Link]("*");
}
[Link]();
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Floyd’s Triangle
1
23
456
7 8 9 10
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Binary Triangle
1
01
101
0101
Logic 1: If you look at the 0’s position in a triangle formation, you will find out that the indexes i and j are either (even,odd) or (odd,even), but
not (odd,odd) or (even,even).
(1,1)
(2,1) (2.2)
(3,1) (3,2) (3,3)
(4,1) (4,2) (4,3) (4,4)
Logic 2: If you look at position of 0’s and 1’s you will find out that the (i+j) of 1’s are even, whereas (i+j) of 0’s are odd.
1
01
101
0101
10101
(1,1)
(2,1) (2.2)
(3,1) (3,2) (3,3)
(4,1) (4,2) (4,3) (4,4)
(5,1) (5,2) (5,3) (5,4) (5,5)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
13
135
1357
Logic 1: As we can see, on each row we have a series of odd numbers starting from 1. Here, we are going to use an extra variable ‘odd’ to start
the odd number from 1, and then we are going to increment odd number by 2, at each step.
Logic 2:
public class PatternPrinting {
public static void main(String[] args) {
Scanner sc = new Scanner([Link]);
int row = [Link]();
for(int i=1; i<=row; i++) {
for(int j=1 ; j<=i; j++) {
[Link](2*j-1+" ");
}
[Link]();
}
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*
**
***
****
Logic 2:
1234
1 *
2 **
3 ***
4****
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
12
123
1234
Logic 1:
Logic 2:
public class PatternPrinting {
public static void main(String[] args) {
Scanner sc = new Scanner([Link]);
int row = [Link]();
for(int i=1; i<=row; i++) {
for(int j=1 ; j<=row; j++) {
if((i+j)>row) [Link](j+" ");
else [Link](" ");
}
[Link]();
}
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
A
BB
CCC
DDDD
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Rhombus
****
****
****
****
Logic 1:
****
****
****
****
Logic 2:
* * * * → 3 spaces & 4 stars
* * * * → 2 spaces & 4 stars
* * * * → 1 space & 4 stars
* * * * → 0 space & 4 stars
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
****
***
**
*
Logic 1:
* * * * → 0 spaces 4 stars
* * * → 1 spaces 3 stars
* * → 2 spaces 2 stars
* → 3 spaces 1 stars
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Pyramid
*
***
*****
*******
Logic 1:
* → 3 spaces 1 stars
* * * → 2 spaces 3 stars
* * * * * → 1 spaces 5 stars
* * * * * * * → 0 spaces 7 stars
1234567
1 * → 3 spaces 1 star
2 * * * → 2 spaces 3 star
3 * * * * * → 1 space 5 star
4 * * * * * * * → 0 space 7 star
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Number Pyramid
1
123
12345
1234567
Logic:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Diamond
*
***
*****
*******
*****
***
*
Logic:
Diamond is printed in two halves.
First half: spaces decrease, stars increase by 2.
Second half: spaces increase, stars decrease by 2.
Before second half, you need to set the nsp and nst as per the second half initial row.
4 rows
3 spaces 1 star
*
23
***
15
*****
07
*******
-1 9
0 7 (eliminate this)
*******
15
*****
23
***
31
*
4
31
*
23
***
15
*****
07
*******
15
*****
23
***
31
*
[Link]();
}
}
}
Logic 2:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bridge
*********
**** ****
*** ***
** **
* *
Logic:
The above pattern displayed for row=5.
To display the following code, we first make a separate for loop for the 1st row.
*********
**** ****
*** ***
** **
* *
****_****
***___***
**_____**
*_______*
package Pattern_Printing;
import [Link];
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Iterate an row with outer loop
Iterate inner loop till (TotalRow + 1) - i.
Print star.
Iterate two separate inner loops for spaces.
Iterate one more inner loop for other side of star.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
Looking at the total row count (ie., N) and the figure. It is clearly visible that we need to perform two separate outer loop. First part would
contain first 5 rows and next half would contain next 4 rows (or remaining rows).
For the first half, we need to divide it into 4 parts. One part would contain stars , other two part would contain spaces, and last part
would contain stars.
Based on the number of stars and spaces, iterate the inner loop till that index.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
It is visible that they have not iterated till 5th index, but one less than 5.
As this figure can be seen as unique pattern, so need to look at its stars position, in order to get an idea of solving approach.
(1,1) (1,2) (1,3) (1,4)
(2,1) (2,4)
(3,1) (3,4)
(4,1) (4,2) (4,3) (4,4)
We can clearly that the position of stars must have either 1 or 4 at outer or inner index.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1___ ___1
12__ __21
123_ _321
1234 4321
Logic:
Iterate outer loop through row.
Iterate inner loop for numbers as per row count.
Print column values (or loop values).
Iterate inner loop for spaces as (TotalRow - CurrentRow).
Iterate inner loop for spaces as (TotalRow - CurrentRow).
Iterate inner loop for numbers as reverse number from row count to 1.
Print loop values.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
Iterate through row.
While iterating column: consider the current row.
Initialize num variable with 1.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 2 3 4 5 6 7 → ith index
1→ 1111
2 1222
3 1233
4 1234
for(int i=1; i<=row; i++) {
for(int j=1; j<=row; j++) {
[Link]([Link](i,j)+" ");
// [Link](((i<j) ? i : j)+" "); // Ternary operation
// if(i<j) [Link](i+" ");
// else [Link](j+" ");
}
[Link]();
}
5 6 7 → actual jth index
3 2 1 → fake jth index (in reverse order)
1 111
2 221
3 321
4 321
// Concept of fake values
for(int i=1; i<=row; i++) {
for(int j=row-1; j>=1; j--) { //this will treat 5th, 6th, and 7th column indexes as 1st, 2nd, and 3rd
[Link]([Link](i,j)+" ");
}
[Link]();
}
1 2 3 4 5 6 7 → actual index
1 2 3 3 2 1 → jth index
53→ 1233321
62 1222221
71 1111111
for(int i=row-1; i>=1; i--) {
for(int j=1; j<=row-1; j++) {
[Link]([Link](i,j)+" ");
}
for(int j=1;j<=1; j++) {
[Link](i+" ");
}
for(int j=row-1; j>=1; j--) {
[Link]([Link](i,j)+" ");
}
[Link]();
}
Logic 2:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
If we look at this first part, we can see the number is in decreasing order, which indicates that we need to use indexing in reverse order.
In first part we can see one pattern, while comparing row with column, the larger index number would get printed.
If we look at the second part , we can see the number is now in increasing order, we just need to find the order of index for both row and
column. [the idea of finding the index number for both row and column. Check the maximum occurring number in that line.] [Here, index
for outer loop would remain same. As it is of the same row.]
For the remaining part, the column index would remain same, we just need to find the order of both part of remaining part. [No change
in column. And for row, think of the maximum occurring number in that line.]
The third part or the first part of remaining part, the index of row would be based on logic that the maximum occurring number in that
line.
The fourth part row index, would be based on same logic. Max occurring number would be the row index, and the larger value comparing
row and column would get printed.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Java Collections
Java Collections Framework (JCF): The JCF is part of the [Link] package and provides a set of ready-to-use classes and interfaces for
storing and manipulating groups of objects. Collections are dynamic — meaning they can grow or shrink in size and they come with built-
in methods for common operations.
The Java Collections Framework isn’t just a random set of classes it’s an organized hierarchy. At the top sits the Iterable interface, which
allows objects to be looped through using the enhanced for-each loop. Then comes the Collection interface, which represents a group
of elements and defines basic operations like adding, removing, checking size, and clearing elements.
From Collection, the hierarchy splits into three main branches:
List : ordered collections that can have duplicate elements.
Set : unordered collections that do not allow duplicates.
Queue : collections designed to hold elements before processing.
Alongside these is the Map interface, which isn’t technically a subtype of Collection but is still part of the framework. A Map
stores data in key-value pairs, ensuring that each key is unique.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Basic maths
Logic:
Perform while condition, asking to accept only number greater than 0, in order to count the digits.
Increase the counter by 1, as the while condition approves.
And eliminate the last digit of a number, in order to count.
int count = 0;
while(num>0) {
count++;
num = num/10;
}
[Link]("Number of digits in a number: "+count);
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
Take a while loop , in order to take a input as a valid number.
As it goes into while loop, trim the last digit from the number and consider it as a reverse.
The number except the last number would be the new number, which will go into while loop again till we get the reverse of the given
number.
int rev = 0;
while(num>0) {
int temp=num%10;
rev = rev*10+temp;
num = num/10;
}
[Link](rev);
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
Perform the same operation as reverse .
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
Logic: (Euclidean Algorithm)
The Euclidean Algorithm finds the GCD (Greatest Common Divisor) of two numbers based on the principle that subtracting the smaller
number from the larger number does not change their GCD. In the program, two numbers n1 and n2 are taken as input. A loop runs until one
of the numbers becomes 0. Inside the loop, the larger number is reduced by subtracting the smaller number (n1 = n1 - n2 or n2 = n2 - n1).
This process repeatedly reduces the values while preserving the same GCD. Eventually, one of the numbers becomes 0, and the remaining
non-zero number is the GCD of the original numbers. This method works because common divisors remain unchanged through repeated
subtraction.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Logic:
This program checks whether a given number is an Armstrong number. In the main() method, the user enters a number using Scanner. The
program then calls the armstrong(num) method and compares its returned value with the original number. If both values are equal, the
program prints true, meaning the number is an Armstrong number; otherwise it prints false.
The armstrong(int num) method performs the main calculation. First, it finds the number of digits in the number by calling countDigits(num),
which determines the power to raise each digit. Then the program extracts each digit using num % 10, raises it to that power using [Link](),
and adds it to result. After processing each digit, the number is reduced using num = num / 10 until all digits are processed.
The countDigits(int num) method simply counts how many digits the number has by repeatedly dividing it by 10 until it becomes zero. For
example, if the input is 153, the program calculates 13+53+33=153. Since the calculated value equals the original number, it is an Armstrong
number.
}
public static int armstrong(int num) {
int power = countDigits(num);
int result = 0;
while(num>0) {
result += (int) [Link]((num%10), power);
num = num/10;
}
return result;
}
public static int countDigits(int num) {
int count = 0;
while(num>0) {
num = num/10;
count++;
}
return count;
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Algorithm:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Algorithm:
First, a variable count is initialized to 0.
A loop runs from 1 to num, and in each iteration it checks if num is divisible by i using num % i == 0. If the remainder is 0, it means i is a
divisor of num, so count is increased.
After the loop finishes, the program checks the value of count. A prime number has exactly two divisors: 1 and itself. Therefore, if count
== 2, the number is prime and "True" is printed. Otherwise, it means the number has more than two divisors and is composite, so "False"
is printed.
int count=0; // to track the prime number, if count crosses 2 then it is composite number
for(int i=1; i<=num; i++) {
if(num%i==0) count++;
}
if(count==2) {
[Link]("True");
} else {
[Link]("False");
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Binary Search
[Link]
s
Recursion
The base condition is the stopping point of a recursive function. It tells the function when to stop calling itself, so that it doesn’t go
on forever.
Without a base condition, a recursive function will call itself infinitely and cause a StackOverflowError (in Java).
The base condition (if (n == 0 || n == 1)) stopped recursion and started returning results back.
A recursion tree is a diagram that shows how recursive function calls branch out — like a tree with the root as the first function call,
and its children as the recursive calls it makes.
◦ It helps you visualize:
▪ How many function calls are made.
▪ How values are combined.
▪ How recursion depth increases and decreases.
▪ The flow of execution (top-down and backtracking).
Print N to 1
/*
OUTPUT
5
4
3
2
1
*/
import [Link];
public class Variables {
public static void main(String[] args) {
int x=10; // x is local to main()
change(x);
[Link](x); // 10
}
public static void change(int x) {
[Link](x); // 10
x=20;
[Link](x); // 20
}
}
In Java, method arguments are passed by value, not by reference. That means a copy of the value 10 is sent to the method — not the actual
variable from main.
Inside change(int x):
◦ The local variable x inside change() is a copy of the one in main().
◦ Initially, it prints 10.
◦ Then you reassign x = 20; → this changes only the local copy, not the one in main(). Prints 20.
◦ When the method finishes, its x variable is destroyed. The original x in main() is still 10.
import [Link];
/*
OUTPUT
i/p: 30
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
*/
import [Link];
/*
OUTPUT
i/p: 30
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
*/
/*
OUTPUT
i/p: 5
5432112345
*/
/*
OUTPUT
i/p: 5
543212345
*/
The if (n != 1) condition avoids printing 1 twice — because when n == 1, it prints 1 before recursion, and there’s no need to print it
again after returning.
Factorial no.
/*
OUTPUT
i/p: 5
Factorial of 5 is: 120
*/
/*
OUTPUT
i/p: 6
Factorial of 6 is: 720
*/
/*
OUTPUT
i/p: 5
Factorial of 5 is: 120
*/
‘a’ raised to the power ‘b’
int ans=1;
for(int i=1; i<=b; i++) {
ans *= a;
}
// TC: O(b)
ab = a * ab-1
264 → 2*263 → 2*262 → 2*261 → 2*260 ………………………..2*20 (N number of calls)
pow(a,b) = a* pow(a,b-1);
[Link](3,4) = 34 = 81.0
/*
OUTPUT
Enter base: 2
Enter exponent: 3
2 raised to the power 3 is: 8
*/
/*
OUTPUT
Enter base: 2
Enter exponent: 3
2 raised to the power 3 is: 8
*/
The inefficiency lies here: pow(a, b/2) * pow(a, b/2)
You’re calling pow(a, b/2) twice → same computation is repeated unnecessarily.
For eg., to compute pow(2, 8), it calls pow(2, 4) two times, and each of those again calls pow(2, 2) twice, etc. That makes the total
number of recursive call exponential – roughly O(2log b)=O(b) again. So this loses the advantage of fast exponentiation.
/*
OUTPUT
Enter base: 2
Enter exponent: 3
2 raised to the power 3 is: 8
*/
TC: O(log b), SC: O(log b)
Your version divides the exponent by 2 each time:int call = pow(a,b/2);
That means recursion depth is proportional to log₂(b).
POWER OF NUMBERS
Given a number n, find the value of n raised to the power of its own reverse.
class Solution {
public int reverseExponentiation(int n) {
// code here
int reverse=reverseNumber(n, 0);
return (int)[Link](n, reverse); // [Link] returns double; Calculate n^reverse
}
public static int reverseNumber(int n, int rev) { // recursive approach to reverse a number
if(n==0) return rev;
int digit=n%10; // extract last digit
return reverseNumber(n/10, rev*10+digit); // removing last digit, and adding digit to reversed number
}
}
TC = O(digits) recursive calls. Since the number of digits d = log₁₀(n), we can also write: O(log₁₀ n) = O(log n)
Recursive reverse takes O(log n) time/space (digits of n), and [Link] adds another O(log n) time.
s
--------------------------------------------------------------------------------
Array
Every array in memory has a base address, which is the memory location of the first element (index 0).
◦ The offset is the distance (in bytes) from his base address to another element in the array.
◦ Offset is calculated as: Offset = (index) x (size of each element)
◦ Address of ith element can be found using: Address of A[i] = Base address of A + (I x size of each element)
when you access arr[3], the system adds an offset of 12 bytes to the base address to reach that element.
Array is a fundamental data structure and used to implement other data structure like stack, queue, dequeue and heap.
Declaration of Array:
int arr[]; → This array will store integer type element
char arr[]; → This array will store char type element
float arr[]; → This array will store float type element
Initialization of Array:
int arr[] = { 1, 2, 3, 4, 5 };
char arr[] = { 'a', 'b', 'c', 'd', 'e' };
float arr[] = { 1.4f, 2.0f, 24f, 5.0f, 0.0f };
Array Operations:
◦ Accessing: O(1)
◦ Insert: O(1) / O(n)
◦ Delete: 0(1) / O(n)
◦ Traverse: O(n)
◦ Search: linear O(n) / binary O(log n)
◦ Update: O(1)
public class ArrayBasics {
public static void main(String[] args) {
// int num1 = 23;
// int num2 = 43;
// int num3 = 59;
int[] nums = {23, 43, 59, 71, 93, 20}; // initialized array
int size = [Link]; // Size of an array
// Indexing
[Link]("First element: "+nums[0]);
[Link]("Last element: "+nums[size-1]);
// Display elements
for(int i=0; i<size; i++) {
[Link](nums[i]+" ");
}
[Link]();
Benefits:
◦ random access O(1): each element can be accessed directly using its index.
◦ cache friendly performance: Arrays are stored contiguously in memory, CPUs can prefetch nearby elements – making traversal
faster. This is why arrays outperform linked lists in iteration-heavy tasks.
◦ ease of sorting: Arrays have comntiguous memory and index-based access, which makes them very fast to sort. No pointer
chasing like in linked lists.
◦ implement other data structure: Arrays from building blocks of many higher-level data structures.
◦ many coding patterns
Limitations:
◦ fixed size
◦ worst-case O(n) – insert, delete
◦ inefficient for frequent modifications
Multi-dimensional arrays
When you pass an integer, the value of num (ie., 12) is copied. The change() method works on a seperate copy, and original variable in
main() is untouched. That’s why change is NOT reflected.
Primitive value is passed → copy is modified → original unchanged
public class Integer2Method {
public static void main(String[] args) {
int num = 12;
[Link](num); // 12
changeNum(num);
[Link](num); // 12
}
public static void changeNum(int num) {
num = 100;
}
}
// DEEP COPY
int[] arr2 = {11, 22, 33, 44};
// int[] y = [Link](arr2, [Link]); // using [Link]()
int[] y = [Link](); // new array 'y' is created; data is copied element by element
y[0] = 111;
[Link](arr[0]); // 11
}
}
Memory view
STACK HEAP
----- ----
arr ───────────▶ [10, 20]
x ───────────▶ (both point to same heap object,
because only one array exists in heap)
(change in one ‘arr’ affects the other reference variable ‘x’ object)
Object Example:
Student s1 = new Student();
Student s2 = s1; // shallow copy
STACK HEAP
----- ----
s1 ───────────▶ Student{id=10}
s2 ───────────▶ (same heap object)
▪ Deep Copy
Copies actaul data.
New object created.
Both pbjects are independent.
int[] arr = {10, 20};
int[] x = [Link]();
Memory view
STACK HEAP
----- ----
arr ───────────▶ [10, 20]
x ───────────▶ [10, 20]
(separate copy; two arrays exist in heap
arr and x point to different memory blocks.)
Object Example:
Student s1 = new Student();
Student s2 = new Student(s1);
STACK HEAP
----- ----
s1 ───────────▶ Student{id=10}
s2 ───────────▶ Student{id=10}
(seperate objects; no shared memory)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int sum=0;
for(int ele:arr) {
sum+=ele;
}
[Link]("Sum of all elements: "+sum);
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Largest element
Algorithm:
Sort an array.
Return the last element of an array.
TC: O(n logn)
SC: O(1)
Algorithm:
Consider first element as max.
Iterate through other remaining elements, and check if any of those elements is greater than max or not, if yes, then update max value.
TC: O(n)
SC: O(1)
Algorithm:
Take a new variable and assign minimum value to it.
Iterate through each element one by one, and check whether that element is greater than new variable or not. If the element appears
to be larger, then consider it as a maximum element.
TC: O(n), iterating through each element.
SC: O(1), extra variable required to store maximum element.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Algorithm:
Sort an array.
Iterate through an array from the last second element, till first element (in reverse order).
While iterating compare current element with the element before it (second last element with last element), and if they are not equal,
return iterated element.
TC: O(n) You perform a single reverse traversal of the array in the worst case (if all elements except the last are the same).
SC: O(1)
class Solution {
public int getSecondLargest(int[] arr) {
int size = [Link];
[Link](arr);
for(int i=size-2; i>=0; i--) {
if(arr[i] != arr[i+1]) return arr[i];
}
return -1;
}
}
Algorithm:
The algorithm relies on the fact that the array is already sorted (if not, then sort an array first).
The largest element is taken directly from the last index of the sorted array.
To find the second largest distinct value, the array is scanned backwards starting from the second-last element.
As soon as a value different from the largest is found, it is considered the second largest.
The search stops immediately once this value is detected, ensuring an efficient lookup.
This approach handles cases with duplicate values and negative numbers correctly.
TC: O(n) You perform a single reverse traversal of the array in the worst case (if all elements except the last are the same).
SC: O(1) Only a few extra variables (largest, secondLargest) are used, regardless of array size.
Algorithm:
1. Start by assuming the first element of the array is the largest, and initialize the second largest to the minimum possible value.
2. Loop through the array beginning from the second element (index 1).
3. If the current element is greater than the largest, then update the second largest to the old largest value, and set the current element
as the new largest.
4. Otherwise, if the current element is smaller than the largest but bigger than the second largest, update the second largest to the
current element.
return secondLargest;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Alternating groups I
class Solution {
public int numberOfAlternatingGroups(int[] colors) {
int count=0;
int size = [Link];
for(int i=0; i<size; i++) {
if(i==0) {
if((colors[size-1]!=colors[i]) && (colors[i]!=colors[i+1])) count++;
} else if(i!=size-1) {
if((colors[i-1]!=colors[i]) && (colors[i]!=colors[i+1])) count++;
} else {
if((colors[i-1]!=colors[i]) && (colors[i]!=colors[0])) count++;
}
}
return count;
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Algorithm
1. Insert all array elements into a set so that only unique values are kept (since a set automatically removes duplicates).
2. Create a new array with a size equal to the number of elements in the set.
3. Copy all elements from the set into this newly created array.
TC: O(n) + O([Link]) = O([Link])
SC: O(n)
import [Link];
for(int num:set) {
newArray[i++] = num;
}
return newArray;
}
}
Algorithm
1. Start with a sorted array.
Place a pointer (first) at index 0.
Use another pointer (current) starting from index 1 to compare elements.
2. Check for duplicates.
If the element at current is equal to the element at first,
simply move the current pointer forward to continue searching for a different value.
3. Identify a new unique element.
If the current element is different from the one at first
▪ increment first,
▪ copy the current element into this new first position,
▪ and increase the counter that tracks how many unique elements exist.
4. Build thee result array.
After processing all elements, create a new array containing only the first counter number of elements (all unique).
5. Return the new array contsaining only the unique values.
TC: Single pass through the array using two pointers O(n) + Creating a new array of unique elements O(k) = O(n)
SC: In-place duplicate removal O(1) + Creating a new array of size k O(k) = O(k)
return newArray;
}
}
Algorithm
1. Store the first element temporarily
Save the element at index 0 in a temporary variable because it will be placed at the end after shifting.
2. Shift elements to the left
Start iterating from index 1 to the last index.
Move each element one position to the left (i.e., assign arr[i-1] = arr[i]).
3. Place the stored element at the end
After completing the shift, insert the temporary element at the last index (arr[n-1]).
TC: You shift each of the remaining n-1 elements once O(n) + Placing the last element O(1) = O(n)
SC: Only one extra variable is used to store the first element O(1). If you are considering the returning array, then the SC would be O(n)
return arr;
}
}
this logic can also be applied:
public static int[] leftRotate(int[] arr, int d) {
int size = [Link];
int[] tempArray = new int[d];
Algorithm
1. Reverse the first part of the array (from index 0 to d-1)
2. reverse the remaining elements of an array.
3. atlast, reverse the whole array. You will find that elements till dth place, placed at last and remaining elements of an array shifted left side.
Algorithm
reverse an entire array, just to keep the last element at first position.
Again reverse the element after first position till the end.
TC: O(n) + O(n-1) = O(n)
SC: O(1) → no extra space used
Algorithm
Reverse the entire array
◦ Flips the whole sequence so that elements that need to move to the front shift closer to their final positions.
Reverse two separate parts of the array:
◦ First part: reverse from index 0 to d-1
▪ (d-1 is the index, but it corresponds to the dth position)
◦ Second part: reverse from index d to n-1
▪ ( n is exclusive in typical programming loops)
After these two segment reversals, the array becomes properly rotated, giving the final result.
TC: Reverse whole array → O(n) + Reverse first part (0 to d-1) → O(d) + Reverse second part (d to n-1) → O(n − d) = O(n)
SC:The reversals are done in-place, requiring no extra array. O(1)
123456→654321→561234
Algorithm
iterate through an array from index 1 till the end.
Check if previous element is less than or equals to the current element or not, if not then return false, else return true.
TC: O(N)
SC: O(1)
Algorithm
Traverse the array from start to end.
If the target value matches any element during the scan, return its index; otherwise, return no result.
TC: In the worst case, every element is checked once → O(n)
SC: No additional memory is used beyond basic variables → O(1)
Algorithm (FindUnion)
Create a HashSet to store unique elements.
Traverse arr1 and insert each element into the HashSet.
Traverse arr2 and also insert elements into the same HashSet.
Since a Set stores only unique values, duplicates are automatically removed.
Convert the HashSet to a TreeSet to automatically sort the elements.
Convert the TreeSet into an ArrayList and return it.
If the target value matches any element during the scan, return its index; otherwise, return no result.
Time Complexity:
Inserting all elements of arr1 into HashSet → O(m)
Inserting all elements of arr2 into HashSet → O(n)
Copying from HashSet to TreeSet (balanced BST) → O((m+n) log(m+n))
Creating ArrayList from TreeSet → O(m+n)
= O((m+n) log(m+n))
Space Complexity:
HashSet stores up to m + n elements → O(m + n)
TreeSet also stores m + n → O(m + n)
Result array list stores m + n (in worst case) → O(m + n)
= O(m+n)
Algorithm (FindIntersection)
Create an empty list to store intersection elements.
For each element in arr1:
◦ Loop through arr2 and compare elements.
◦ If both values match:
▪ Add the value to the list.
▪ Break the inner loop to avoid adding duplicates for the same arr1 element.
TC:
Outer loop runs m times.
Inner loop runs n times in the worst case.
Therefore, total operations = m×n
= O(m*n)
SC: O(min(m,n))
}
public static List<Integer> FindUnion(int[] arr1, int[] arr2) {
int m = [Link];
int n = [Link];
// to add unique elements
Set<Integer> set = new HashSet<>();
for(int num:arr1) {
[Link](num);
}
for(int num:arr2) {
[Link](num);
}
// convert to TreeSet for sorted order
Set<Integer> set2 = new TreeSet<>(set);
return new ArrayList<>(set2);
}
public static List<Integer> FindIntersection(int[] arr1, int[] arr2) {
// int m = [Link];
// int n = [Link];
List<Integer> list = new ArrayList<>();
for(int num1:arr1) {
for(int num2:arr2) {
if(num1 == num2) {
[Link](num1);
break; // to avoid repetition
}
}
}
return list;
}
}
Algorithm (FindUnion)
Use two pointers i and j, each starting at the beginning of arr1 and arr2.
While both pointers are in range:
◦ If arr1[i] == arr2[j]:
▪ Add the element to the result list if it is not already present.
▪ Move both pointers forward.
◦ If arr1[i] < arr2[j]:
▪ Add arr1[i] to the result list if not already present.
▪ Move pointer i forward.
◦ If arr1[i] > arr2[j]:
▪ Add arr2[j] to the result list if not already present.
▪ Move pointer j forward
After the loop, add any remaining elements of arr1 and arr2 to the result list (avoiding duplicates).
Time Complexity:
Two-pointer traversal takes O(m + n)
But [Link]() inside the loop takes O(k) time for each check, where k is current size of list.
Worst case, this becomes: O((m + n) × (m + n)), because for each insert we may scan the list.
Space Complexity:
Result list stores at most m + n unique elements → O(m+n)
Algorithm (FindINtersection)
Use two pointers i and j.
While both pointers are in array bounds:
◦ If arr1[i] == arr2[j]:
▪ Add value to result list.
▪ Move both pointers forward.
◦ If arr1[i] < arr2[j]:
▪ Add arr1[i] to the result list if not already present.
▪ Move pointer i forward.
◦ If arr1[i] > arr2[j]:
▪ Add arr2[j] to the result list if not already present.
▪ Move pointer j forward
Since both arrays are sorted, values are compared in linear fashion.
Time Complexity:
Each array is traversed at most once.
Total operations = number of elements in both arrays.
O(m+n)
Space Complexity:
Result list stores only common elements → O(min(m,n))
Array Duplicates
I/P: arr = [2, 3, 1, 2, 3]
O/P: [2, 3]
Explanation: 2 and 3 occir more than once in the given array
Algorithm (HashSet)
Use a HashMap or HashSet to track visited elements.
If an element already exists, it is a duplicate.
Much faster lookup: O(1) average for HashSet and HashMap.
TC: Single loop → O(n)
SC: Extra HashSet or HashMap → O(n)
Missing in array
T/P: arr[] = [1, 2, 3, 5]
O/P: 4
Explanation: All the numbers from 1 to 5 are present except 4.
Algorithm (Sorting)
Use any sorting method (like [Link]).
TC: O(nlogn)
SC: O(1)
Kadane’s Algorithm
I/P: arr[] = [2, 3, -8, 7, -1, 2, 3]
O/P: 11
Explanation: The subarray [7, -1, 2, 3] has the largest sum 11.
Algorithm
Initialize maxSum with the first element.
For each index i, treat it as the starting point of a subarray.
Set currSum = 0 for each new i.
From index i, keep adding elements one by one (j loop) to form subarrays.
Update maxSum after each addition by comparing it with currSum.
Continue until all possible subarrays have been evaluated.
Return maxSum as the maximum subarray sum.
TC: O(n2)
SC: O(1)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
return list;
}
}
Algorithm (one pass)
Initially store max value, as minimum as possible.
Later we iteratean array in reverse order, to keep the track of right-most max element one-by-one.
If current element in an array founds to be greate than equal to the max value so far, we update the max value, and add the max
element in a list.
Reverse the list and return it as a result.
TC: O(n)
SC: O(k)
Given an unsorted array arr[] of size n, containing elements from the range 1 to n, it is known that one number in this range is missing, and
another number occurs twice in the array, find both the duplicate number and the missing number.
Algorithm
I
TC: O(n2)
SC: O(1)
Two Sum
Time complexity: O(n)
If interviewer says:
“Array is unsorted”
If interviewer says:
“Array is sorted”
[Link]
Logic 1: If there is no repetitive elements in an array.
class Solution {
public int binarysearch(int[] arr, int k) {
int start = 0;
int end = [Link]-1;
while(start<=end) {
// int mid = (end-start)/2; // ❌ This gives an index relative to 0, not to start.
int mid = start + (end-start)/2;
if(arr[mid] == k) {
return mid; // Use 'return' or 'break' to stop after finding the element
} else if(arr[mid] > k) {
// end = mid; // ❌ This can cause infinite loop because mid doesn’t change.
end = mid-1;
} else {
// start = mid; // ❌ This can cause infinite loop because mid doesn’t change.
start = mid+1;
}
}
return -1;
}
}
int start = 0;
int end = [Link] - 1;
int ans = -1;
Parenthesis Checker
[Link]
Equilibrium Point
[Link]
class Solution {
// Function to find equilibrium point in the array.
public static int findEquilibrium(int arr[]) {
int size = [Link];
int ans = -1;
int first = 0;
int last = size-1;
for(int i=1; i<size-1; i++) {
int leftSum = CheckSum(first, i-1, arr);
int rightSum= CheckSum(i+1, last, arr);
if(leftSum == rightSum) {
ans = i;
break;
}
}
if(ans != -1) {
return ans;
} else return ans;
}
public static int CheckSum(int first, int last, int[] arr) {
int sum=0;
for(int i=first; i<=last; i++) {
sum+=arr[i];
}
return sum;
}
}
class Solution {
// Function to find equilibrium point in the array.
public static int findEquilibrium(int arr[]) {
int n = [Link];
return -1;
}
}
class Solution {
// Function to find equilibrium point in the array.
public static int findEquilibrium(int arr[]) {
int size = [Link];
int totalSum = 0;
for(int i=0; i<size; i++) {
totalSum+=arr[i];
}
int ans = -1;
int leftSum = arr[0];
for(int i=1; i<size-1; i++) {
if(leftSum == (totalSum-(leftSum+arr[i]))) {
ans = i;
break;
}
leftSum+=arr[i];
}
if(ans != -1) return ans;
else return ans;
}
}
Parenthesis Checker
[Link]