You are on page 1of 655

This book helps you to leam how to solve real-worid problems

with data manipulation.

- Opeyemi Emmanuel
CEO/Founder (aiTSkillsCenter, Lagos

Algorithrr
for all programmers

A compilation of 90+ Algorithm


challenges and solution briefs
Data Structures &
Algorithms
for all programmers

Israel C. Abazie
What readers say...

"Data Structures and Algorithms for all


programmers" is an essential read for
backend engineers looking to elevate
their skills. Israel Abazie's book
offers practical insights and real-
world examples, providing valuable
knowledge for building scalable and
optimized software solutions.
- Awotoro E. Oladimeji, Senior Backend
Engineer at SeamlessHR

Israel Abazie's book, 'Data Structures


and Algorithms for all programmers,"
offers a well-balanced blend of theory
and practice that fosters efficient
problem-solving-an essential skill for
excelling in interviews with esteemed
companies like Meta and Microsoft.
- Grace Olayinka, Tech Lead at Niyo
Group, UK

II
What readers say...

As a programmer, honing creative


problem-solving skills is crucial.
Israel's "Data Structures and
Algorithms for all programmers" offers
a practical approach, clear
explanations, and a comprehensive
understanding of these concepts, making
it an invaluable resource for all skill
levels.
- Olaoluwa Ibukun, Senior Software
Developer at ITSkillsCenter

Israel's "Data Structures and


Algorithms for all programmers" is an
outstanding reference book that
simplifies complex concepts with
concise explanations and practical
examples for mastering data structures
and algorithms.
- Chinyelu Ibute, Software Engineer at
Microsoft, Ireland

III
What readers say...

Reading Israel Abazie's "Data


Structures and Algorithms for all
programmers" is the key to excelling in
coding tests on platforms like Turing
and Coderbyte. This comprehensive and
indispensable book not only provides a
solid foundation in data structures and
algorithms but also offers interesting
perspectives on their real-world
applications. It is a must-read for
aspiring programmers seeking to enhance
their problem-solving abilities.
- Anjula Samarasinghe, Software
Engineer/Mentor at OpenMRS

IV
Foreword

Data structures and algorithms are the


fundamental building blocks of any
software application. They are the tools
that programmers use to organize and
manipulate data, and to solve complex
problems efficiently. In this book, you
will explore the most commonly used data
structures and algorithms, and learn how
to apply them in real-world situations.

The initial section of the book


introduces fundamental algorithms along
with essential data structures such as
arrays, linked lists, stacks, queues,
and trees. To enhance practical
learning, a paper-based coding
environment is provided, enabling
readers to implement their logic in
their preferred programming language.
This interactive approach fosters a
hands-on learning experience. The book
further delves into the practical
application of these structures in
popular algorithms like sorting and
searching.

V
In the subsequent section, readers
explore advanced algorithms, including
graph algorithms, dynamic programming,
and greedy algorithms. These advanced
techniques equip readers with
invaluable problem-solving strategies
for challenges such as finding the
shortest path and constructing minimum
spanning trees.

This book is intended for programmers


of all levels, from beginners to
experienced developers. Whether you are
new to the field of computer science,
or simply looking to brush up on your
data structures and algorithms skills,
this book will provide you with the
knowledge and tools you need to be a
more effective and efficient
programmer.

Israel Abazie's background in UI


development gives him a unique
perspective and skill set that allows

VI
him to create streamlined and intuitive
controls in his writing.

His experience in programming and


design allows him to approach writing
with a focus on user experience, making
his work easy to understand and engage
with for readers. This makes him
particularly well-suited for writing
about topics related to UI and user
experience, and sets him apart from
other authors in the field.

To summarize, this book serves as a


comprehensive guide for programmers,
covering basic and advanced data
structures and algorithms. Through
practical examples and code snippets,
it illuminates key concepts and
techniques, enhancing understanding and
real-world application. Suitable for
all levels of expertise.

- Opeyemi Emmanuel
CEO/Founder @ITSkillsCenter, Lagos

VII
What to expect

• Algorithms - a set of rules that


precisely define a sequence of
operations. This book delves into the
most common interview algorithm
challenges and data structures,
covering beginner to advanced levels.

Throughout the book, various


concepts, such as the following, will
be thoroughly examined.

o Dynamic Programming

o Recursion

° Graph

° Binary Tree

o Array

° Heaps

° Queue

IX
What to expect

o Graph Traversal

o Linked List

° Sorting

• Practice screen - A paper-based


emulation of the VS Code environment
designed for tackling algorithm
challenges in your preferred
programming language.

For contributing, please visit


https://qithub.com/v intaqe-creator/
Data.St ruetures.and.Algorithms to
submit your solutions for the
algorithm challenges. As a token of
our sincere gratitude, top
contributors will receive a custom
swag from us.

X
Table of contents

Beginner’s level
o Gray Code ........................................ 2

o Minimum Substring ....................... 6


o FizzBuzz ......................................... 12
□ ABC Problem .................................. 16
o Balanced Brackets ..................... 20
o Circle of Given Radius .......... 26
o 100 Doors ...................................... 32

o Date Manipulation ..................... 36


o String Formatting....................... 42
o String Searching ....................... 48
o Count the Coins ......................... 54
o Longest Word ................................ 59
o Define a Primitive Data ........ 64
o Two Sum Problem ......................... 72
o Find Intersection ..................... 77
o First Reverse .............................. 82

XI
Table of contents

Beginner’s level
o First Factorial ......................... 85

o Missing Number in Array ....... 89


o Kadane Algorithm ....................... 93
□ Sort an Array of 0s, Is ........ 98
o Majority Element ..................... 104
o Find Duplicates ....................... 109
o Count Inversions ..................... 115
o Trapping Rain Water .............. 123
o Minimum Platforms..................... 127
o Peak Element .............................. 134
o Common Elements ....................... 139
o Hash from Two Arrays ............ 145
o Average Salary ......................... 150
o Median of Two Sorted ............ 155
o Longest Palindromic .............. 160
o Reverse Integer ....................... 166

XII
Table of contents

Beginner’s level
o Container with Most Water .. 172

o Integer to Roman ..................... 178


o Longest Common Prefix .......... 184
□ 3Sum ............................................... 190
o 4Sum ............................................... 198
o Letter Combinations .............. 204
o Divide Two Integers .............. 210
o Next Permutation ..................... 217
o Search in Rotated Array .... 224
o Search Insert Position ....... 230
o Count and Say ........................... 237
o Rotate Image .............................. 243
o Group Anagrams ......................... 251
o Spiral Matrix ........................... 258
o Add Binary .................................. 268
o Climbing Stairs ....................... 276

XIII
Table of contents

Beginner’s level
o Decode Ways ................................. 289

o Word Search ................................. 298

Advanced's level
o Recursive Backtracking ......... 308
o Amicable Pairs ........................... 315
o Deepcopy ........................................ 321
o Bracket Combinations ............. 326
o Minimum Number of Jumps .... 332
o Check for BST ............................. 339
o Left View of Binary Tree ... 346
o Remove Loop in Linked List . 353
o Detect Loop in Linked List . 360
o0-1 Knapsack Problem ......... 365
o Regular Expression ................ 372
o Remove Nth Node ................... 380

XIV
Table of contents

Advanced's level
o Merge K Sorted Lists ............. 385
o N-Queens ........................................ 396
o Permutation Sequence ............. 405
o Text Justification .................. 413
o Unique Binary Search Tree .. 439
o Validate BST ............................... 445
o Recover Binary Search Tree . 452
o Binary Tree Level Order .... 460
o Binary Zigzag Level Order .. 467
o Construct Binary Tree ........... 475
o Convert Sorted List to BST . 482
o Flatten Binary Tree ............... 498

o Distinct Subsequences ........... 501

o Triangle ........................................ 509

o Best Time to Buy I .................. 517

o Best Time to Buy II ............... 525

XV
Table of contents

Advanced's level
o Word Ladder ................................ 533
o Largest Consecutive .............. 542
o Surrounded Regions ................. 549
o Palindrome Partitioning .... 558
o Gas Station ................................ 566
o Candy ............................................. 574

o Word Break .................................. 583


o Max Points on a Line ............ 592
o Maximum Gap ................................ 601

o Repeated DNA Sequences ........ 609


o House Robber .............................. 617
o Dungeon Game .............................. 624

XVI
DSA Poem

In the world of programming,


Where code reigns supreme,
Data structures and algorithms,
Are the tools of our dreams.

From arrays to linked lists,


We craft them with care,
To store and manipulate data,
And handle it with flair.

And algorithms, the brains behind it


all,
We study and optimize,To make our
programs faster,And to avoid any
compromise.

Israel Abazie's book,Guides us through


it all,
With clear explanations and examples,
Tc make sure we stand tall.

So let's embrace these structures and


tools,And use them with delight,For
they are the foundation,Of our
programming might.

XVII
Data Structures &
Algorithms
• for all programmers •

BEGINNER’S LEVEL
Beginner's level

DSA Challenge 1
Gray Code:
Write a function to encode a number to
and decode a number from Gray code.
The function should have 2 parameters.
The first would be a boolean and the
second parameter would be the number
to be encoded/decoded. The function
should encode for true and decode for
false.

Our logic:

function grayCode(encode, num) {


if (encode) {
return num A (num >> 1);
} else {
let mask;
for (mask = num >> 1; mask != 0;
mask = mask >> 1) {
y num = num A mask;
return num;
}
}

2
Beginner's level

The grayCode function takes two


parameters: encode and num.
encode is a boolean that indicates
whether the function should encode a
number to Gray code or decode a Gray
code number,
num is the number to be encoded or
decoded.
The function works as follows:
• If encode is true, the function
returns the result of num XOR
(bitwise exclusive OR) num shifted
to the right by 1 bit: num A (num >>
1). This is the formula for
converting a binary number to its
corresponding Gray code
representation.
• If encode is false, the function
initializes a variable mask with num
shifted to the right by 1 bit, num
>> 1. Then, it enters a loop where
it repeatedly updates mask by
shifting it to the right by 1 bit
mask >> 1 until mask becomes 0. In
each iteration of the loop, num is
updated with num XOR mask.

3
Beginner's level

This is the formula for converting a


Gray code number back to its
corresponding binary representation.
Finally, the function returns num
which is either the Gray code
representation of the input number or
the binary representation of the input
Gray code number.

Importance of Gray code Algorithm in


Real-world Applications:
Gray codes are very useful in the
normal sequence of binary numbers
generated by the hardware that may
cause an error or ambiguity during the
transition from one number to the
next. So, the Gray code can eliminate
this problem easily since only one bit
changes its value during any
transition between two numbers.

4
Beginner's level

DSA Challenge 2

Minimum Substring:
Given an array 'strArr' with two
strings, *N’ and K', the
'MinWindowSubstring' function aims to
find the smallest substring in
that includes all characters from
'K'. For example: if strArr is
["aaabaaddae", "aed"] then the
smallest substring of 'N' that
contains the characters a, e, and d
is "dae" located at the end of the
string. So for this example your
program should return the string
' dae' .

Our logic:
The *MinWindowSubstring' function aims
to find the shortest substring in a
given string that includes all
characters from another given string.
It initializes the necessary variables
by extracting the first elements from
the input array.

6
Beginner's level

function MinWindowSubstring(strArr) {
let str = strArr[0];
let needle = strArr[1].split(1 1 );
for (let i = needle.length; i <=
str.length; i++ ) {
for (j =0; j <= str.length - i;
j++) {
let mySlice = str.substr(j, i);
if (isContained(mySlice)) {
return mySlice;
}
}
}
return false;

function isContained(str) {
let arr = str.split( ’ ’ );
for (let i = 0; i < needle.length;
i++) {
let place = arr.findlndex(val =>
{return val === needle[i]});
if (place === -1) {
return false;

7
Beginner's level

The function iterates through


different substring lengths, ranging
from the length of the needle string
to the length of the input string. It
uses the isContained function to
determine if a substring contains all
characters in the needle by comparing
individual characters and removing
them from the substring array.
If any character in needle is not
found in the substring array, the
function returns false, indicating
that the substring does not contain
all the characters in needle.

8
Beginner's level

If all the characters in needle are


found in the substring array, the
function returns true, indicating that
the substring contains all the
characters in needle.
If the 'isContained function returns
true for a given substring, the
function immediately returns that
substring as the smallest substring of
str that contains all the characters
in needle. If no such substring is
found, the function returns false.

Importance of Window Substring


Algorithm in Real-world Applications:
The algorithm, known as the minimum
window substring algorithm, can be
useful in a number of real-life
applications where we need to find a
substring that contains a certain set

9
Beginner's level

of characters. For example, in natural


language processing, we might want to
find the shortest sentence in a text
that contains all the words in a given
set of words.

In bioinformatics, we might want to


find the shortest DNA sequence that
contains all the nucleotides in a
given set. The algorithm could also be
used in search engines, where we want
to find the shortest query that
matches all the search terms entered
by a user.

Overall, the minimum window substring


algorithm can be a useful tool
whenever we need to search for a
substring that contains a certain set
of characters, and we want to find the
shortest possible substring that
satisfies the requirement.

10
Beginner's level

DSA Challenge 3

FizzBuzz:
Write a program that generates an
array of integers from 1 to 100
(inclusive). But:
• for multiples of 3, add "Fizz" to
the array instead of the number
• for multiples of 5, add "Buzz" to
the array instead of the number
• for multiples of 3 and 5, add
"FizzBuzz" to the array instead of
the number
Your program should return an array
containing the results based on the
rules above.

Our logic:

function fizzBuzz() {
const result = [ ] ;
for (let i = 1; i <= 100; i++) {
if (i % 3 === 0 && i % 5 === 0) {
result.push("FizzBuzz") ;
} else if (i % 3 === 0) {
result.push("Fizz") ;

12
Beginner's level

} else if (i % 5 === 0) {
result.push("Buzz") ;
} else {
result.push(i);

return result;
}

console.log(fizzBuzz());

The fizzBuzz() function uses a for


loop to iterate from 1 to 100
(inclusive). Inside the loop, it
checks whether the current number is a
multiple of 3, 5, or both using the
modulo operator (%). If the number is
a multiple of both 3 and 5, it adds
"FizzBuzz" to the result array. If
it's only a multiple of 3, it adds
"Fizz". If it's only a multiple of 5,
it adds "Buzz". Otherwise, it adds the
number itself to the array.

13
Beginner's level

Importance of the FizzBu zz Algorithm


in Real-world Applications:
The usefulness of this program lies in
its ability to demonstrate important
programming concepts such as loops,
conditional statements, and arrays,
while also providing a practical
example of how these concepts can be
used to solve real-world problems.
This program also highlights the
importance of being able to break down
problems into smaller, manageable
parts, and to develop code that is
easy to read and understand. The
FizzBuzz problem is a common coding
exercise used in interviews and coding
challenges, so being able to solve
this problem is a valuable skill for
any programmer to have.

14
Beginner's level

DSA Challenge 4

ABC Problem:
You are given a collection of ABC
blocks (e.g., childhood alphabet
blocks). There are 20 blocks with two
letters on each block. A complete
alphabet is guaranteed amongst all
sides of the blocks.

The sample collection of blocks:


(B 0) (X K) (D Q) (C P) (N A) (C T) (R
E) (T G) (Q D) (F S) (J W) (H U) (V I)
(A N) (0 B) (E R) (F S) (L Y) (P C) (Z
M)

Implement a function that takes a


string (word) and determines whether
the word can be spelled with the given
collection of blocks.
Some rules to keep in mind:
• Once a letter on a block is used,
that block cannot be used again.
• The function should be case­
insensitive .

16
Beginner's level

Our logic:
function canMakeWord(word) {
const blocks = ['BO1, *XK', ’DQ1,
'CP', 'NA1, 'GT', 'RE1, ' TG', ' QD1 ,
'FS', 'JW, 'HU', VI', ‘ AN ' , 'OB',
'ER', ’FS’, 'LY', 'PC1, ’ZM'];
const usedBlocks = [];
word = word. tollpperCase() ;
for (let i = 0; i < word.length; i+
+) {
let foundBlock = false;
for (let j = 0; j < blocks.length;
j++) {
if (usedBlocks.includes(j))
continue;
if (blocks[j].includes(word[i]))
{
usedBlocks.push(j);
foundBlock = true;
break;
}
}
if (’foundBlock) return false;
}
return true;
}

17
Beginner's level

The function takes a word as input and


checks if it can be spelled using a
collection of blocks. It iterates
through each letter of the word,
attempting to find a corresponding
block. If a block is found, it marks
it as used and proceeds to the next
letter. If no block is available for a
letter, it returns false to indicate
that the word cannot be spelled with
the given blocks.

Importance of ABC Problem Algorithm:


This algorithm presents a concise
illustration of a problem-solving task
centered around data manipulation.
Such challenges are prevalent across
industries like finance, healthcare,
and logistics, where informed
decision-making relies on processing
and analyzing data. Moreover, the
algorithm highlights the significance
of logical thinking and problem­
solving abilities, vital traits for
success in any programming role.

18
Beginner's level

DSA Challenge 5

Balanced Brackets:
Determine whether a generated string
of brackets is balanced; that is,
whether it consists entirely of pairs
of opening/closing brackets (in that
order), none of which mis-nest.

Examples:

Input Output

[] true

][ false

[][] true

][] false

[]][[] false

[[[[]]]] true

20
Beginner's level

Our logic:
One way to solve this problem is to
use a stack data structure. We can
iterate over the string of brackets,
and for each opening bracket we
encounter, we push it onto the stack.
For each closing bracket, we pop the
most recent opening bracket from the
stack and check if they match. If they
don't match, or if we encounter a
closing bracket before we've seen any
opening brackets, then the string is
not balanced.
Here's one possible implementation in
JavaScript:

function isBalanced(str) {
const stack = [ ];
const opening = [ ’ [ ' , ' {1, ' (' ] ;
const closing = [']',
for (let i = 0; i < str.length; i+
+) {
const char = str[i] ;
if (opening.includes(char)) {
stack.push(char);
} else if
(closing.includes(char)) {

21
Beginner's level

const lastOpening =
stack.pop();
if
(opening.indexOf(lastOpening) !==
closing.indexOf(char)) {
return false;
}

}
return stack.length === 0;
}
console.log(isBalanced("[]")) //true
console.log(isBalanced("]][[[][][][]]
[")) //false

This function creates an empty stack


and two arrays to hold the opening and
closing brackets. It then iterates
over the input string, pushing open?.ng
brackets onto the stack and popping
the most recent opening bracket for
each closing bracket. If the brackets
don't match, it returns false.
Finally, it checks if the stack is
empty at the end, which ensures that
all opening brackets had a matching
closing bracket.

22
Beginner's level

Importance of Balanced Bracket


Algorithm in Real-world Applications:
The problem of checking whether a
string of brackets is balanced is a
classic problem in computer science,
and the solution we described using a
stack data structure is a fundamental
algorithm used in many applications.
One common application of this
algorithm is in programming language
compilers and interpreters. In many
programming languages, brackets are
used to delimit blocks of code, such
as functions, loops, and conditionals.
When compiling or interpreting code,
the compiler or interpreter needs to
check that the brackets are properly
balanced in order to avoid syntax
errors.

Another application of this algorithm


is in validating user input in web
applications. For example, a web form
that asks for a user's phone number
might use this algorithm to check that

23
Beginner's level

the input contains only digits and


possibly some separators, such as
parentheses and hyphens.

In general, any application that


involves processing or validating
strings of text might benefit from
using this algorithm to check whether
brackets are properly balanced.

24
Beginner's level

DSA Challenge 6

Circles of Given Radius Through Two


Points:
Implement a function that takes two
points and a radius and returns the
two circles through those points. For
each resulting circle, provide the
coordinates for the center of each
circle rounded to four decimal digits.
Return each coordinate as an array,
and coordinates as an array of arrays.
For edge cases, return the following:
• If points are on the diameter,
return one point. If the radius is
also zero however, return "Radius
Zero".
• If points are coincident, return
"Coincident point. Infinite
solutions".
• If points are farther apart than the
diameter, return "No intersection.
Points further apart than circle
diameter".

Sample inputs:

26
Beginner's level

0.1234, 0.9876 0.8765, 0.2345 2.0


0.0000, 2.0000 0.0000, 0.0000 1 .0
0.1234, 0.9876 0.1234, 0.9876 2.0
0.1234, 0.9876 0.8765, 0.2345 0.5
0.1234, 0.987 0.1234, 0.9876 0.0

Our logic:

function findCircles(p1, p2, r) {


const [x1 , y1] = p1 ;
const [x2, y2] = p2;
const d = Math.sqrt((x2 - xl) ** 2 +
(y2 - y1) ** 2) ;
if (d > 2 * r) {
return "No intersection. Points
further apart than circle diameter";
}
if (d === 0 && r === 0) {
return "Radius Zero";
}
if (d === 0) {
return "Coincident point. Infinite
solutions";
Beginner's level

}
const a = (r ** 2 - d ** 2 / 4)I **
0.5;
const xm = (xl + x2) / 2;
const ym = (y1 + y2) / 2;
const xcl = xm + a * (y2 - yi) / d;
const ycl = ym + a * (xl - x2) / d;
const xc2 = xm -a * (y2 - yi) / d;
const yc2 = ym -a * (xl - x2) / d;
return [
[xc1.toFixed(4), yc1.toFixed(4)],
[xc2.toFixed(4), yc2.toFixed(4)],
I;
}
console.log(findCircles([0.1234,
0.9876], [0.8765, 0.2345], 2.0))

The function takes two points as


arrays of two numbers, and a radius as
a number. It first computes the
distance d between the points, and
checks whether the points are farther
apart than the diameter of the
circles. If so, it returns a string
indicating there is no intersection.

28
Beginner's level

If the distance d is zero and the


radius 'r‘ is zero, it returns a
string indicating the radius is zero.
If the distance 'd' is zero, it
returns a string indicating the points
are coincident.

Otherwise, it computes the centers of


the two circles using the formulas
derived from the Pythagorean theorem
and basic trigonometry. The first
center is obtained by adding a
displacement vector to the midpoint of
the line segment connecting the two
points, and the second center is
obtained by subtracting the same
displacement vector.
The function returns the centers of
the two circles as arrays of two
numbers, each rounded to four decimal
digits, or a string if there is no
intersection or an edge case is
encountered.

29
Beginner's level

Importance of this Algorithm in Real-


world Applications:
The findCircles algorithm can be
useful in various fields that involve
geometry, such as engineering,
architecture, and physics. For
example, in mechanical engineering,
the algorithm can be used to determine
the possible paths of motion of a
rotating object or to calculate the
size of a gear needed to transmit
motion between two gears. In
architecture, the algorithm can be
used to design circular buildings,
such as domes or rotundas. In physics,
the algorithm can be used to calculate
the path of an object moving in a
circular motion or the trajectory of a
charged particle in a magnetic field.

Overall, the algorithm can be useful


whenever there is a need to find
circles that pass through two given
points.

30
Beginner's level

DSA Challenge 7

100 Doors:
There are 100 doors in a row that are
all initially closed. You make 100
passes by the doors. The first time
through, visit every door and
'toggle1 the door (if the door is
closed, open it; if it is open, close
it). The second time, only visit
every 2nd door (i.e., door #2, #4,
#6, ...) and toggle it. The third
time, visit every 3rd door (i.e.,
door #3, #6, #9, ...), etc., until
you only visit the 100th door.
Implement a function to determine the
state of the doors after the last
pass. Return the final result in an
array, with only the door number
included in the array if it is open.

Our logic:

function toggleDoors() {
let doors = Array(100).fill(false) ;

for (let i = 1; i <= 100; i++) {


for (let j = i - 1; j < 100; j +=
i) {

32
Beginner's level

doors[j] = !doors[j];
}
}

let openDoors = [];


for (let i = 0; i < 100; i++) {
if (doors[i]) {
openDoors.push(i + 1);
}
}

return openDoors;
>

This function initializes an array of


100 doors, with each door initially
closed (represented by false). It then
iterates through the doors 100 times,
toggling every *i-th' door on each
pass. After the 100th pass, it creates
a new array containing only the door
numbers of the doors that are open
(i.e., have a value of true).
To use this function and see the
resulting open doors, you can call it
like this:
console.log(toggleDoors());

33
Beginner's level

This will output an array containing


the door numbers of the open doors.
For example, if the function
determines that doors 1, 4, 9, 16, 25,
36, 49, 64, 81, and 100 are open, the
output will be:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Importance of the 100 Doors Algorithm


in Real-world Applications:
Tfiis algorithm can help you in
developing critical thinking skills
and logical reasoning, which can be
applied to a wide range of real-world
problems. The algorithm involves
creating and iterating through an
array of doors and toggling their
states based on a pattern, which is a
fundamental programming concept. By
understanding this algorithm and how
it works, one can learn important
programming concepts, such as loops,
arrays, and conditional statements.

34
Beginner's level

DSA Challenge 8

Date manipulation:
Given a date string in EST, output
the given date as a string with 12
hours added to the time. Time zone
should be preserved.

Example input: "March 6 2009 7:30pm


EST"
Example output: "March 7 2009 7:30am
EST"

Our logic:

function add12Hours(dateString) {
const months = [‘January’, ‘February1,
‘March', 'April', 'May', 'June',
'July', 'August', 'September',
'October', 'November1, 'December'];
// Get the parts of the string
const parts = datestring.split(' ');
const month =
months.indexOf(parts[0]);
const day = parselnt(parts[ 1 ], 10);
const year = parselnt(parts[2], 10);
const time = parts[3].split(’:');

36
Beginner's level

let hours = parselnt(time[0], 10);


if (time[1].slice(-2) === 'pm') {
hours += 12;
}
const minutes =
parselnt(time[1].slice(0f -2), 10);
// Create a date with given parts,
and updated hours
const date = new Date();
date.setFullYear(year, month, day);
date.setHours(hours + 12); // Add 12
hours
date.setMinutes(minutes) ;

let hoursOutput = date.getHours();


let abbreviation = 'am';
if (hoursOutput > 12) {
hoursOutput -= 12;
abbreviation = 'pm';
}
return ' ${months[date.getMonth()]}
${date.getDate()}
${date.getFullYear()} ${hoursOutput}:
${date.getMinutes()}${abbreviation}
EST';
}

37
Beginner's level

console.log(add12Hours("March 6 2009
7:30pm EST"))

The code is a function called


add12Hours' that takes a date string
in a specific format as its parameter.
The function adds 12 hours to the
input date and outputs a string in the
same format with the updated time and
date.

The first line of the function


initializes an array of month names to
use later. The next few lines of the
function split the input date string
into its component parts and parse
them into usable variables. These
include the month, day, year, hours,
and minutes. The hours are adjusted
for a 12-hour clock and adjusted based
on the "am/pm" designation.

A new date object is then created with


the given date parts and the updated
hours. The output time and date are
then formatted as a string with the
appropriate month name, day, year, and

38
Beginner's level

12-hour clock time with the "am/pm"


designation. The "EST" timezone is
also appended to the end of the
string.

Finally, the function is called with a


sample date string as an argument, and
the resulting output string is logged
to the console.

Importance of Date Manipulation


Algorithm in Real-world Applications:
This algorithm can be useful in
various industries and situations
where date and time calculations are
important. Here are some examples:
1. Finance: Financial institutions
need to keep track of the time in
different time zones to ensure that
trades and other transactions are
processed accurately and on time.
This algorithm can help them
calculate the correct time in a
specific time zone.
2. Travel: The travel industry relies
heavily on accurate time zone
calculations to ensure that flights

39
Beginner's level

• and other forms of transportation


depart and arrive on time. This
algorithm can be useful in
calculating the correct arrival and
departure times for travelers.
3. Healthcare: In the healthcare
industry, accurate timekeeping is
essential for scheduling
appointments, administering
medication, and documenting medical
procedures. This algorithm can be
useful in ensuring that the correct
time is recorded for each patient
encounter.
4. E-commerce: Online retailers need
to process orders and shipments
quickly and efficiently. This
algorithm can be used to calculate
the correct delivery times for
shipments to different time zones.
5. Manufacturing : Manufacturers need
to keep track of production times
and shipping schedules. This
algorithm can be used to ensure
that production and shipping
schedules are aligned with the
correct time zone.

40
Beginner's level

DSA Challenge 9

String Formatting Algorithm:


Write a function to generate a string
output which is the concatenation of
input words from a list/sequence
where:
1 . An input of no words produces the
output string of just the two brace
characters ("{}")
2. An input of just one word, e.g.
["ABC"], produces the output string
of the word inside the two braces,
e.g. "{ABC}"
3. An input of two words, e.g. ["ABC",
"DEF"], produces the output string
of the two words inside the two
braces with the words separated by
the string " and ", e.g. "{ABC and
DEF}"
4. An input of three or more words,
e.g. ["ABC", "DEF", "G", "H"],
produces the output string of all
but the last word separated by ", "
with the last word separated by "
and " and all within braces; e.g.
"{ABC, DEF, G and H}"

42
Beginner's level

Test your function with the following


series of inputs showing your output
here on this page:
• [] # (No input words).
• ["ABC"]
• ["ABC", "DEF"]
• ["ABC", "DEF", "G", "H"]

Our logic:

function generateOutputSt ring(words)


{
let output = ";
const n = words.length;
if (n === 0) {
output += "}”;
} else if (n === 1) {
output += words[0] +
} else if (n === 2) {
output += words[0] + " and " +
words[1 ] + ";
} else {
for (let i - 0; i < n - 1; i++) {
output += words[i] + ",
}

43
Beginner's level

output += "and " + words[n - 1] +


"}■';
}
return output;
}
console. log(generateOutputString([])),’
// {}
console.log(generateOutputString(["ABC
"])); // {ABC}
console.log(generateOutputString(["AB
C", "DEF"])); // {ABC and DEF}

The function
'generateOutputString(words)' takes
an input parameter words which is an
array of strings. It then generates
an output string based on the number
of input words according to the rules
specified in the problem statement.
Here's how the function works:
1. We initialize the output string as
output = ".
2. We then check the length of the
input words array to determine
which of the four cases we need to
handle.

44
Beginner's level

3. If words is empty (i.e. n === 0),


we simply append the closing brace
and return the output string.
4. If words has only one word (i.e. n
=== 1), we append the word and the
closing brace and return the output
string.
5. If words has two words (i.e. n ===
2), we append the first word, the
word "and", the second word, and
the closing brace and return the
output string.
6. If words has three or more words
(i.e. n > 2), we loop through the
array from index 0 to n-2 and
append each word followed by a
comma and space. Finally, we append
the word "and" followed by the last
word and the closing brace, and
return the output string.

Importance of this Algorithm in Real-


world Applications:
It can be used to generate a user­
friendly, grammatically correct

45
Beginner's level

message based on the number of items


in a list.

For example, suppose you have an e­


commerce website where users can add
items to their shopping cart. When
the user views their shopping cart,
you can use this function to generate
a message like "Your shopping cart
contains {iteml, item2, and item3}"
or "Your shopping cart contains
{iteml and item2}" depending on the
number of items in the cart. This
makes the message more readable and
helps to avoid awkward phrasing.

46
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 10

String Searching Algorithm:


Create a function, or show a built-in
function, to count the number of non­
overlapping occurrences of a
substring inside a string.
The function should take two
arguments:
• the first argument being the string
to search, and
• the second a substring to be
searched for.
It should return an integer count.
The matching should yield the highest
number of non-overlapping matches.
In general, this essentially means
matching from left-to-right or right-
to-left .

Our logic:

function countSubstrings(str, substr)


{
let count = 9;
let i = 0;
while (i < str.length) {

48
Beginner's level

const index = str.indexOf(substr, i);


if (index === -1) {
break;
}
count++;
i = index + substr.length;
}
return count;
}
console.log(countSubstrings("banana",
"an")); // expected output: 1
console.log(countSubstrings("mississi
ppi", "ss")); // expected output: 2

This function uses a loop to iterate


over the string and indexOf method to
find the first occurrence of the
substring starting from the current
index. If it finds a match, it
increments the count and updates the
current index to be the end of the
matched substring (i.e., the index of
the match plus the length of the
substring). It then continues
searching for the next occurrence of

49
Beginner's level

the substring starting from the


updated index. The loop stops when no
more matches are found.

Note that this function finds the


non-overlapping occurrences of the
substring in the string. This means
that if a substring occurs multiple
times in the same location, it will
only count it once.

Importance of this Algorithm in Real-


world Applications:
The ability to count the number of
non-overlapping occurrences of a
substring inside a string is a common
requirement in many real-world
scenarios. Here are a few examples:
1. Data analysis: In data analysis,
it's often necessary to search for
specific patterns or keywords in a
dataset. This function can be used
to count the number of occurrences
of a particular keyword in a given
dataset. For example, if you have a

50
Beginner's level

• large text file containing customer


feedback, you can use this function
to count the number of times a
particular keyword (e.g.,
"problem", "complaint", "issue")
appears in the text.
2. Search engine optimization: In
search engine optimization (SEO),
it’s important to optimize web pages
for specific keywords. This function
can be used to count the number of
times a keyword appears on a web
page. This can help you ensure that
the keyword is used frequently
enough to rank well in search engine
results, but not so frequently that
it's considered "keyword stuffing".
3. Natural language processing: In
natural language processing (NLP),
it's often necessary to search for
specific phrases or patterns in
text. This function can be used to
count the number of occurrences of a
particular phrase or pattern in a
given text. For example, if you're
building a chatbot that needs to

51
Beginner's level

• recognize when a user is asking for


help, you can use this function to
count the number of times certain
keywords (e.g., "help", "support",
"assistance") appear in the user's
message.

Overall, the ability to count non­


overlapping occurrences of a
substring in a string is a powerful
tool that can be used in a wide
variety of real-world scenarios to
search, analyze, and extract
information from text data.

52
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 11

Count Lhe Coins:


There are four types of common coins
in US currency:
• quarters (25 cents)
• dimes (10 cents)
• nickels (5 cents), and
• pennies (1 cent)
There are six ways to make change for
15 cents:
• A dime and a nickel
• A dime and 5 pennies
• 3 nickels
• 2 nickels and 5 pennies
• A nickel and 10 pennies
• 15 pennies
Implement a function to determine how
many ways there are to make change
for a given input, cents, that
represents an amount of US pennies
using these common coins.

Our 1Dflic:

function countChange(cents) {
let count = 0;

54
Beginner's level

for (let q = 0; q <= cents / 25; q++)


{
for (let d = 0; d <= (cents - 25
* q) / 10; d++) {
for (let n = 0; n <= (cents -
25 * q - 10 * d) / 5; n++) {
count++;
}

return count;
}

This implementation uses nested loops


to iterate through all possible
combinations of quarters, dimes,
nickels, and pennies that add up to
cents. The outermost loop iterates
through the number of quarters (q),
the middle loop iterates through the
number of dimes (d), and the
innermost loop iterates through the
number of nickels (n). The count of
the number of combinations is
incremented in the innermost loop.

55
Beginner's level

Note that this implementation assumes


that all coins are available in
unlimited quantities. If the problem
statement includes constraints on the
maximum number of coins that can be
used, then the implementation will
need to be modified accordingly.

Importance of this Algorithm in Real-


world Applicatiions:
The algorithm for counting the number
of ways to make change using common
US coins has several real-world
applications. Here are a few
examples:
1 . Cash registers and vending
machines: Cash registers and
vending machines often need to
calculate the correct amount of
change to give to customers. This
algorithm can be used to determine
the number of ways to make change
for a given amount, which can help
ensure that the correct amount of
change is dispensed.

56
Beginner's level

2. Banking and finance: Banks and


financial institutions may use this
algorithm to help automate currency
conversion processes. For example,
if a customer wants to exchange US
dollars for another currency, the
bank could use this algorithm to
determine the optimal combination
of coins and bills to dispense as
change.

3. Gaming and entertainment: Games


that involve currency or gambling
may use this algorithm to determine
the number of ways to make change
for a given amount. This can help
ensure that the games are fair and
that the correct payouts are made
to players.

57
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 12

Longest Word:
Have the function
longestWord(vintage) take the vintage
parameter being passed and return the
longest word in the string. If there
are two or more words that are the
same length, return the first word
from the string with that length.
Ignore punctuation and assume vintage
will not be empty. Words may also
contain numbers, for example "Hello
world123 567"

Our logic:

function longestWord(vintage) {
let words = vintage.split(" ");
let result =
for (let i = 0; i < words.length;
i++) {
if (words[i].length >
result.length) {
result = words[i];
}

59
Beginner's level

}
return result;
}

console.log(longestWord("Tech Dev
ITSkillsCenter"));

The 'longestWord' function that takes


a string vintage as an argument,
splits the string into an array of
words using the split() method, and
then loops through the words to find
the longest word in the array.

The function initializes an empty


string result, which is used to store
the longest word found so far. The
for loop iterates through each word
in the words array, and if the length
of the current word is greater than
the length of the current result
string, it updates result to the new
longest word.

Finally, the function returns the


longest word found.

68
Beginner's level

Importance of Longest Word Algorithm


in Real-world Applications:
The 'longestWord' function can be
useful in a variety of real-world
scenarios and industries. Here are a
few examples:

1. Natural Language Processing: In the


field of natural language
processing, the longestWord
function can be used to extract the
most important keywords from a
piece of text. This can be useful
for tasks such as text
summarization, sentiment analysis,
and document classification.
2. Search Engines: Search engines use
complex algorithms to find relevant
results for a user's query. One of
the factors that search engines
consider is the length of the query
terms. The 'longestWord' function
can be used to extract the longest
word from a user's search query,
which can be used to help rank
search results.

61
Beginner's level

3. Data Analysis: Data analysts often


work with large datasets and need
to extract meaningful information
from them. The longestWord function
can be used to extract the longest
word from a column of text data,
which can provide insights into the
topics and themes that are most
frequently mentioned.
4. Content Creation: Content creators,
such as bloggers and writers, often
need to come up with catchy titles
and headlines for their articles.
The longestWord function can be
used to extract the longest word
from the main topic or theme of the
article, which can be used as a
starting point for crafting a
compelling title or headline.

Overall, the longestWord function is


a simple but versatile tool that can
be used in a variety of industries
and applications.

62
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 13

Define a Primitive Data Type:


Write a program that defines a custom
'Num' type that behaves like an
integer with a minimum value of 1 and
a maximum value of 10.

It should throw a TypeError with the


message 'Out of range' if the value
provided is outside of this range,
and throw a TypeError with the
message 'Not a Number' if the value
provided is not a number.

Our loi-ic:

class Num {
constructor(value) {
if (typeof value !== 'number' ||
isNaN(value)) {
throw new TypeError(1 Not a
Number');
}
if (value < 1 || value > 10) {
throw new TypeError(1 Out of
range' );

64
Beginner's level

}
this.value = value;
}
valueOf() {
return this.value;

toString() {
return St ring(this.value);
}

plus(num) {
return new Num(this.value +
num.value) ;
}

minus(num) {
return new Num(this.value -
num.value);
}
times(num) {
return new Num(this.value *
num.value);
}

65
Beginner's level

dividedBy(num) {
return new Num(this.value /
num.value);
)
modulo(num) {
return new Num(this.value %
num.value);
}
}
// Examples
const num1 = new Num(5);
console.log(numl.plus(new Num(3)));
// Output: 8

const num2 = new Num(10);


console.Iog(num2.minus(new Num(7)));
// Output: 3

const num3 = new Num(2);


console.log(num3.times(new Num(4)));
// Output: 8

const num4 = new Num(8);


console.log(num4.dividedBy(new
Num(2))); // Output: 4

66
Beginner's level

const num5 - new Num(9);


console.log(num5.modulo(new Num(2)));
// Output: 1

// Error handling examples


try {
const num6 = new Num(0);
} catch (error) {
console.log(error.message) ; //
Output: Out of range
}

try {
const num7 = new Num(’not a
number 1);
} catch (error) {
console.log(error.message); //
Output: Not a Number
}

To implement the 'Num' type, we use


the constructor pattern in
JavaScript. The constructor pattern
allows us to create new objects with

67
Beginner's level

the same structure and behavior. In


the code, we create a constructor
function 'Num' that takes an initial
value as an argument. Inside the
constructor function, we check if the
initial value is a valid number and
falls within the range of 1 to 10. If
the initial value is not valid, we
throw a 'TypeError' with an
appropriate error message.

Next, we define several methods that


allow us to perform arithmetic
operations between Num objects. These
methods include add, subtract,
multiply, and divide. These methods
also perform checks to ensure that
the resulting value after the
arithmetic operation is still within
the valid range of 1 to 10.

Finally, we add a 'valueOf' method to


the 'Num' prototype.
This method returns the internal

68
Beginner's level

value property of the Num object,


which allows us to use the Num object
in arithmetic operations with regular
numbers.

Overall, this implementation of the


'Num' type demonstrates the power and
flexibility of OOP and the
constructor pattern in JavaScript. By
encapsulating behavior and data, we
can create custom data types that
provide useful functionality and
prevent errors that could arise from
improper usage.

Importance of this Algorithm in Real-


world Applications:
The algorithm implemented above is of
object-oriented programming (OOP)
pattern, specifically the constructor
pattern in JavaScript. It defines a
custom data type ’Num' that behaves
like an integer with a minimum and
maximum valid value and provides

69
Beginner's level

several arithmetic operations that


can be performed between 'Num'
objects.

The usefulness of OOP and the


constructor pattern is vast. It
allows for code organization,
reusability, and encapsulation. The
'Num' type could be useful in
scenarios where we want to restrict
values to a specific range, such as a
rating system on a website or an
input field that should only accept
numbers within a certain range.

Additionally, by encapsulating the


'Num' object's behavior and providing
only specific methods to perform
arithmetic operations, we can prevent
errors that could arise from improper
calculations. The constructor pattern
is widely used in JavaScript and is a
fundamental building block for many
popular libraries and frameworks,
such as React and Vue.js.

70
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 14

Two Sum Problem:


Given an array of integers and a
target sum, find all pairs of
integers in the array that add up to
the target sum. If there are multiple
pairs that add up to the target sum,
return all pairs in a nested array.

Hint:
If the array is [3, 5, 2, -4, 8, 11]
and the sum is 7 , your program
should return [[11, -4], [2, 5]]
because 11 + -4 = '7' and 2 + 5 = '7'

Our logic:

function twoSum(arr, targetSum) {


let pairs = [];
let visitedNums = new Set();

for (let num of arr) {


let complement = targetSum -
num;

72
Beginner's level

if (visitedNums.has(complement))

pairs.push([num, complement]);
}
visitedNums.add(num);
}

return pairs;
}

The algorithm above takes a brute


force approach to solve the Two Sum
problem. Here we iterate through the
array to find all possible pairs of
numbers that add up to the target
sum. The time complexity of this
algorithm is 0(nA2).

The 'twoSum' function takes an array


arr of integers and a target sum
'targetSum'. We create an empty array
pairs to store our pairs of numbers
that add up to the target sum, and a
Set object called 'visitedNums' to
keep track of the numbers we’ve
already visited.

73
Beginner's level

We then iterate through each number


in the arr array. For each number, we
calculate its complement by
subtracting it from the 'targetSum'.
We then check if the 'visitedNums'
set already contains the complement.
If it does, then we've found a pair
of numbers that add up to the
'targetSum', so we push an array with
both numbers into the pairs array.

Finally, we add the current number to


the 'visitedNums' set and continue
iterating through the array. Once
we've visited every number in the arr
array, we return the pairs array that
contains all pairs of numbers that
add up to the 'targetSum'.

Importance of Two Sum Problem in


Real-world Applications:
The Two Sum problem can be applied in
a variety of scenarios such as
financial analysis, machine learning,

74
Beginner's level

data science, and more. For example,


the Two Sum problem can be used to
analyze stock market data and
identify pairs of stocks that are
positively correlated, or to find
pairs of customers who are most
likely to purchase a particular
product together.

Additionally, the Two Sum problem is


used as a building block in many
other algorithms and can be used as a
starting point for solving more
complex problems.

75
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 15

Find Intersection:
Have the function
Findintersection(strArr) read the
array of strings stored in strArr
which will contain 2 elements: the
first element will represent a list
of comma-separated numbers sorted in
ascending order, the second element
will represent a second list of
comma-separated numbers (also
sorted). Your goal is to return a
comma-separated string containing the
numbers that occur in elements of
strArr in sorted order. If there is
no intersection, return the string
false.

Example:
Input: ["1, 3, 4, 7, 13", "1, 2, 4,
13, 15"]
Output: 1,4,13
Input: ["1, 3, 9, 10, 17, 18", "1, 4,
9, 10"]
Output: 1,9,10

77
Beginner's level

Our logic:

function Findlntersection(strArr) {
const [listl, list2] = strArr.map(str
=> str.split(", ").map(Number));
const intersection = [] ;
let i = 0, j =0;
while (i < listl.length && j <
list2.length) {
if (listl[i] === list2[j]) {
intersection.push(listl[i]) ;
i++ (
j++;
} else if (listl[i] < list2[j]) {
1++ j

} else {
j++;
}

return intersection.length > 0 ?


intersection.join(",") : "false";

console.log(Findlntersection(["1, 3,
4, 7, 13", "1, 2, 4, 13, 15"]));

78
Beginner's level

The function takes an array 'strArr'


containing two comma-separated number
lists as input. First, the two lists
are extracted from the input array by
splitting the strings and converting
them to arrays of numbers. Then, a
loop is used to iterate through the
two arrays simultaneously and find
the common elements. If an element is
found in both lists, it is added to
the 'intersection' array.If an
element in the first list is smaller
than an element in the second list,
we increment the first list index to
move on to the next element.
Similarly, if an element in the
second list is smaller than an
element in the first list, we
increment the second list index. Once
the loop is complete, we join the
'intersection' array into a comma-
separated string and return it. If
there is no intersection, we return
the string "false 1.

79
Beginner's level

The algorithm used in this function


has a time complexity of 0(n+m),
where n and m are the lengths of the
input lists, and is therefore quite
efficient.

Importance of Find Intersection


Algorithm in real-world situations:
The 'Findintersection' algorithm can
be useful in various industries that
deal with data analysis and
processing. For example, in e­
commerce, it can be used to find the
common items that appear in both the
user's search history and the current
inventory. In finance, it can be used
to identify the common stocks held by
different portfolios. It can also be
used in social media analytics to
find common interests or followers
between two users or groups. In
general, the algorithm can help in
identifying the commonalities between
two sets of data, which can aid
decision-making and analysis in
various fields.

80
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 16

First Reverse:
Have the function ’FirstReverse(str)‘
take the 'str' parameter being passed
and return the string in reversed
order.

For example: if the input string is


"Hello World and Coders" then your
program should return the string
sredoC dna dlroW olleH.

Our logic:

function FirstReverse(str) {
return
str.split('1).reverse().join('');
}

This code defines a JavaScript


function called *FirstReverse' that
takes a string as input, reverses it,
and returns the reversed string.

82
Beginner's level

Here's how the code works:


• The split('') method is called on
the input string str. This splits
the string into an array of
individual characters.
• The reverse() method is called on
the resulting array of characters.
This reverses the order of the
array elements.
• The join('') method is called on
the reversed array. This combines
the elements of the array into a
new string, with an empty string
separator between each element.
The end result is a reversed string.

Importance of First Reverse Algorithm


in Real-world Applications:
In web development, a string reversal
function can be used to manipulate
user input, perform data analysis, or
present information in a specific
order. In data processing and
analysis, string reversal can be used
to reverse the order of date or time
values, or to manipulate large
datasets for specific purposes.

83
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 17

First Factorial:
Have the function FirstFactorial(num)
take the num parameter being passed
and return the factorial of :t. For
example: if num = 4, then your
program should return (4 * 3 * 2 * 1)
= 24. For the test cases, the range
will be between 1 and 18 and the
input will always be an integer.

Our logic:
Here's the code for the
FirstFactorial function in
JavaScript:

function FirstFactorial(num) {
if (num === 0 || num === 1) {
return 1;
}
return num * FirstFactorial(num -
i);

85
Beginner's level

This function takes an integer 'num'


as input and recursively calculates
its factorial. The factorial of num
is defined as the product of all
positive integers from 1 to num.
The function first checks if the
input is either 0 or 1, in which case
it simply returns 1. Otherwise, it
calculates the factorial by
multiplying num with the factorial of
num - 1.

Importance of First Factorial


Algorithm in Real-world Applications:
The firstFactorial function, which
calculates the factorial of a given
number, is a fundamental mathematical
function that can be used in various
real-world scenarios and industries.
Here are some examples:

1 . Combinatorics: The factorial


function is commonly used in
combinatorics, which is the branch
of mathematics that deals with
counting and arranging objects.

86
Beginner's level

■ For instance, in a poker game, the


number of possible combinations of
cards can be calculated using the
factorial function.
2. Probability theory: The factorial
function is used in probability
theory to calculate the number of
ways in which events can occur. For
instance, if you roll a die three
times, the number of possible
outcomes can be calculated using
the factorial function.
3. Computer Science: The factorial
function is used in computer
science algorithms to solve
problems like permutations,
combinations, and recursive
functions.
4. Finance: The factorial function can
be used in finance to calculate the
number of possible outcomes for a
given set of investments or trades.

87
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 18

Missing Number in Array:


Given an array of size N-1 such that
it only contains distinct integers in
the range of 1 to N. Find the missing
element.

Our logic:
Here's a JavaScript program that
finds the missing element in an array
of size N-1 containing distinct
integers in the range of 1 to N:

function findMissingElement(arr, n)
{
let sum = (n * (n + 1)) / 2; //
Calculate sum of all elements from 1
to n
let missingElement = sum -
arr.reduce((acc, curr) => acc +
curr); // Subtract sum of array
elements from the sum of all elements
to find the missing element
return missingElement;
}

89
Beginner's level

This function takes two arguments -


the array 'arr' of size 'N-1'
containing distinct integers in the
range of 1 to N, and the integer 'n
which represents the maximum value in
the range (i.e. 1 to N) .
The function first calculates the sum
of all elements from 1 to n using the
formula 'sum = (n * (n + 1)) / 2'. It
then calculates the sum of all
elements in the given array using the
reduce() method, and subtracts it
from the sum of all elements to find
the missing element. The missing
element is then returned by the
function.

Importance of Missing Number in Array


Algorithm in Real-world Applications:
The missing element algorithm has
applications in various real-world
scenarios and industries. Some
examples are:

1. Data analysis: In data analysis,


missing values can cause problems

90
Beginner's level

• while performing calculations or


generating reports. The missing
element algorithm can be used to
find missing values in datasets and
to perform calculations
accordingly.
2. Inventory management: In inventory
management, it is important to keep
track of stock levels and to
identify missing items. The missing
element algorithm can be used to
check if any item is missing from
the inventory list.
3. Quality control: In manufacturing
industries, it is important to
maintain quality control standards.
The missing element algorithm can
be used to identify missing parts
or components in the production
process.
4. Security: In security systems, it
is important to identify anomalies
and missing elements. The missing
element algorithm can be used to
identify missing data points or to
detect tampering with data.

91
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 19

Kadane Algorithm:
Given an array Arr[] of N integers.
Find the contiguous sub­
array (containing at least one number)
which has the maximum sum and return
its sum.

Your Task:
You don't need to read input or print
anything. The task is to complete the
function maxSubarraySum() which takes
Arr[] and N as input parameters and
returns the sum of subarray with
maximum sum.

Our logic:

function maxSubarraySum(Arr, N) {
let maxSoFar = Arr[0];
let maxEndingHere = Arr[0];
for (let i = 1; i < N; i++) {
maxEndingHere =
Math.max(maxEndingHere + Arr[i],
Arr[i]);

93
Beginner's level

maxSoFar = Math.max(maxSoFar,
maxEndingHere);
}

return maxSoFar;
}

This function uses the Kadane's


algorithm to find the maximum sum of
a contiguous sub-array in an array of
integers. The 'maxSoFar' variable
stores the maximum sum found so far,
while 'maxEndingHere' variable stores
the maximum sum ending at the current
index. We iterate through the array
and update both variables at each
index. The final value of 'maxSoFar'
is returned as the maximum sum of a
contiguous sub-array.

Importance of Kadane Algorithm in


Real-world Applications:
Kadane's algorithm is widely used in
various real-world situations. Here

94
Beginner's level

are a few examples:

1. Image processing: Kadane's


algorithm is used for analyzing
images, where subarrays of pixels
are analyzed to find patterns or
detect objects.
2. Financial analysis: The algorithm
is used in financial analysis to
calculate the maximum return on
investment over a period of time.
3. Stock market prediction: Kadane's
algorithm is used to predict stock
prices by analyzing the trend of
prices over a certain period of
time.
4. Machine learning: Kadane's
algorithm is used in machine
learning for feature selection,
where it is used to find the most
important features in a dataset.
5. Signal processing: The algorithm is
used in signal processing to
analyze signals, where subarrays of
signal data are analyzed to detect
patterns or anomalies.

95
Beginner's level

Overall, Kadane's algorithm is an


important tool for analyzing large
datasets and finding maximum sum
subarrays, making it a valuable
technique in many industries and
applications.

96
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 20

Sort an Array of 0s, 1s and 2s:


Given an array of size N containing
only 0s, Is, and 2s; sort the array
in ascending order.

Your Task:
You don't need to read input or print
anything. Your task is to complete
the function sort012() that takes an
array arr and N as input parameters
and sorts the array in-place.

Our logic:
Here's a JavaScript program that
sorts an array of 0s, Is, and 2s in
ascending order using the "Dutch
National Flag" algorithm:

function sort012(arr, n) {
let low = 0;
let mid = 0;
let high = n - 1 ;

while (mid <= high) {

98
Beginner's level

if (arr[mid] === 0) {
[arr[low], arr[mid]] =
[arr[mid], arr[low]];
low++;
mid++;
} else if (arr[mid] === 1) {
mid++;
} else if (arr[mid] === 2) {
[arr[mid], arr[high]] =
[arr[high], arr[mid]];
high--;
>
}

The function takes two parameters,


the array 'arr' and its size 'n'. It
initializes three pointers, 'low',
'mid', and 'high', pointing to the
beginning, the current position, and
the end of the array respectively.

The function then enters a while loop


that continues until the mid pointer
crosses the 'high' pointer. Inside
the loop, it checks the value of
'arr[mid]‘. If it is 0,it swaps the

99
Beginner's level

values of 'arr[low]‘ and 'arr[mid]',


increments both 'low' and mid'
pointers, and continues with the next
iteration of the loop. If 'arr[mid]'
is 1, it simply increments ’mid' and
continues with the next iteration. If
'arr[mid]' is 2, it swaps the values
of *arr[mid]' and 'arr[high]',
decrements 'high', and continues with
the next iteration.

This process partitions the array


into three sections: the elements
before 'low' are all 0s, the elements
between 'low and mid' are all Is, and
the elements after 'high' are all 2s.
Since the loop continues until 'mid'
crosses 'high', all elements in the
array are processed, and the array is
sorted in ascending order.

100
Beginner's level

Importance of this Algorithm in Real-


world Applications:
Sorting an array of 0s, Is, and 2s is
an important algorithm as it has many
real-world applications. Here are
some examples:

1 . Computer networks: In computer


networks, data is often represented
in binary format. Sorting an array
of 0s and Is is crucial in many
network protocols, including the
Transmission Control Protocol (TCP)
and the Internet Protocol (IP).
2. Image processing: In image
processing, an image can be
represented as an array of pixels,
with each pixel having a value
between 0 and 255. Sorting such
arrays is a key part of many image
processing algorithms.
3. DNA sequencing: In bioinformatics,
DNA sequences are often represented
as strings of characters, where
each character is a nucleotide (A,
C, G, or T). These strings can be

101
Beginner's level

converted into arrays of integers (0,


1, 2, or 3), which can then be sorted
using the algorithm.

4. Search algorithms: In many search


algorithms, such as binary search,
the input data must be sorted
before the algorithm can be
applied. Sorting an array of 0s,
Is, and 2s can be an efficient way
to sort such data.

102
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 21

Majority Element:
Given an array A of N elements. 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 in the array.

Your Task:
The task is to complete the function
'majorityElement()' which returns the
majority element in the array. If no
majority exists, return '-1'.

Our logic:

function majorityElement(arr, n) {
let count = 0;
let candidate = 0;

for (let i = 0; i < n; i++) {


if (count === 0) {
candidate = arr[i];
}

104
Beginner's level

if (candidate === arr[i]) {


count++;
} else {
count--;
}
}
count = 0;

for (let i = 0; i < n; i++) {


if (arr[i] === candidate) {
count++;
}

return count > n / 2 ? candidate :


-i;
}

The function uses the Boyer-Moore


Majority Vote algorithm to find the
majority element in the array. The
algorithm works by iterating through
the array and keeping track of a
candidate element that is assumed to
be the majority element. The count of

105
Beginner's level

the candidate is incremented each time


it is encountered, and decremented
each time a different element is
encountered. If the count of the
candidate reaches 0, a new candidate
element is selected from the remaining
elements of the array. At the end of
the iteration, the function checks if
the candidate element is the majority
element by counting its occurrences in
the array. If the count is greater
than N/2, the candidate is returned,
otherwise -1 is returned.

Importance of Majority Element


Algorithm in Real-world Applications:
The majority element algorithm has
several real-world applications,
including:
1 . Voting systems: In a voting system,
the majority element is the
candidate who receives more than
half of the total votes. Knowing the
majority element is essential for
determining the winner of the
election.
2. Market research: In market research,

106
Beginner's level

• the majority element can be used to


identify the most popular product
or service among customers. This
information can help companies to
improve their products and services
or to focus their marketing efforts
on the most popular items.
3. Medical research: In medical
research, the majority element can
be used to identify the most common
disease or condition among
patients. This information can help
researchers to develop new
treatments or to allocate resources
for treating the most common
conditions.
4. Data analysis: In data analysis,
the majority element can be used to
identify patterns and trends in
large datasets. This information
can be used to make better
decisions and to identify areas for
improvement.
Overall, the majority element
algorithm is an important tool for
analyzing and understanding data in a
wide range of applications.

107
Beginner's level

DSA Challenge 22

Find duplicates in an array:


Given an array a[] of size N which
contains elements from 0 to N-1, you
need to find all the elements
occurring more than once in the given
array. Return the answer in ascending
order.

Your Task:
Write a function called duplicates()
wh^ch takes array ’a[] ' and ’n' as
input as parameters and returns a
list of elements that occur more than
once in the given array in a sorted
manner. If no such element is found,
return list containing [-1].

Our logic:

function duplicates(arr, n) {
let result = [];
let freq = new Array(n).fill(0);
for (let i = 0; i < n; i++) {

109
Beginner's level

freq[arr[i]]++;

for (let i = 0; i < n; i++) {


if (freq[i] > 1) {
result.push(i);

}
}

if (result.length === 0) {
result.push(-1);

return Array,from(new Set(result));

110
Beginner's level

The ’duplicates' function takes an


array 'arr' of integers and its size
'n' as input parameters. The function
finds all the elements occurring more
than once in the array and returns
them in ascending order.

The function first initializes an


empty array called 'result' and an
array 'freq' of size 'n' with all
elements initialized to zero. It then
iterates over the input array 'arr'
and increments the frequency count of
each element in the 'freq' array.

Next, the function iterates over the


'freq' array and pushes the index of
all elements that have a frequency
count greater than one into the
'result' array.

Finally, the function checks if there


are no duplicate elements found, and
if so, it pushes -1 into the 'result'
array. The Array.from(new Set(result))

111
Beginner's level

statement at the end removes any


duplicate elements from the 'result'
array and returns the sorted array of
duplicate elements.

Importance of this Algorithm in Real-


world Applications:
The Find Duplicates in an Array
algorithm has various real-world
applications, including:

1. Data Analysis: This algorithm is


often used in data analysis to find
duplicate entries in datasets,
which helps in cleaning and
preparing data for analysis.
2. Database Management: In database
management systems, this algorithm
is used to identify and remove
duplicate records, which helps in

112
Beginner's level

• maintaining data integrity and


improving database performance.
3. E-commerce: In the e-commerce
industry, duplicate product entries
can lead to confusion among
customers and can result in lost
sales. This algorithm can be used
to identify and remove such
duplicate entries.
4. Image and Audio Processing: In
image and audio processing, this
algorithm can be used to identify
and remove duplicate files, which
helps in saving storage space and
improving the efficiency of the
processing system.
5. Security: In cybersecurity,
duplicate entries in a database can
be used to exploit vulnerabilities
in the system. This algorithm can
be used to identify and remove such
duplicate entries, thereby
improving the security of the
system.

113
Beginner's level

DSA Challenge 23

Count Inversions:
Given an array of integers. Find the
Inversion Count in the array.

Inversion Count: For an array,


inversion count indicates how far (or
close) the array is from being
sorted. If array is already sorted
then the inversion count is 0. If an
array is sorted in the reverse order
then the inversion count is the
maximum. Formally, two elements a[i]
and a[j] form an inversion if a[i] >
a [ j ] and i < j.

Our logic:

function inversioncount(arr, N) {
let count = 0;
mergeSort(arr, 0, N - 1);
return count;

115
Beginner's level

function mergeSort(arr, 1, r) {

if (1 < r) {
let mid = Math.floor((1 + r) /

2);
mergeSort(arr, 1, mid);
mergeSort(arr, mid + 1, r) ;
merge(arr, 1, mid, r);

}
}

function merge(arr, 1, mid, r) {


let nl = mid - 1 + 1 ;
let n2 = r - mid;
let L = new Array(nl);
let R = new Array(n2);

116
Beginner's level

for (let i = 0; i < n1 ; i++) {


L[i] = arr[l + i];

}
for (let j = 0; j < n2; j++) {
R[ j ] = arr[mid + 1 + j ];

let i = 0,

j = 0,
k = 1;

while (i < n1 && j < n2) {


if (L[i] <= R[j]) {
arr[k++] = L[i++];
} else {

117
Beginner's level

arr[k++] = R[j++];
count += n1 - i;

}
}
while (i < nl) {
arr[k++] = L[i++];

}
while (j < n2) {
arr[k++] = R[j++];

}
}

The given code implements the


inversion count algorithm using the
merge sort technique. The algorithm
works as follows:

118
Beginner's level

1 . The 'inversioncount' function takes


an array 'arr' and its size 'N' as
input.
2. It initializes a count variable to
0.
3. It calls the 'mergeSort' function
to sort the array and count the
number of inversions.
4. The mergeSort' function
recursively divides the array into
two halves and sorts them using
merge sort technique.
5. The merge' function merges the two
sorted halves and counts the number
of inversions.
6. The 'merge' function compares the
first element of the left and right
subarrays. If the left element is
smaller than or equal to the right
element, it copies the left element
into the merged array and
increments the left index.
Otherwise, it copies the right
element into the merged array,
increments the right index, and

119
Beginner's level

adds the number of remaining elements


in the left subarray to the count
variable.
7. The 'merge' function continues this
process until all elements in both
subarrays have been copied into the
merged array.
8. The 'inversioncount' function
returns the count of inversions.

In summary, the algorithm counts the


number of inversions in an array by
first sorting the array using the
merge sort technique, which counts the
number of inversions in the process of
merging the sorted halves.

Importance of Count Inversions


Algorithm in Real-world Applications:

120
Beginner's level

For example, it can be used in the


analysis of time series data to detect
any irregularities or anomalies that
indicate deviations from the expected
pattern. It can also be used in the
analysis of financial data to identify
patterns in stock prices or other
market trends.

In addition, this algorithm is useful


in the development of sorting
algorithms. By counting the number of
inversions in an array, we can
determine the time complexity of
sorting algorithms like merge sort,
and assess their performance for large
datasets. This information is useful
in designing and optimizing algorithms
for big data applications, where
efficient processing of large datasets
is crucial for performance.

121
Beginner's level

DSA Challenge 24

Trapping Rain Water:


Given an array arr[] of N non­
negative integers representing the
height of blocks. If width of each
block is 1, compute how much water
can be trapped between the blocks
during the rainy season.

Our lo ic:

function trappingWater(arr, n) {
let result = 0;
let leftMax = 0;
let rightMax = 0;
let left = 0;
let right = n - 1 ;
while (left <= right) {
if (arr[left] < arr[right]) {
if (arr[left] > leftMax) {

123
Beginner's level

leftMax = arr[left];
}
left++ ;
} else {
if (arr[right] > rightMax) {
rightMax = arr[right];
} else {
result += rightMax -
arr[right];
}
right--;
}
}
return result;
}

This function takes in an array of 'N'


non-negative integers representing the
height of blocks, and returns the
amount of water that can be trapped
between the blocks during the rainy
season if the width of each block is
'1' .

The algorithm uses a two-pointer

124
Beginner's level

approach to traverse the array from


left and right simultaneously and
calculates the water trapped at each
position. The time complexity of this
algorithm is 0(N) and the space
complexity is 0(1).

Importance of Trapping Rain Water


Algorithm in Real-world Applications:
The Trapping Rain Water algorithm has
practical applications in various
fields, including civil engineering
and architecture.

In civil engineering, it can be used


to design drainage systems and
calculate water flow in flood-prone
areas. In architecture, it can be used
to design buildings that can withstand
heavy rainfall without damage or
flooding. The algorithm can also be
applied in the analysis of terrain and
the design of irrigation systems.
Additionally, it has practical
applications in weather forecasting
and predicting flood events.

125
Beginner's level

DSA Challenge 25

Minimum Platforms:
Given arrival and departure times of
all trains that reach a railway
station. Find the minimum number of
platforms required for the railway
station so that no train is kept
waiting.Consider that all the trains
arrive on the same day and leave on
the same day. Arrival and departure
time can never be the same for a
train but we can have arrival time of
one train equal to departure time of
the other. At any given instance of
time, same platform can not be used
for both departure of a train and
arrival of another train. In such
cases, we need different platforms.

Your task is to write a function


'findPlatform()' which takes the
array arr[] (denoting the arrival
times), array dep[] (denoting the
departure times) and the size of the
array as inputs and returns the
minimum number of platforms required

127
Beginner's level

at the railway station such that no


train waits.
Note: Time intervals are in the 24­
hour format(HHMM) , where the first
two characters represent hour (between
00 to 23 ) and the last two characters
represent minutes (this may be > 59).

Our logic:
Here is the javascript program using
the given function to find the minimum
number of platforms required at a
railway station:

function findPlatform(arr, dep, n) {


// Sort the arrival and departure
times in ascending order.
arr.sort((a, b) => a - b);
dep.sort((a, b) => a - b);
// Initialize variables to keep
track of minimum platforms needed
// and currently occupied platforms.

128
Beginner's level

let platformsNeeded = 1 ;
let platformsOccupied = 1 ;
// Start from the second train and
check if there is a clash with the
previous train.

let i = 1 ;
let j = 0;
while (i < n && j < n) {
if (arr[i] <= dep[j ]) {
// If the current train arrives
before the previous train departs,
// a new platform is needed.
platformsNeeded++;

if (platformsNeeded >
platformsOccupied) {

129
Beginner's level

} else {
// If the current train arrives
after the previous train departs,
// the previous platform is
freed up and can be used again.
platformsNeeded--;

j++;
}
}
return platformsOccupied;

This program takes three inputs:


'arr‘, 'dep', and 'n'. The 'arr' array
represents the arrival times of the
trains, the 'dep' array represents the
departure times of the trains, and 'n'
represents the size of both arrays.
The function first sorts the arrival

130
Beginner's level

and departure times in ascending


order. It then uses two pointers, i
and j, to iterate over the arrival and
departure times. The 'platformsNeeded'
variable keeps track of the number of
platforms needed at any given time,
and the 'platformsOccupied' variable
keeps track of the maximum number of
platforms occupied at any given time.
The function returns the
'platformsOccupied' variable as the
minimum number of platforms needed to
avoid any clashes between trains.

Importance of Minimum Platforms


Algorithm in Real-world Applications:
The minimum platforms algorithm is
essential in scheduling and managing
the arrival and departure of trains at
a railway station. It helps to ensure
that trains arrive and depart on time,
with the least possible waiting time.
This can help to reduce congestion at
the station and improve the overall
efficiency of the railway system. The

131
Beginner's level

algorithm can also be applied to other


real-world scenarios where resources
need to be allocated efficiently and
without conflicts.

132
Beginner's level

DSA Challenge 26

Prak element:
An element is called a peak element
if its value is not smaller than the
value of its adjacent elements(if
they exists).Given an array arr[] of
size N, Return the index of any one
of its peak elements.Note: The
generated output will always be 1 if
the index that you return is correct.
Otherwise output will be 0.

Your Task: Write a function


peakElement() which takes the array
arr ] and its size N as input
parameters and return the index of
any one of its peak elements.

Our logic:

function peakElement(arr, N) {
// Check if first or last element is
peak

134
Beginner's level

if (arr[N - 1] >= arr[N - 2]) {


return N - 1 ;

//Check for peak in middle elements


for (let i = 1; i < N - 1; i++) {
if (arr[i] >= arr[i - 1] && arr[i]
>= arr[i + 1 ]) {

return i;

}
}

// No peak found
return -1;

135
Beginner's level

The function first checks if the first


or last element is a peak. If either
of them is, it returns the index of
that element. If not, it checks for a
peak in the middle elements of the
array. It does this by iterating over
the array from index 1 to index N-2,
and checking if the current element is
greater than or equal to its adjacent
elements. If it is, it returns the
index of that element. If no peak is
found, the function returns -1 .

Importance of Peak Element Algorithm


in Real-world Applications:
Finding peak elements can be useful in
a variety of real-world applications.
Here are a few examples:

1 . Signal Processing: Peak detection


algorithms are often used in signal
processing to find peaks in a
waveform or a signal. For example,
in audio processing, peak detection
can be used to identify the highest

136
Beginner's level

2. Stock Market Analysis: Peak


detection can be useful in
analyzing stock market trends. For
example, if we can identify a peak
in a particular stock's price, we
can use that information to make
decisions about buying or selling
that stock.
3. Image Processing: Peak detection
can be used in image processing to
detect the highest point or region
of interest in an image. For
example, peak detection can be used
to identify the brightest spot in
an image or the most intense point
in a particular color channel.
4. Machine Learning: Peak detection
can be used in machine learning
algorithms for feature extraction.
For example, in computer vision,
peak detection can be used to
extract salient features from an
image, which can be used to train a
machine learning model to recognize
objects in that image.

137
Beginner's level

DSA Challenge 27

Common Elements:
Given three arrays sorted in
increasing order. Find the elements
that are common in all three arrays.

Your task is to write a function


'commonElements()' which take the 3
arrays A[], B[], C[] and returns an
array containing the common element
present in all the 3 arrays in sorted
order. If there are no such elements
return an empty array.

Our logic:

function commonElements(A, B, C) {
const common = [];
let i = 0,

j = 0.
k = 0;

139
Beginner's level

while (i < A.length && j < B.length


&& k < C.length) {
if (A[i] === B[j] && B[j] ===
C[k]) {
common.push(A[i]);

k++;
} else if (A[i] <= B[j] && A[i] <=
C[k]) {
1++ ,

} else if (B[j] <= A[i] && B[j] <=


C[k]) {

J I

} else {

140
Beginner's level

k++;

}
}
return common;

The function takes three sorted arrays


'A', B', and 'C' as input and returns
an array containing the common
elements in all three arrays in sorted
order.
The function uses three pointers, 'i',
'j', and 'k' to iterate over the
arrays. It starts by comparing the
first elements of all three arrays. If
they are equal, it adds the element to
the common array and moves to the next
element in each array.

If they are not equal, it advances the


pointer for the smallest element.

141
Beginner's level

Finally, we remove duplicates from


result using the Set data structure
and the spread operator.

If no common elements are found, an


empty array is returned.

Importance of Common Elements


Algorithm in Real-world Applications:
Finding common elements between arrays
is a fundamental problem in computer
science and has various real-world
applications.

One of the most common applications is


in data analysis and processing, where
datasets from different sources need
to be compared for commonalities. For
example, finding common genes across
multiple genomes or identifying common
customer behavior across different
sales channels.

Another application is in networking

142
Beginner's level

and distributed systems, where nodes


need to communicate and share
information.

Finding common elements between the


sets of data can help optimize
communication and improve overall
system performance. The problem also
arises in natural language processing,
where common words across multiple
texts need to be identified for text
classification and summarization.

143
Beginner's level

DSA Challenge 28

Hash from two arrays:


Given two arrays, create a hash
object where the elements from one
array (the keys) are linked to the
elements of the other (the values).

Example:

const firstArr =[1,2, 3];


const secondArr = ["a", ”b', c"];

const hashObj = arrToObj(firstArr,


secondArr);

The hash object created from the


arrays abcve will be '{1: "a", 2:
"b", 3: "c"}' •

145
Beginner's level

Our logic:

function arrToObj(keys, vals) {

const obj = {};

for (let i = 0; i < keys.length; i+

+) {
obj[keys[i ] ] = vals[i];

}
return obj;

The function takes two arrays as


input, 'keys' and 'vals', and creates
a new object 'obj'. Then it loops
through the keys array and assigns
the corresponding value from 'vals'
to the property of 'obj' with the
same index. Finally, it returns the
'obj' object containing the key-value
pairs from the two arrays.

146
Beginner's level

Importance of Hash from Two Arrays


Algorithm in Real-world Applications:
The Hash from two arrays algorithm is
a very useful technique in many real-
world applications, especially in web
development. For example, it can be
used to create key-value pairs from
form data submitted by users. The keys
could represent the input field names,
and the values would be the
corresponding values entered by the
user. The resulting hash object can
then be easily processed or stored in
a database.

Another common use case is when


working with APIs that return data in
JSON format. Often, this data needs to
be transformed or processed in some
way, and creating a hash object from
the returned JSON data can be a very
efficient way to do this. By mapping
certain JSON properties to specific
keys in the hash object, developers
can quickly extract the data they need

147
Beginner's level

and work with it in a more convenient


format.

Overall, the Hash from t^o arrays


algorithm is a powerful technique that
can be used in a wide range of
applications, wherever there is a need
to link data from two separate arrays
together in a meaningful way.

148
Beginner's level

DSA Challenge 30

Average Salary:
You are given an array of unique
integers salary where salary[i] is
the salary of the ith employee.

Write a program that will return the


average salary of employees excluding
the minimum and maximum salary.
Note: Answers within 10“5 of the
actual answer will be accepted.

Our logic:

function
calculateAverageSalary(sala ry) {
// Sort the salary array in
ascending order
salary.sort(function(a, b) { return

a - b; });
// Remove the minimum and maximum
salaries from the array

150
Beginner's level

salary.shift();
salary.pop();
let sum = 0;
for (let i = 0; i < salary.length;

i++) {
sum += salary[i];

}
let average = sum / salary.length;
return average;

The ‘calculateAverageSalary' function


takes an array of salaries as input
and returns the average salary of the
employees, excluding the minimum and
maximum salaries.

The function first sorts the input


array in ascending order using the

151
Beginner's level

’sort()' method. This is done so that


the minimum and maximum salaries can
be easily identified and removed from
the array.

After sorting the array, the function


removes the first element (minimum
salary) using the 'shift()' method and
removes the last element (maximum
salary) using the pop()' method. The
resulting array now contains only the
salaries between the minimum and
maximum values.

Next, the function calculates the sum


of the remaining salaries using the
'reduce()' method. The 'reduce()'
method iterates over the array,
applying a provided function to each
element and accumulating the results.
In this case, the function simply adds
the current element to the previous
value.

Finally, the function calculates the

152
Beginner's level

average salary by dividing the sum of


the remaining salaries by the length
of the array. The ‘toFixed()' method
is used to round the result to five
decimal places, as specified in the
problem statement.

Importance of this Algorithm in Real-


world Applications:
The ‘calculateAveragesalary' algorithm
is essential for analyzing salary data
in various domains, such as human
resources and financial management. It
determines the average salary of
employees, aiding in negotiations and
fair compensation. This versatile
algorithm can be applied to any
numerical dataset by excluding the
minimum and maximum values. It
showcases the power of programming
constructs in making informed
decisions and analyzing data across
different real-world applications.

153
Beginner's level

DSA Challenge 31

Median of Two Sorted Arrays:


Given two sorted arrays 'numsl' and
'nums2‘ of size 'm' and 'n'
respectively, return the median of
the two sorted arrays.

Our logic:
To find the median of two sorted
arrays 'numsl' and 'nums2', you can
combine the arrays and then sort
them. The median of the combined
array can then be determined based on
whether the total number of elements
is even or odd.

If the total number of elements is


odd, the median is simply the middle
element of the combined and sorted
array. If the total number of
elements is even, the median is the
average of the middle two elements of
the combined and sorted array.

155
Beginner's level

Here is an example of how this can be


implemented in JavaScript:

function
findMedianSortedArrays(nums1, nums2) {
const combined = [...numsl,
...nums2].sort((a, b) => a - b);
const total = combined.length;
const middle = Math.floor(total /

2);
if (total % 2 === 0) {

return (combined[middle] +
combined[middle - 1]) / 2;
} else {
return combined[middle] ;

}
}

156
Beginner's level

Importance of Hash from Two Arrays


Algorithm in Real-world Applications:
The median of two sorted arrays
algorithm has many real-world
applications in various fields,
including finance, economics, and
healthcare. Here are a few examples:

1. Financial Analysis: In the field of


finance, the median is an important
metric used to analyze data. For
example, a financial analyst might
use this algorithm to determine the
median salary of employees at a
company, or to analyze the median
price of a certain stock over a
period of time.
2. Economic Research: Economists often
use the median to analyze data
related to income, wealth, and
other economic indicators. For
example, this algorithm might be
used to calculate the median income
of a certain demographic group or
to analyze the median home prices

157
Beginner's level

• in a certain region.
3. Healthcare: In the medical field,
the median is used to analyze data
related to patient outcomes and to
evaluate the effectiveness of
treatments. For example, this
algorithm might be used to
calculate the median length of stay
for patients in a hospital or to
analyze the median survival time
for patients with a certain
disease.

Overall, the median of two sorted


arrays algorithm is a powerful tool
for analyzing data in a variety of
real-world applications. By providing
a way to determine the middle value(s)
of a set of data, this algorithm helps
to provide insight into trends,
patterns, and other important
information.

158
Beginner's level

DSA Challenge 32

Longest Palindromic Substring:


Given a string s, return the longest
palindromic substring in s.

Note: A string is palindromic if it


reads the same forward and backward.

Our logic:
To find the longest palindromic
substring in a given string s', we
can use the following algorithm:
1 . Create a variable
'maxPalindromicSubstring' and set
it to an empty string.
2. Loop through each character in the
string 's':
a. For each character, consider it as
the center of a potential palindromic
substring.
b. Expand the potential palindromic
substring outward from the center
until it is no longer a palindrome.

160
Beginner's level

c. If the length of the resulting


palindrome is greater than the length
of maxPalindromicSubstring, update
maxPalindromicSubstring to be the new
palindrome.
3. After looping through all
characters, return
maxPalindromicSubstring.

Here's the implementation of the


algorithm in JavaScript:

function
longestPalindromicSubstring(s) {
let maxPalindromicSubstring = ’';
for (let 1=0; i < s.length; i++) {
// odd-length palindromes
let left = i, right = i;

while (left >= 0 && right < s.length


&& s[left] === s[right]) {
left--;

161
Beginner's level

right++;

}
let oddLengthPalindrome =
s.slice(left + 1, right);
// even-length palindromes
left = i;
right = i + 1 ;
while (left >= 0 && right <
s.length && s[left] === s[right]) {
left--;
right++;

}
let evenLengthPalindrome =
s.slice(left + 1, right);
if (oddLengthPalindrome.length >
maxPalindromicSubstring.length) {
maxPalindromicSubstring =

162
Beginner's level

oddLengthPalindrome;

}
if (evenLengthPalindrome.length >
maxPalindromicSubstring.length) {
maxPalindromicSubstring =
evenLengthPalindrome;

}
}
return maxPalindromicSubstring;

Importance of this Algorithm in Real-


world Applications :
This algorithm has practical
applications in various fields,
including natural language processing,
genetics, and data compression. For
example, in natural language
processing, this algorithm can be used

163
Beginner's level

to identify palindromic phrases or


words, which can provide insights into
the structure and meaning of the
language.

In genetics, this algorithm can be


used to identify palindromic DNA
sequences, which can have important
implications for gene expression and
regulation.

In data compression, this algorithm


can be used to identify repeating
patterns in data, which can help to
reduce the size of the data and
improve storage and transmission
efficiency.

164
Beginner's level

DSA Challenge 33

Reverse Integer:
Given a signed 32-bit integer x,
return x with its digits reversed. If
reversing x causes the value to go
outside the signed 32-bit integer
range [-231, 231 - 1], then return 0.

Note: Assume the environment does not


allow you to store 64-bit integers
(signed or unsigned).

Example 1:
Input: x = 123
Output: 321

Example 2:
Input: x = -123
Output: -321

Example 3:
Input: x = 120
Output: 21

166
Beginner's level

Our logic:

function reverse(x) {
let reversed = 0;
const isNegative = x < 0;
x = Math.abs(x);

while (x > 0) {
reversed = reversed * 10 + x % 10;
x = Math.floor(x / 10);

if (reversed >2**31 - 1) {
return 0;

}
return isNegative ? -reversed :
reversed;

167
Beginner's level

This algorithm first checks whether


the input integer is negative or not,
and stores that information in a
variable. It then takes the absolute
value of the integer to ensure that
the algorithm works correctly
regardless of the sign of the input.

The algorithm then repeatedly takes


the remainder of the input integer
when divided by 10, and appends that
to a new integer by multiplying the
existing integer by 10 and adding the
remainder. This effectively reverses
the digits of the input integer.

Finally, the algorithm checks whether


the reversed integer is within the
bounds of a 32-bit signed integer,
and returns 0 if it is not. If the
input integer was originally
negative, the algorithm multiplies
the reversed integer by -1 to return
a negative integer with reversed
digits.

168
Beginner's level

This algorithm first checks whether


the input integer is negative or not,
and stores that information in a
variable. It then takes the absolute
value of the integer to ensure that
the algorithm works correctly
regardless of the sign of the input.

The algorithm then repeatedly takes


the remainder of the input integer
when divided by 10, and appends that
to a new integer by multiplying the
existing integer by 10 and adding the
remainder. This effectively reverses
the digits of the input integer.

Finally, the algorithm checks whether


the reversed integer is within the
bounds of a 32-bit signed integer,
and returns 0 if it is not. If the
input integer was originally
negative, the algorithm multiplies
the reversed integer by -1 to return
a negative integer with reversed
digits.

168
Beginner's level

Importance of Reverse Integer


Algorithm in Real-world Applications:
The reverse integer algorithm has
several real-world applications,
particularly in the field of data
processing and data manipulation. Here
are some examples:

1. Financial Transactions: Financial


transactions such as bank
transfers, money transfers, and
online payments often involve large
numbers, and sometimes it is
required to reverse the digits of
the transaction amount for auditing
or error-checking purposes.
2. Telecommunications:
Telecommunication systems often
deal with large numbers, such as
phone numbers and IP addresses. The
reverse integer algorithm can be
used to manipulate these numbers
for various purposes such as
routing and billing.

169
Beginner's level

3. Data Science: In data science, it


is sometimes required to manipulate
large datasets that contain
numerical values. The reverse
integer algorithm can be used to
reverse the digits of these
numbers, making them easier to
analyze and manipulate.
4. Cryptography: Cryptography involves
the use of large numbers to encrypt
and decrypt messages. The reverse
integer algorithm can be used to
manipulate these numbers, making
them more secure and less
vulnerable to attacks.

Overall, the reverse integer algorithm


is a powerful tool that has many real-
world applications in various fields,
particularly in situations where large
numbers need to be processed or
manipulated.

170
Beginner's level

DSA Challenge 34

Container With Most Water:


You are given an integer array height
of length 'n'. There are n' vertical
lines drawn such that the two
endpoints of the ith line are '(i,
0)' and '(i, height[i])'.
Find two lines that together with the
x-axis form a container, such that
the container contains the most
water.
Return the maximum amount of water a
container can store.

Notice that you may not slant the


container.

Our Ionic:
function maxArea(height) {
let maxArea = 0;
let left = 0;
let right = height.length - 1;

172
Beginner's level

while (left < right) {


const width = right - left;
const area = width *
Math.min(height[left]( height!right]);
maxArea = Math.max(maxArea, area);

if (height[left] < height[right])

{
left++;

} else {
right--;

}
}

return maxArea;

173
Beginner's level

The ‘maxArea’ algorithm finds the two


vertical lines in the input array
that, when paired with the x-axis,
form a container with the maximum
amount of water that can be stored
within it. It does this by using the
two-pointer technique.

The algorithm first initializes two


pointers, one at the beginning of the
array and one at the end. It then
calculates the area of the container
formed by the two lines and updates
the maximum area if the current area
is greater than the previous maximum.

The next step is to move one of the


pointers towards the other, as moving
the pointer with the shorter height
will not increase the area, and a
greater area can only be achieved by
increasing the height of the shorter
line.

The process is repeated until the two

174
Beginner's level

pointers meet. At this point, the


algorithm returns the maximum area
found.

Importance of this Algorithm in Real-


world Applications:
The 'maxArea' algorithm for finding
the maximum amount of water that can
be stored in a container has practical
applications in a variety of fields,
such as civil engineering,
architecture, and urban planning.

For example, the algorithm can be used


to determine the optimal design for
water tanks or reservoirs, ensuring
that they can store the maximum amount
of water possible within the available
space. It can also be used to optimize
the design of drainage systems, by
calculating the maximum amount of
water that can be drained from a
particular area.

In addition, the algorithm can be

175
Beginner's level

applied in the field of environmental


management, such as in the design of
green roofs or rain gardens, to
determine the optimal size and shape
of the container for storing
rainwater.

Overall, the maxArea' algorithm is a


powerful tool for optimizing the
design of water storage and drainage
systems, and can help improve the
efficiency and sustainability of urban
infrastructure.

176
Beginner's level

DSA Challenge 35

Integer to Roman:
Roman numerals are represented by
seven different symbols: 'I', 'V',
'X', 'L', 'C', D' and 'M'.

Symbol Value
I 1
V 5
X 10
L. 50
C 100
D 500
M 1000

Roman numerals are usually written


largest to smallest from left to
right. However, the numeral for four
is not 'IIII'. Instead, the number
four is written as 'IV'. Because the
one is before the five we subtract it
making four. The same principle

178
Beginner's level

applies to the number nine, which is


written as 'IX'. There are six
instances where subtraction is used:

• 'I' can be placed before 'V' (5)


and 'X' (10) to make 4 and 9.
• 'X' can be placed before L (50)
and 'C' (100) to make 40 and 90.
• 'C' can be placed before 'D' (500)
and 'M' (1000) to make 400 and 900.

Your task: Given an integer, write a


function to convert it to a roman
numeral.

Our logic:

function intToRoman(num) {
const values [1000, 900, 500, 400,
100, 90, 50, 40, 10,
const symbols "CM", D",
CD", "XC", " XL", "X", "IX",
V", "IV",

179
Beginner's level

while (num > 0) {


if (num >= values[index]) {
romanNumeral += symbols[index] ;
num -= values[index] ;
} else {
index++;

}
}
return romanNumeral;

The 'intToRoman' function uses two


arrays, one for the Roman numeral
symbols and another for their
corresponding values. It starts by
initializing an empty string to store
the resulting Roman numeral and an
index variable to keep track of the
current position in the values and
symbols arrays.

180
Beginner's level

The function then enters a loop that


continues while the input number is
greater than zero. At each iteration,
it checks if the current value in the
values array is less than or equal to
the input number. If it is, it adds
the corresponding symbol to the
result string and subtracts the value
from the input number. If it's not,
it moves on to the next index in the
arrays.

Finally, it returns the resulting


Roman numeral as a string.

Importance of Integer to Roman


Algorithm in Real-world Applications:
The Integer to Roman algorithm has
several real-world applications,
including:

1 . Historical and cultural


preservation: Roman numerals are an
essential part of ancient Roman
culture, and they are still used

181
Beginner's level

2. Printing and publishing: Roman


numerals are commonly used in
printing and publishing to indicate
chapter numbers, page numbers, and
publication dates. Converting
integers to Roman numerals can help
simplify the formatting of these
documents.
3. Clocks and watches: Roman numerals
are often used to display the hours
on clocks and watches, especially
on traditional or antique
timepieces. Converting integers to
Roman numerals can help in the
design and manufacturing of these
timepieces.
4. Educational purposes: Converting
integers to Roman numerals can be a
useful educational tool for
teaching math, historys and
language arts. It can also be used
in games and puzzles to challenge
and entertain students.

182
Beginner's level

DSA Challenge 36

Longest Common Prefix:


Write a function to find the longest
common prefix string amongst an array
of strings.
If there is no common prefix, return
an empty string .

Example 1 :

Input: strs =
["flower"/flow" /’flight” ]
Output: "fl"

Example 2:

Input: strs = ["dog"racecar","car"]


Output: ""
Explanation: There is no common prefix
among the input strings.

184
Beginner's level

Our logic:
function longestCommonPrefix(strs) {

if (!strs || strs.length === 0)


return

let prefix = strs[0];

for (let i = 1; i < strs.length;

i++) {
while (strs[i].indexOf(prefix)

!== 0) {
prefix =
prefix.substring(0, prefix.length -

D;
if (prefix === "") return
Illi •
I

}
return prefix;

185
Beginner's level

The function takes an array of


strings 'strs' as input and returns
the longest common prefix of all the
strings in the array. It first checks
if the input array is empty or null,
and returns an empty string in that
case.

If the array has at least one string,


it initializes the 'prefix' variable
to the first string in the array. It
then iterates over the remaining
strings in the array and checks if
the current prefix is a substring of
each string. If not, it removes the
last character from the prefix and
tries again. If the prefix becomes
empty, there is no common prefix and
the function returns an empty string.

If the function completes the loop


without returning, it means that the
prefix is common to all the strings
in the array and is returned as the
result.

186
Beginner's level

Importance of Longest Common Prefix


Algorithm in Real-world Applications:
The longest common prefix algorithm
has several applications in the real
world. One of the most common
applications is in the field of
bioinformatics, where it is used to
compare DNA sequences. In this case,
the algorithm can be used to find the
longest common prefix between two or
more DNA sequences. This information
can be used to determine the
similarity between different sequences
and to identify patterns and
relationships between them.

The algorithm is also used in text


processing applications, such as
search engines, where it can be used
to compare and match strings. For
example, when a user types a search
query into a search engine, the engine
may use the longest common prefix
algorithm to match the query against
the database of indexed documents.

187
Beginner's level

In addition, the algorithm is used in


network routing protocols, where it is
used to compare network addresses and
to determine the common prefix between
them. This information is used to
determine the most efficient routing
path between different network nodes.

Overall, the longest common prefix


algorithm is a versatile and important
tool with many real-world applications
in various fields, including computer
science, bioinformatics, and network
engineering.

188
Beginner's level

DSA Challenge 37

3Sum:
Given an integer array nums, return
all the triplets [nums[i]f numsfj],
nums[k]] such that i != j, i != k(
and j != k, and nums[i] + nums[j] +
nums[k] == 0.
Notice that the solution set must not
contain duplicate triplets.

Example:

Input: nums - [-1,0,1,2,-1,-4]


Output: [[-1,-1,2],[-1,0,1]]
Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0
+ 1=0.
nums[1] + nums[2] + nums[4] = 0 + 1 +

(-1) = 0-
nums[0] + nums[3] + nums[4] = (-1) + 2
+ (-1) = 0.

190
Beginner's level

The distinct triplets are [-1,0,1] and


[-1,-1,2].

Notice that the order of the output


and the order of the triplets does not
matter.

Our logic:

function threeSum(nums) {
nums.sort((a, b) => a - b);
let res = [ ] ;
for (let i = 0; i < nums.length -

2; i++) {
if (i === 0 || (i > 0 && nums[i] !==
nums[i - 1])) {

191
Beginner's level

let lo = i + 1 ;
let hi = nums.length - 1;
let sum = 0 - nums[i];
while (lo < hi) {
if (nums[lo] +
nums[hi] === sum) {

res.push([nums[i](
nums[lo], nums[hi]]);
while (lo < hi &&
nums[lo] === nums[lo +1]) lo++;
while (lo < hi &&
nums[hi] === nums[hi - 1]) hi--;
lo++;
hi--;

} else if (nums[lo] + numsfhi] < sum)

192
Beginner's level

let lo = i + 1 ;
let hi = nums.length - 1;
let sum = 0 - nums[i];
while (lo < hi) {
if (nums[lo] +
nums[hi] === sum) {

res.push([nums[i](
nums[lo], nums[hi]]);
while (lo < hi &&
nums[lo] === nums[lo +1]) lo++;
while (lo < hi &&
nums[hi] === nums[hi - 1]) hi--;
lo++;
hi--;

} else if (nums[lo] + numsfhi] < sum)

193
Beginner's level

10++ •
} else {
hi--;

}
}
}
}
return res;

The ’threeSum' function uses the two-


pointer technique to solve the
problem. We first sort the array in
ascending order, and then loop through
the array, fixing one number at a
time.

We then use two pointers, one starting


from the number after the fixed number
and the other starting from the end of
the array, and move them towards each

194
Beginner's level

while checking if their sum equals the


negation of the fixed number. If the
sum equals the negation of the fixed
number, we add the triplet to the
result array. We also avoid adding
duplicate triplets by skipping over
any numbers that are equal to their
preceding number. Finally, we return
the result array.

The time complexity of this algorithm


is 0(nA2), where n is the length of
the input array, because we are
looping through the array and using
two pointers to traverse the remaining
part of the array.

Importance of 3Sum Algorithm in Real-


worId Applications:
The algorithm for finding triplets
that sum to zero has applications in
various fields such as data analysis,
financial analysis, and image
processing.

195
Beginner's level

For example, in data analysis, this


algorithm can be used to find groups
of items that have a certain
relationship.

In financial analysis, this algorithm


can be used to find stocks that are
highly correlated or anti-correlated.

In image processing, this algorithm


can be used to identify groups of
pixels that form specific shapes or
patterns.

Therefore, the algorithm has practical


applications in various fields and is
a fundamental algorithm in computer
science.

196
Beginner's level

DSA Challenge 38

4Sum:
Given an array nums of n integers,
return an array of all the unique
quadruplets [nums[a], nums[b],
nums[c], nums[d]] such that:
• C <= a, b, c, d < n
• a, b, c, and d are distinct.
• nums[a] + nums[b] + nums[c] +
nums[d] == target
You may return the answer in any
order.

Our logic:

function fourSum(nums, target) {


const result = new Set();
nums.sort((a, b) => a - b);
const n = nums.length;

for (let i = 0; i < n - 3; i++) {

198
Beginner's level

for (let j = i + 1; j < n - 2; j++) {


let left = j + 1 ;
let right = n - 1 ;

while (left < right) {


const sum = nums[i] + nums[j] +
nums[left] + nums[right];

if (sum === target) {


result.add([nums[i], nums[j],
nums[left], nums[right]].toString());

left++;
right--;
} else if (sum < target) {

left++;

199
Beginner's level

else {
right--;

}
}
}
}

return Array.from(result).map(item
=> item.split(1,*).map(num =>
parselnt(num)));

The 'fourSunT function takes an array


of integers and a target integer as
input and returns an array of arrays
containing unique quadruplets of
integers whose sum is equal to the
target integer.

200
Beginner's level

The algorithm first sorts the input


array and iterates through each
element of the array, and then for
each element, it uses a 'threeSum'
function to find unique triplets whose
sum is equal to the target integer
minus the current element. The
‘fourSum' function then combines each
found triplet with the current element
to form a quadruplet and adds it to
the result array. The algorithm
eliminates duplicate quadruplets using
a set data structure.

Importance of 4Sum Algorithm in Real-


world Applications:
The 'fourSum' algorithm can be applied
in various real-life applications
where there is a need to find all
unique combinations of four numbers
from an array that add up to a
specific target value. For instance,
in finance, it can be used to identify
stocks with a particular price that

201
Beginner's level

would provide a specific return when


combined with other stocks. It can
also be used in data analysis to find
four distinct variables that have a
specific sum.

Additionally, the algorithm can be


used in image processing and computer
vision for feature detection, where it
is required to identify groups of four
points that share a particular
relationship.

202
Beginner's level

DSA Challenge 39

Letter Combinations of a Phone Number:


Given a string containing digits from
'2-9' inclusive, return all possible
letter combinations that the number
could represent. Return the answer in
any order.

A mapping of digits to letters (just


like on the telephone buttons) is
given below. Note that 1 does not map
to any letters.

204
Beginner's level

Our logic:

function letterCombinations(digits) {
if (digits.length === 0) return [];

const map = {

};
function backtrack(index, current) {
if (index === digits.length) {

result.push(current);

205
Beginner's level

return;
}

const letters =
map[digits[index]];
for (let i = 0; i <
letters.length; i++) {
backtrack(index + 1, current +
letters[i]);
}
}

const result = [ ] ;
backtrack(0, ’1);
return result;
}

We first check if the input digits is


an empty string and return an empty
array if so. We then create a mapping
of digits to letters using an object.

The 'backtrack' function takes two

206
Beginner's level

arguments: the index of the current


digit we are iterating over, and the
current string we have built so far.
If we have iterated over all the
digits, we add the current string to
the result array and return.
Otherwise, we iterate over each letter
corresponding to the current digit,
and call 'backtrack' with the next
index and the updated string.

We then initialize an empty result


array, and call the backtrack function
with an initial index of 0 and an
empty string. Finally, we return the
result array containing all possible
combinations of letters.

Importance of this Algorithm in Real-


world applications:
The algorithm for generating all
possible letter combinations of a
given phone number can be useful in
various real-world applications such

207
Beginner's level

as phonebook applications, automated


customer service systems, and phone
verification systems. In these
applications, it can be used to
generate all possible combinations of
letters that can correspond to a given
phone number so that the system can
recognize different variations of a
name or a word that a user may input
using the phone keypad.

Additionally, this algorithm can be


used in data analysis applications to
preprocess data by generating all
possible combinations of letters and
numbers that can be associated with a
given phone number.

208
Beginner's level

DSA Challenge 40

Divide Two Inteqers:


Given two integers 'dividend' and
'divisor', divide two integers
without using multiplication,
division, and mod operator.

The integer division should truncate


toward zero, which means losing its
fractional part. For example, 8.345'
would be truncated to '8', and
'-2.7335' would be truncated to '-2'.

Return the quotient after dividing


'dividend' by 'divisor'.
Note: Assume we are dealing with an
environment that could only store
integers within the 32-bit signed
integer range: '[-231, 231 - 1]'. For
this problem, if the quotient is
strictly greater than '231 - 1', then
return '231 - 1', and if the quotient
is strictly less than '-231 , then
return ' -231 .

210
Beginner's level

Our logic:
One possible solution for this
problem is to use bit manipulation to
perform the division operation
without using multiplication,
division, and mod operators.

Here’s a JavaScript function that


implements this approach:

function divide(dividend, divisor) {


if (divisor === 0) {
return NaN;

if (dividend === -2147483648 &&


divisor === -1) {

return 2147483647;

}
const isNegative = (dividend < 0) !==
(divisor < 0);

211
Beginner's level

let quotient = 0;
for (let i = 31; i >= 0; i--) {
if ((dividend >>> i) - divisor

>= 0) {
quotient |= (1 << i);
dividend -= (divisor <<

i):

return isNegative ? -quotient :

quotient;

The function first handles the special


cases of division by zero and the
minimum 32-bit signed integer divided
by -1, which would overflow.

212
Beginner's level

Then, it determines whether the result


will be negative by checking the signs
of the dividend and divisor.

The main part of the algorithm uses a


loop to iterate over the 32 bits of
the dividend, from the most
significant bit to the least
significant bit. It checks whether the
divisor can be subtracted from the
current dividend, shifted by the
current bit position, without making
the result negative. If so, it sets
the corresponding bit of the quotient
and subtracts the product of the
divisor and the bit position from the
dividend.

Finally, the function returns the


quotient with the correct sign.

213
Beginner's level

Importance of Divide Two Integers


Algorithm in Real-world Applications:
The divide two integers algorithm has
several real-world applications,
including:

1 . Computer graphics: In computer


graphics, the algorithm is used to
render high-quality images, videos,
and animations. Dividing two
integers is used to calculate the
aspect ratio, which determines the
size and shape of the image.
2. Finance: In finance, the algorithm
is used to calculate interest
rates, investment returns, and loan
payments. It is also used to
calculate financial ratios, such as
price-to-earnings ratios and debt-
to-equity ratios.
3. Engineering: In engineering, the
algorithm is used to calculate
measurements and design structures.
It is also used to calculate the
speed, acceleration, and force of

214
Beginner's level

• moving objects.
4. Scientific computing: In scientific
computing, the algorithm is used to
solve complex mathematical
equations and perform numerical
simulations. It is used in a wide
range of fields, including physics,
chemistry, biology, and economics.

Overall, the divide two integers


algorithm is a fundamental operation
that is used in many real-world
applications. Its importance lies in
its ability to perform accurate and
efficient calculations that are
critical in various industries.

215
Beginner's level

DSA Challenge 41

Next Permutation:
Given an array of integers ’nurns',
find the next permutation of 'nurns'.

For reference, the next permutation


of an array of integers is the next
lexicographically greater permutation
of its integer.

For example:

• The next permutation of arr =


[1,2,3] is [1,3,2].

• Similarly, the next permutation of


arr = [2,3,1] is [3,1,2].

• While the next permutation of arr =


[3,2,1] is [1,2,3] because [3,2,1]
does not have a lexicographical
larger rearrangement.

217
Beginner's level

Our logic:
To find the next permutation of an
array of integers nums, we can follow
the below steps:
1. Traverse the array from right to
left and find the first element
nums[i] such that nums[i] <
nums[i+1].
2. If no such element exists, it means
that we have reached the last
permutation of the given array. In
this case, we can simply reverse
the array to get the first
permutation.
3. If such an element exists, we need
to find the smallest element in the
subarray nums[i+1:] which is
greater than nums[i]. We swap these
two elements.
4. Finally, we reverse the subarray
nums[i+1:] to get the
lexicographically next greater
permutation.

218
Beginner's level

Here's the JavaScript code to


implement the above algorithm:

function nextPermutation(nums) {
let i = nums.length - 2;
// Find the first element i such
that nums[i] < nums[i+l]
while (i >= 0 && nums[i] >=
nums[i+1]) {

i--;
}
if (i >= 0) {
// Find the smallest element in
nums[i+1:] which is greater than
nums[i ‘
let j = nums.length - 1;
while (j >= 0 && nums[j] <=
nums[i]) {

219
Beginner's level

// Swap numsfi] and nums[j]


[nums[i], nums[j]] = [nums[j],
nums[i]];

>
// Reverse the subarray nums[i+1:]
let left = i + 1 ;
let right = nums.length - 1;
while (left < right) {
[nums[left], nums[right]] =
[nums[right], nums[left]];
left++;
right--;

return nums;

220
Beginner's level

Importance of Next Permutation


Algorithm in Real-world Applications:
The Next Permutation algorithm has
various real-world applications,
including:

1. Generating Permutations: The Next


Permutation algorithm can be used
to generate permutations of a set
of elements. In computer science,
this can be useful in algorithms
related to sorting, searching, and
graph theory.
2. Cryptography: In cryptography, the
Next Permutation algorithm can be
used to generate keys for
encryption and decryption. It is
also used in various symmetric-key
encryption algorithms to generate a
series of keys.
3. Music Theory: The Next Permutation
algorithm can be used in music
theory to generate all possible
permutations of a given set of
notes. This can help in the

221
Beginner's level

• analysis and understanding of music


compositions.
4. Image Processing: In image
processing, the Next Permutation
algorithm can be used to generate
all possible permutations of image
pixels. This can be useful in
various image analysis algorithms,
including object recognition and
image segmentation.

Overall, the Next Permutation


algorithm is a powerful tool with
numerous real-world applications in
various fields, including computer
science, cryptography, music theory,
and image processing.

222
Beginner's level

DSA Challenge 42

Search in Rotated Sorted Array:


Given an integer array nums that is
sorted in ascending order and may be
rotated at an unknown pivot index k,
the resulting array becomes
'[nums[k], nums[k+1], nums[n-1],
nums[0], nums[1], nums[k-1]]'
(0-indexed).

You need to write an algorithm to


find the index of a given integer
'target' in the rotated array 'nums'
with 0(log n) runtime complexity. If
target is not in 'nums', return -1.

Our logic:

function searchRotatedArray(nums,
target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {

224
Beginner's level

let mid = Math.floor((left + right)

/ 2);
if (nums[mid] === target) {
return mid;

}
if (nums[left] <= nums[mid]) {
if (target >= nums[left] &&
target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1 ;

}
} else {
if (target > numsfmid] && target
<= nums[right]) {
left = mid + 1;

} else {

225
Beginner's level

right = mid - 1;

}
}
}
return -1;

This function takes an array 'nums'


and an integer 'target' as input and
returns the index of target in 'nums',
or '-1' if 'target' is not in 'nums'.
The function assumes that 'nums' is
sorted in ascending order (with
distinct values) and has been rotated
at some unknown pivot index.

The function uses the binary search


algorithm to efficiently search for
target in 'nums'. The key idea is to
keep track of the two ends of the
current subarray being searched,
namely 'left' and 'right', and to

226
Beginner's level

compute the middle index 'mid' as the


average of 'left' and 'right'. Then,
depending on the relative order of
'nums[left]', 'nums[mid]'f and
'nums[right]', the function updates
either 'left' or 'right' to narrow
down the search space. This process is
repeated until 'target' is found or
the search space is exhausted. The
time complexity of the function is
0(log n), where n is the length of
'nums', as required by the problem
statement.

Importance of this Algorithm in Real-


world Applications:
The algorithm for finding an element
in a rotated sorted array has
important applications in fields such
as search engines, databases, and data
analysis. For instance, when searching
a large dataset or database, this
algorithm can help to efficiently
locate the required data without
iterating over the entire dataset.

227
Beginner's level

It is also essential in sorting


algorithms that handle partially
sorted arrays, such as merge sort.

Additionally, the algorithm is used in


cryptography for data decryption,
where it helps in manipulating and
rearranging data to recover the
original information. Its significance
extends to domains like search
engines, databases, data analysis, and
secure data transmission. By
efficiently handling rotated sorted
arrays, it enables faster and
optimized operations in scenarios
involving searching and sorting of
data.

228
Beginner's level

DSA Challenge 43

Search Insert Position:


Given a sorted array of distinct
integers and a target value, return
the index if the target is found. If
not, return the index where it would
be if it were inserted in order.

You must write an algorithm with


0(log n) runtime complexity.

Our logic:
To solve this problem with 0(log n)
runtime complexity, we can use the
binary search algorithm. Here's a
JavaScript implementation:

function searchinsert(nums, target) {


let left = 0;
let right = nums.length - 1;
while (left <= right) {
let mid = Math.floor((left +

230
Beginner's level

right) / 2);
if (numsfmid] === target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1 ;
} else {
right = mid - 1 ;

return left;

In this algorithm, we maintain two


pointers, 'left' and 'right', which
represent the search range within the
array. We compute the middle index
mid and compare the value at 'mid'
with the 'target'.

231
Beginner's level

If they are equal, we return the index


'mid*. If the value at 'mid' is less
than the 'target', we update 'left' to
'mid + 1' to search the right half of
the array. If the value at 'mid' is
greater than the target, we update
'right' to 'mid - 1' to search the
left half of the array. We continue
this process until the target is found
or the search range is exhausted.

If the 'target' is not found, we


return the 'left' pointer, which
represents the index where the target
would be inserted in order. Since the
array is sorted, the left pointer will
always point to the correct position
for insertion.

This algorithm has 0(log n) runtime


complexity because at each step, we
divide the search range in half,
reducing the number of elements to
search by half each time.

232
Beginner's level

Importance of Search Insert Position


Algorithm in Real-world Applications:
The algorithm for finding the index to
insert a target value in a sorted
array has various real-world
applications. Here are a few examples:

1. Information Retrieval: In search


engines and databases, this
algorithm is commonly used to
quickly locate or insert data. It
allows for efficient searching or
insertion of new records based on
their order in the indexed data
structure.
2. Data Analysis and Statistics: When
analyzing sorted datasets or
performing statistical
computations, it is often necessary
to find the position where a new
data point would fit in the ordered
sequence. This algorithm enables
efficient insertion and maintenance
of sorted data structures used in
various statistical calculations.

233
Beginner's level

3. Online Shopping and E-commerce: E­


commerce platforms often use sorted
arrays or lists to store products
or customer data. This algorithm
can be utilized to quickly
determine the appropriate position
to insert a new product or customer
based on their ordering criteria,
such as price or name.
4. Gaming and Ranking Systems: In
online gaming or ranking systems,
players or entities are often
sorted based on their performance
or ratings. This algorithm can be
employed to determine the position
where a new player or entity should
be inserted in the ranked list.
5. Cryptography: Sorting and searching
algorithms are fundamental in
cryptography for various
operations, including key
management, secure data storage,
and encryption. This algorithm's
efficiency plays a crucial role in
cryptographic systems that involve

234
Beginner's level

• sorting or searching encrypted


data.

Overall, the importance of this


algorithm lies in its ability to
efficiently handle sorted arrays and
provide the correct index for
inserting a target value. It enables
faster and optimized operations in
scenarios where maintaining order or
finding the appropriate position is
critical, such as search engines,
databases, statistical analysis, e­
commerce, gaming, and cryptography.

235
Beginner's level

DSA Challenge 44

Count and Say:


Given a positive integer n, return
the nth term of the count-and-say
sequence.

The count-and-say sequence is a


sequence of digit strings defined by
the recursive formula:
• countAndSay(1) = "1"
• countAndSay(n) is the way you would
"say" the digit string from
countAndSay(n-1), which is then
converted into a different digit
string.

Our logic:
Here's a JavaScript implementation of
the countAndSay function:

function countAndSay(n) {
if (n === 1) {

return "1";

237
Beginner's level

let prevTerm = countAndSay(n - 1);


let count = 1 ;
let result = ,,,,;

for (let i = 0; i < prevTerm.length;

i++) {
if (prevTerm[i] === prevTerm[i +

1]) {
count++;
} else {
result += count + prevTerm[i];
count = 1 ;

}
}

return result;

238
Beginner's level

The 'countAndSay' function takes a


positive integer n' as input and
recursively computes the nth term of
the count-and-say sequence. The base
case is when 'n' equals '1', in which
case the function returns the string
II -1 II

For n > 1', the function calls itself


with 'n - 1' to obtain the previous
term of the sequence. It then iterates
over the digits of the previous term,
counting consecutive occurrences of
each digit. When a different digit is
encountered, the count and the digit
are concatenated to the result string.
The process continues until all digits
of the previous term have been
processed, and the final result is
returned.

This algorithm generates the count-


and-say sequence efficiently and
produces the desired digit string for
the given 'n'.

239
Beginner's level

Importance of the Count and Say


Algorithm in Real-world Applications:
The count-and-say algorithm is a
useful algorithm in certain scenarios.
Here are a few points to consider
regarding its importance:

1. Pattern Generation: The count-and-


say sequence generates a unique
pattern of digit strings. It can be
used in applications that require
generating and analyzing different
patterns, such as pattern
recognition, sequence analysis, or
mathematical research.
2. Coding Exercises: The count-and-say
algorithm is often used as an
exercise in coding interviews or
programming courses. It helps
developers enhance their problem­
solving skills, recursion
understanding, and string
manipulation abilities.

240
Beginner's level

• intuitive example of a recursive


algorithm. It can be used in
educational settings to introduce
students to recursion and how it
can be used to solve problems.
3. Algorithmic Thinking: The count-
and-say algorithm encourages
algorithmic thinking and
understanding of sequences and
patterns. It trains individuals to
decompose a problem into smaller
steps, apply rules to transform
data, and build a solution through
iteration or recursion.

While the count-and-say algorithm may


not have direct real-world
applications in practical domains, it
still serves a purpose in
strengthening programming skills,
fostering problem-solving abilities,
and promoting algorithmic thinking,
which are essential in various areas
of software development and computer
science.

241
Beginner's level

DSA Challenge 45

Rotate Image:
You are given an n x n 2D matrix
representing an image, rotate the
image by 90 degrees (clockwise).

You have to rotate the image in^


place, which means you have to modify
the input 2D matrix directly. DO NOT
allocate another 2D matrix and do the
rotation.

Input: matrix = [ [1,2,3],[4,5,6],


[7,8,9]]
Output: [[7,4,1],[8,5,2],[9,6,3]]

Our logic:
Here's the function to rotate a given
n x n matrix by 90 degrees clockwise:

function rotatelmage(matrix) {
const n = matrix.length;
// Transpose the matrix

243
Beginner's level

for (let 1=0; i < n; i++) {


for (let j = i; j < n; j++) {
const temp = matrix!i][j];
matrix[i][j] = matrix!j][i];
matrix!j][i] = temp;

}
}
// Reverse each row
for (let i = 0; i < n; i++) {
for (let j = 0; j < n / 2; j++) {
const temp = matrix[i ] [j ] ;
matrix[i] [ j] = matrix[i][n - 1 -

J]>
matrix[i][n - 1 - j] = temp;

}
}

244
Beginner's level

The algorithm begins by transposing


the matrix, which involves swapping
each element of the matrix with its
corresponding element along the main
diagonal. This operation effectively
reflects the matrix over its main
diagonal. For example, if we have a
matrix like this:

1 2 3
4 5 6
7 8 9

After transposing, it becomes:

1 4 7
2 5 8
3 6 9

Next, the algorithm reverses each row


of the transposed matrix. This
operation flips the elements
horizontally. For example, if we take
the transposed matrix from the
previous step:

245
Beginner's level

Next, the algorithm reverses each row


of the transposed matrix. This
operation flips the elements
horizontally. For example, if we take
the transposed matrix from the
previous step:

1 4 7
2 5 8
3 6 9

After reversing each row, it becomes:

7 4 1
8 5 2
9 6 3

The combination of transposing and


reversing the rows achieves the
desired rotation of the image by 90
degrees clockwise.

In terms of time complexity, the


algorithm has a nested loop structure
that iterates over each element of the
matrix. Since the matrix has n x n

246
Beginner's level

elements, the time complexity is


0(nA2) .

One notable advantage of this


algorithm is that it performs the
rotation in-place, meaning it modifies
the original matrix without requiring
any additional space. This property
makes it efficient in terms of memory
usage, as it does not require
allocating a new matrix or using
additional data structures.

Importance of Rotate Image Algorithm


in Real-world Applications:
The rotate image algorithm has several
important real-world applications in
various domains. Here are a few
examples:

1. Image Processing: The algorithm is


commonly used in image processing
tasks to rotate images. It allows
images to be transformed and
manipulated without the need for

247
Beginner's level

• additional memory or creating new


image structures. This is crucial
2. Computer Graphics: In computer
graphics, the algorithm is utilized
to rotate objects or scenes in a 3D
environment. By applying the
rotation to the vertices of the
objects, the entire scene can be
transformed accordingly. This is
crucial in applications such as
video games, virtual reality, and
3D modeling software.
3. Mobile Applications: Many mobile
applications involve image capture
and display. The rotate image
algorithm enables users to view
images in different orientations by
rotating them based on user
preferences. This enhances the user
experience and provides flexibility
in image manipulation.
4. Data Visualization: In data
visualization applications, the
algorithm is used to rotate
graphical elements such as charts,
graphs, and maps. This allows users
to view data from different angles

248
Beginner's level

• and perspectives, aiding in data


analysis and decision-making
processes.
5. Document Processing: The algorithm
can be applied to rotate scanned
documents or images of documents.
This is beneficial in document
management systems, OCR (Optical
Character Recognition) software,
and digital archiving, where proper
orientation is crucial for
readability and document
processing.

Overall, the rotate image algorithm


plays a vital role in various real-
world applications that involve image
processing, computer graphics, mobile
applications, data visualization, and
document processing. Its ability to
efficiently and in-place rotate images
provides flexibility, enhances user
experiences, and facilitates effective
data analysis and information
management.

249
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 46

Group Anagrams:
Given an array of strings strs, group
the anagrams together. You can return
the answer in any order.

An Anagram is a word or phrase formed


by rearranging the letters of a
different word or phrase, typically
using all the original letters
exactly once.

Input: strs =
["eat”,"tea","tan”,"ate”,"nat","bat"]
Output: [[ "bat"],["nat","tan”],
["ate "eat","tea"]]

Our logic:
to group anagrams together, we can
use a hashmap to store the sorted
version of each word as the key and
the corresponding anagrams as the
value. Here's a JavaScript
implementation of the algorithm:

251
Beginner's level

function groupAnagrams(strs) {
const anagramMap = new Map();

for (const word of strs) {


// Sort the word to get its
canonical form
const sortedWord =
word.split(11).sort().join(’1) ;
// Check if the sorted word is
already in the map
if (anagramMap.has(sortedWord)) {
// If it is, add the word to the
existing anagram group
anagramMap.get(sortedWord).push(word);
} else {

// If it's not, create a new anagram


group with the sorted word as the key
anagramMap.set(sortedWord,

252
Beginner's level

[word]);

}
// Convert the map values to an
array of anagram groups
return
Array.from(anagramMap.values()) ;

The algorithm iterates over each word


in the input array. For each word, it
sorts the characters to obtain the
canonical form of the word. If the
sorted form is already present in the
hashmap, the word is added to the
corresponding anagram group.
Otherwise, a new anagram group is
created.

Finally, the algorithm returns an


array containing all the anagram
groups.

253
Beginner's level

This algorithm has a time complexity


of 0(n * m * log m), where n is the
number of words in the input array and
m is the maximum length of a word. It
efficiently groups anagrams together
without using excessive memory,
allowing for efficient analysis and
organization of anagram data.

Importance of Group Anagrams Algorithm


in Real-world Applications:
The algorithm to group anagrams
together has significant importance in
various real-world applications that
involve analyzing and organizing
textual data. Here are a few examples:

1 . Text Analysis: In natural language


processing and text mining tasks,
grouping anagrams together can help
in tasks like text classification,
information retrieval, and
sentiment analysis. By identifying
and grouping words with the same

254
Beginner's level

• set of characters, the algorithm


enables more accurate analysis of
text data.
2. Word Games and Puzzles: Anagram
grouping is commonly used in word
games and puzzles where players
need to find all possible anagrams
of a given word or phrase. The
algorithm provides an efficient way
to generate anagram groups,
allowing game developers to enhance
the gameplay experience.
3. Data Cleaning and Deduplication:
When dealing with large datasets
containing textual information,
it's common to encounter duplicate
or similar records. By grouping
anagrams together, the algorithm
helps in identifying and
eliminating redundant or similar
data entries, improving data
quality and reducing redundancy.
4. Word Frequency Analysis: Anagram
grouping can be useful in analyzing
the frequency distribution of words
in a text corpus. By grouping

255
Beginner's level

• anagrams together, the algorithm


allows for a more accurate
calculation of word frequencies,
enabling researchers and analysts
to gain insights into word usage
patterns.
5. Information Retrieval and Search
Engines: In search engines or
information retrieval systems,
grouping anagrams can optimize the
search process. By precomputing
anagram groups and indexing them,
the algorithm enables faster
retrieval of relevant information,
enhancing search engine
performance.

Overall, the algorithm to group


anagrams has practical applications in
various domains, including text
analysis, game development, data
cleaning, information retrieval, and
more. Its ability to efficiently
organize anagrams provides valuable
insights and optimizations in tasks
that involve textual data
manipulation.

256
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 47

Spiral Matrix:
Given an m x n matrix, return all
elements of the matrix in spiral
order.

Input: matrix = [[1,2,31,14,5,6],


[7,8,9]]
Output: [1,2,3,6,9,8,7,4,51

Our logic:
7o obtain the elements of the matrix
in spiral order, we can use a
simulation approach. We define four
boundaries that represent the 'top',
'bottom', 'left', and 'right' edges
of the matrix, respectively. We start

258
Beginner's level

by traversing the top row from left


to right, adding each element to the
result array. After that, we
increment the ‘top’ boundary to
exclude the top row from further
traversal.

Next, we traverse the rightmost


column from top to bottom, adding
each element to the result array.
After that, we decrement the 'right*
boundary to exclude the rightmost
column from further traversal.

We then check if the 'top' boundary


is still less than or equal to the
'bottom' boundary. If it is, we
traverse the bottom row from right to
left, adding each element to the
result array. After that, we
decrement the 'bottom' boundary to
exclude the bottom row from further
traversal.

Next, we check if the left boundary


is still less than or equal to the

259
Beginner's level

the ‘right’ boundary. If it is, we


traverse the leftmost column from
bottom to top, adding each element to
the result array. After that, we
increment the ’left’ boundary to
exclude the leftmost column from
further traversal.

We repeat these steps until all


elements have been added to the
result array. Finally, we return the
result array containing the matrix
elements in spiral order.

By using this simulation approach, we


can efficiently traverse the matrix
in a spiral pattern, ensuring that we
collect all the elements in the
desired order.

Here’s the code implementation in


JavaScript:

260
Beginner's level

function spira!Order(matrix) {

if (matrix.length === 0) {

return [];

}
const result = [] ;

let top = 0;

let bottom = matrix.length - 1;

let left = 0;

let right = matrix[0].length - 1;


while (top <= bottom && left <=
right) {
// Traverse from left to right
for (let i = left; i <= right; i+

<•) {
result.push(matrix!top][i]);

}
top++;

261
Beginner's level

// Traverse from top to bottom


for (let i = top; i <= bottom; i+

+) {
result.push(matrix[i][right]);

}
right--;
// Check if top is still less than
or equal to bottom
if (top <= bottom) {
// Traverse from right to left
for (let i = right; i >= left;

i-) {
result.push(matrix[bottom]

[i]) ;

}
bottom--;

262
Beginner's level

// Check if left is still less than or


equal to right
if (left <= right) {
// Traverse from bottom to top
for (let i = bottom; i >= top;
i-) {
result.push(matrix[i][left ]);

}
left++;

return result;

You can call the 'spiralOrder'


function with your matrix as an
argument to obtain the elements in
spiral order.

263
Beginner's level

Importance of Spiral Matrix Algorithm


in Real-world Applications:
The algorithm for obtaining the
elements of a matrix in spiral order
has several important applications in
real-world scenarios. Here are some
examples:

1. Image Processing: In image


processing tasks like edge
detection or image filtering, it is
often necessary to traverse the
pixels of an image in a specific
order. The spiral matrix algorithm
allows for a systematic traversal,
enabling efficient processing and
analysis of image data.
2. Computer Graphics: When rendering
2D or 3D graphics, certain effects
or algorithms require accessing
pixels or vertices in a specific
order. The spiral matrix algorithm
can be used to traverse the
graphics buffer or mesh data,
ensuring the desired order for
rendering or applying

264
Beginner's level

• transformations.
3. Matrix Operations: Various
mathematical operations on
matrices, such as matrix
multiplication or finding
determinants, require accessing
matrix elements in a specific
order. The spiral matrix algorithm
provides a structured way to access
elements, enabling efficient
computation of matrix operations.
4. Spiral-based Data Representation:
In data visualization or charting
applications, representing data in
a spiral pattern can create
visually appealing and informative
displays. The spiral matrix
algorithm can be used to arrange
and access data points in a spiral
order, facilitating the creation of
engaging visualizations.
5. Grid-based Games or Simulations:
Many games or simulations involve
grid-based environments, where
characters or objects move or
interact with the grid. Traversing

265
Beginner's level

• the grid in a spiral pattern can be


useful for generating movement
paths, identifying neighboring
cells, or exploring the grid
systematically.
6. Memory Access Optimization: In
certain scenarios where memory
access patterns are critical,
arranging data in a spiral order
can optimize cache utilization and
improve performance. The spiral
matrix algorithm can be applied to
flatten multi-dimensional data
structures, allowing for efficient
memory access and processing.

Overall, the spiral matrix algorithm


is important in various real-world
applications, including image
processing, computer graphics, matrix
operations, data visualization, games,
simulations, and memory optimization.
Its ability to provide a structured
and systematic approach for accessing
and processing matrix elements makes
it a valuable tool for solving diverse
problems in different domains.

266
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 48

Add Binary:
Given two binary strings a and b,
return their sum as a binary string.

Example 1:

Input: a = "11", b = "1"


Output: "100"

Example 2:

Input: a = "1010", b = "1011"


Output: "10101"

Our logic:
To add two binary strings 'a' and
'b', you can follow these steps:

1. Initialize an empty string 'result'


to store the 'sum'.
2. Start iterating from the rightmost

268
Beginner's level

bit (LSB) of the binary strings 'a'


and b' until you reach the leftmost
bit (MSB). Keep track of the current
bit's position using an index
variable 'i'.
3. Initialize two variables, 'carry'
and 'bitSum', to keep track of the
carry and the sum of the current
bits, respectively. Set 'carry' to
0 initially.
4. At each position 'i', calculate the
sum of the current bits and the
carry:
• Convert the current bits at
position i' in 'a' and 'b' to
integers using 'parselnt(bit, 2)'.
• Add the integers, along with the
carry, 'carry'.
• Calculate the sum of the current
bits and the carry using 'bitSum
= (sum % 2).toString(2)'.
• Update the carry for the next
iteration using 'carry =
Math.floor(sum / 2)'.
5. Append the 'bitSum' to the
beginning of the result string.

269
Beginner's level

6. After the loop ends, if there is


still a carry, append it to the
beginning of the ‘result’ string.
7. Return the 'result1 string.

Here s an example implementation in


JavaScript:

function addBinary(a, b) {
let result = '';
let carry = 0;
let i = a.length - 1 ;
let j = b.length - 1 ;

while (i >= 0 || j >= 0) {


const bitA = i >= 0 ?

parselnt(a[i]) : 0;
const bitB = j >= 0 ?
parselnt(b[j]) : 0;

270
Beginner's level

const sum = bitA + bitB + carry;


const bitSum = (sum %

2).toString(2);
carry = Math.floor(sum / 2);

result = bitSum + result;

i--;
j —;

if (carry > 0) {
result = carry + result;

return result;

271
Beginner's level

You can then call the ‘addBinary'


function with two binary strings, for
example: 'addBinary(' 101 ' , '11')',
and it will return the sum as a
binary string: '110001 ' .

Importance of Add Binary Algorithm in


Real-world Applications:
The algorithm to add two binary
strings has practical applications in
various domains, including but not
limited to:

1. Networking and Data Communication:


Binary addition is fundamental in
network protocols and data
communication systems. For example,
when handling network addresses,
subnet masks, or IP addresses,
binary addition is used to perform
operations such as subnetting or
determining network ranges.

272
Beginner's level

2. Cryptography and Security: Binary


addition is crucial in
cryptographic algorithms and
security protocols. Many encryption
and decryption algorithms operate
on binary data, and performing
binary addition is essential for
cryptographic operations such as
XOR, modular arithmetic, or key
generation.
3. Digital Systems and Computer
Architecture: Binary addition is at
the core of digital systems and
computer architecture. Arithmetic
logic units (ALUs), which are
responsible for performing
mathematical and logical operations
in processors, rely on binary
addition for computations.
4. Data Manipulation and Bitwise
Operations: Binary addition is
commonly used for manipulating and
performing bitwise operations on
binary data. This includes tasks
like setting or clearing specific
bits, extracting or modifying

273
Beginner's level

• specific fields in binary


representations, or bitwise logical
operations.
3. Number Representation and
Conversion: Binary addition is
fundamental in converting between
different number representations.
For example, in binary-coded
decimal (BCD) arithmetic, binary
addition is used to perform
calculations on decimal numbers
represented in binary form.
4. Coding and Error Detection: Binary
addition is used in error detection
and correction codes such as
checksums, cyclic redundancy checks
(CRC), or parity bits. These
techniques rely on binary addition
to compute checksums or detect
errors in transmitted data.

274
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 49

Climbing Stairs:
You are climbing a staircase. It
takes n steps to reach the top.
Each time you can either climb 1 or 2
steps. In how many distinct ways can
you climb to the top?

Example:

Input: n = 2
Output: 2
Explanation: There are two ways to
climb to the top.
1 . 1 step + 1 step
2 . 2 steps

Our logic:
This problem can be solved using
dynamic programming. We can observe
that the number of distinct ways to
reach step 'n' is equal to the sum of
the distinct ways to reach step 'n-V
and step 'n-2'. This is because we

276
Beginner's level

can reach step n cither by taking a


single step from step 'n-1' or by
taking two steps from step n-2'.

Here's a JavaScript implementation of


the algorithm:

function climbStairs(n) {
if (n < = 2) {
return n;

}
const dp = [0, 1, 2];

for (let i = 3; i <= n; i++) {

dp[i] = dp[i - 1] + dp[i - 2];

return dp[n];

277
Beginner's level

// Example usage
const n = 5;
const distinctWays = climbStairs(n) ;
console.log(distinctWays); // Output:
8

In this implementation, we initialize


an array dp where ‘dp[ij' represents
the number of distinct ways to reach
step i. We start by assigning base
cases for 'dp[0]\ 'dp[1j’t and
’dp[2]'. Then, we iterate from step 3
up to 'n', calculating the number of
distinct ways based on the previous
steps using the recurrence relation
'dp[i] = dp[i - 1] + dp[i - 2]'.
Finally, we return ’dp[n]which
represents the number of distinct
ways to climb to the top of 'n'
steps.

The time complexity of this algorithm


is 0(n), as we only need to iterate

278
Beginner's level

*rT times to calculate the number of


distinct ways.

Importance of this Algorithm in Real-


world Applications:
The algorithm for counting the
distinct ways to climb a staircase is
important in several real-world
applications. It is used to solve
staircase problems, determine paths in
grids and mazes, and analyze
combinatorial scenarios. The algorithm
showcases the concept of dynamic
programming and provides a foundation
for solving more complex optimization
problems. Its efficient time
complexity allows for quick
calculations, making it useful in
performance optimization tasks.
Overall, this algorithm has practical
significance in fields such as
computer science, mathematics,
optimization, and combinatorics.

279
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 50

Same Tree:
Given the roots of two binary trees p
and q, write a function to check if
they are the same or not.
Two binary trees are considered the
same if they are structurally
identical, and the nodes have the
same value.

Example 1:

Input: p = [1,2], q = [1,null,2]


Output: false

281
Beginner's level

Example 2:

Input: p = [1,2,1], q = [1,1,2]


Output: false

Our logic:
To check if two binary trees are the
same, we need to compare their
structure and the values of their
nodes.

Here's a JavaScript implementation of


the algorithm:

282
Beginner's level

function isSameTree((p, q) {
// Base case: if both trees are
empty, they are the same

if (!p && !q) {

return true;

// If one tree is empty and the


other is not, they are not the same

if (!P II !q) {
return false;

// Check if the values of the

current nodes are the same

if (p.val !== q.val) {

return false;

283
Beginner's level

// Recursively check if the left


and right subtrees are the same

const isLeftSame =
isSameTree(p.left, q.left);

const isRightSame =
isSameTree(p.right, q.right);

// The trees are the same if both


subtrees are the same

return isLeftSame && isRightSame;

The algorithm compares the nodes’


values at each level and recursively
checks if the left and right subtrees
are the same. It returns 'true' if
both trees are structurally identical
and have the same node values.
Otherwise, it returns 'false'.

284
Beginner's level

The time complexity of this algorithm


is 0(min(m, n)), where 'm‘ and 'n' are
the numbers of nodes in the two trees,
respectively.

Importance of Same Tree Algorithm in


Real-world Applications:
The algorithm for checking if two
binary trees are the same
(structurally identical with the same
node values) is important in various
real-world applications involving tree
structures. Here are a few examples:

1. Data Analysis: Binary trees are


commonly used to represent
hierarchical data structures, such
as organizational charts, file
systems, or product categories. By
determining if two binary trees are
the same, data analysts can compare
different versions of these
structures, identify changes, and
analyze the impact of
modifications.

285
Beginner's level

2. Decision-Making Algorithms: In
decision-making processes, binary
trees are often used to represent
decision trees or game trees. By
checking if two decision trees are
the same, decision-makers can
determine if they represent the
same set of decisions or
strategies, enabling them to make
informed choices based on existing
knowledge.
3. Tree-Based Data Structures: The
algorithm is crucial in various
tree-based data structures like
binary search trees, AVL trees, or
red-black trees. It allows
developers to compare and identify
if two trees have the same
structure and values, ensuring the
integrity and consistency of these
data structures.
4. Software Development: Binary trees
are utilized in algorithms for
parsing expressions, generating
code, or optimizing computations.
Checking if two trees are the same

286
Beginner's level

• is essential to ensure the


correctness and expected behavior
of these algorithms, leading to
reliable and efficient software
development.

Overall, the importance of the same


tree algorithm extends to several
domains, including data analysis,
decision-making, data structures, and
software development. By accurately
determining if two binary trees are
the same, professionals in these
fields can enhance data analysis
processes, improve decision-making
algorithms, ensure integrity in tree­
based data structures, and develop
robust software applications.

287
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 51

Decode Ways:
A message containing letters from A-Z
can be encoded into numbers using the
following mapping:

’A' -> "I"


'B' -> "2"

'Z' -> "26"

To decode an encoded message, all the


digits must be grouped then mapped
back into letters using the reverse
of the mapping above (there may be
multiple ways). For example, "11106"
can be mapped into:

• "AAJF" with the grouping (1 1 10 6)


• "KJF" with the grouping (11 10 6)
Note that the grouping (1 11 06) is
invalid because "06" cannot be mapped
into 1F' since "6" is different from
"06".

289
Beginner's level

Given a string s' containing only


digits, return the number of ways to
decode it.

Example 1:

Input: s = "12"
Output: 2
Explanation: "12 could be decoded as
"AB‘ (1 2) or "L" (12).

Example 2:

Input: s = "06"
Output: 0
Explanation: "06" cannot be mapped to
"F" because of the leading zero ("6"
is different from "06").

Our logic:
The task is to determine the number
of ways the encoded message can be
decoded.

290
Beginner's level

Here’s an algorithm to solve this


problem in JavaScript:

1 . Initialize a dynamic programming


array "dp‘ of length 's.length + 1'
and set dp[0] = 1' to represent
the base case.
2. Iterate through the string 's' from
index 1 to s.length':
• Set 'currentDigit' to the current
digit at index 'i' .
• Set 'prevDigit' to the previous
digit at index 'i - 1'.
• If 'currentDigit' is not 10’ ,
update dp[i]' to 'dp[i - 1]'
since 'currentDigit' can be
decoded individually.
• If 'prevDigit' is ’I’ or
('prevDigit' is ’2’ and
'currentDigit' is between ’01 and
’6), update 'dp[i]' to dp[i] +
dp[i - 2]'. This means we can
combine prevDigit' and
currentDigit' as a two-digit
code.
3. Return 'dp[s.length]' as the
number of ways to decode the

291
Beginner's level

string.

Here's the corrected JavaScript


implementation of the algorithm:

function numDecodings(s) {
if (S[0] === -0-) {

return 0;

const dp = Array(s.length +

1).fill(0);
dp [ 0 ] = 1 ;

dp [ 1 ] = 1;
for (let i = 2; i <= s.length; i++)

{
const currentDigit = s[i - 1 ];
const prevDigit = s[i - 2];

if (currentDigit !== '0') {

292
Beginner's level

dp[i] += dp[i - 1 ];

}
if (prevDigit === ’ 1 ' ||
(prevDigit === 2 && currentDigit >=
'0' && currentDigit <= '6')) {
dp[x] += dp[i - 2];

}
}
return dp[s.length];

The algorithm follows a bottom-up


dynamic programming approach to
calculate the number of ways to decode
the string. By considering different
combinations of digits, it determines
the valid ways to decode the encoded
message. Additionally, the algorithm
handles the case where the string

293
Beginner's level

starts with '01 appropriately to avoid


invalid decodings.

The time complexity of the algorithm


is 0(n), where n is the length of the
input string. This is because we
iterate through the input string once
and perform constant time operations
at each iteration. Therefore, the time
complexity is linear with respect to
the size of the input.

Importance of Decode Ways Algorithm in


Real-world Applications:
The algorithm for decoding an encoded
message has several real-world
applications, especially in scenarios
where data is represented using a
different encoding scheme and needs to
be transformed back to its original
form. Here are a few examples of its
importance:

1 . Communication and messaging


systems: In communication systems

294
Beginner's level

• where messages are transmitted in a


compact format, such as numeric
codes or compressed
representations, the algorithm can
be used to decode the messages and
convert them back into their
original human-readable form. This
is particularly useful in
telecommunication systems, where
encoded messages need to be decoded
for proper interpretation.
2. Data compression and storage: The
algorithm is crucial in data
compression techniques, where data
is encoded in a more efficient
format to reduce storage space or
transmission bandwidth. When the
compressed data needs to be
decompressed, the algorithm can be
used to decode the encoded data and
restore it to its original form.
3. Cryptography: In certain encryption
schemes, numerical representations
are used to encode sensitive
information. The algorithm can be
utilized to decode encrypted
messages and retrieve the original

295
Beginner's level

• plaintext, allowing authorized


individuals to access the
information.
4. Natural language processing: In
language processing applications,
the algorithm can be employed to
convert numerical representations
of text, such as character indices
or tokenized sequences, back into
the original textual form. This is
particularly useful in tasks like
text generation, machine
translation, and sentiment
analysis.

Overall, the algorithm for decoding


encoded messages plays a vital role in
various domains, enabling the
transformation of encoded data into
its original form for analysis,
interpretation, and further
processing.

296
Fi Edi Select Vie

i-n — n dsa.js x
Beginner's level

DSA Challenge 52

Word Search:
Given an 'm x n' grid of characters
board and a string 'word', return
'true' if 'word' exists in the grid.
The word can be constructed from
letters of sequentially adjacent
cells, where adjacent cells are
horizontally or vertically
neighboring. The same letter cell may
not be used more than once.

Input: board = [["A","BN,"C","E"],


["S"," F","C","S"],["A "D","E","Ei]],
word = "ABCCED"
Output: true

298
Beginner's level

Input: board = [["A","B",”C”, "E"],


["S","F","C","S"], ["A","D","E","E" ]],
word = “SEE"
Output: true

Our logic:
Here's an algorithm in JavaScript to
solve the problem:

function exist(board, word) {


const rows = board.length;
const cols = board[0].length;
const wordLength = word.length;

299
Beginner's level

function dfs(row, col, index) {


if (index === wordLength) {
return true; // Found the entire

word

if (
row < 0 | |
row >= rows ||
col < 0 ||

col >= cols ||


board[row][col] !== word[index]

) {
return false; // Out of bounds
or mismatching character

380
Beginner's level

// Mark the current cell as visited


by modifying its value
const originalChar = board[row]
[col] ;
board[row][col] = ’#' ;
// Check the neighboring cells
const found =
dfs(row + 1, col, index +1) ||
dfs(row - 1, col, index +1) ||
dfs(row, col + 1, index +1) ||
dfs(row, col - 1, index +1);
// Restore the original value of
the cell
board[row][col] = originalChar;
return found;

301
Beginner's level

// Iterate through each cell in the


board
for (let row = 0; row < rows; row+

+) {
for (let col = 0; col < cols;
CO1++) {

if (dfs(row, col, 0)) {


return true; // Word found

return false; // Word not found

302
Beginner's level

This algorithm uses a depth-first


search (DFS) approach to traverse the
grid and check if the word can be
constructed from adjacent cells. It
starts at each cell in the grid and
recursively explores the neighboring
cells, marking the visited cells to
avoid reusing them. If the entire word
is found, it returns true, indicating
that the word exists in the grid. If
the search exhausts all possibilities
without finding the word, it returns
false.

Importance of Word Search Algorithm in


Real-world Applications:
The algorithm for finding a word in a
grid has several real-world
applications, particularly in word
games and puzzles. Here are a few
examples:

303
Beginner's level

1 . Word Search Games: The algorithm


can be used to build interactive
word search games where players
need to find words hidden in a grid
of letters. The algorithm
efficiently determines if a word
exists in the grid, allowing game
developers to validate players'
answers.
2. Crossword Puzzles: In crossword
puzzles, the algorithm can be used
to verify if a word entered by the
user matches the given grid. It
helps ensure that the entered word
follows the rules of the puzzle,
such as forming valid intersections
with other words.
3. Spell Checkers: Spell-checking
algorithms can utilize this
approach to verify if a given word
is present in a dictionary or a
corpus of valid words. By treating
the grid as a representation of
available letters, the algorithm
can quickly determine if a word is
valid or misspelled.

304
Beginner's level

• individuals to access the


information.
4. Natural language processing: In
language processing applications,
the algorithm can be employed to
convert numerical representations
of text, such as character indices
or tokenized sequences, back into
the original textual form. This is
particularly useful in tasks like
text generation, machine
translation, and sentiment
analysis.

Overall, the algorithm for decoding


encoded messages plays a vital role in
various domains, enabling the
transformation of encoded data into
its original form for analysis,
interpretation, and further
processing.

305
Fi Edi Select Vie

i-n — n dsa.js x
Get ready to embark on an
exciting journey into the
depths of Data Structures and
Algorithms!4?
Data Structures &
Algorithms
• for all programmers •

ADVANCED LEVEL
Advanced level

DSA Challenge 1

Recursive Backtracking Algorithm:


Given non-negative integers m and n,
write a program to generate all size
m combinations of the integers from 0
(zero) to n-1 in sorted order (each
combination is sorted and the entire
table is sorted).

Example:

3 comb 5 is:
0 1 2, 0 1 3, 0 1 4, 0 2 3, 0 2 4,
0 3 4, 1 2 3, 1 2 4, 1 3 4, 2 3 4

Our logic:
Here's a JavaScript function that
implements this algorithm:

function generateCombinations(m, n) {
const combinations = [];
function backtrack(start, current)
{
if (current.length = = = m) {
combinations.push(current.slice());

308
Advanced level

// add a copy of the current


combination to the list
return;
}
for (let i = start; i < n; i++) {
current.push(i) ;
backtrack(i + 1, current);
current.pop() ;
}
}

backtrack(0, [ ]) ;

// sort the combinations and each


combination
combinations.sort((a, b) => {
for (let i = 0; i < m; i++) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return 1;
}
return 0;
});

return combinations;
}

generateCombinations(3, 5);

309
Advanced level

The function uses a recursive


backtracking algorithm to generate
all possible combinations of size 'm'
from the integers between 0 and n-1 .
The backtrack function takes two
parameters: the start index to begin
the search (initialized to 0), and
the current array representing the
current combination being built. The
function checks if the current
combination has the desired size, and
if so, adds a copy of it to the
combinations list. Otherwise, it
loops through the remaining integers
from start to n-1, adds each one to
the current combination, and
recursively calls itself with an
updated start index and the new
current combination.

Finally, the function sorts the list


of combinations using a custom
comparator function that compares
each combination element-wise and
returns the appropriate comparison
result.

310
Advanced level

Importance of Recursive Backtracking


Algorithm in Real-world Applications:
The algorithm for generating all size
m combinations of the integers from 0
to n-1 in sorted order can be useful
in various real-world scenarios and
industries that involve combinatorial
optimization or search problems. Here
are a few examples:

1. Data Analysis: In data analysis, it


is often necessary to find all
possible combinations of a given
set of elements, such as features
or variables. This algorithm can be
used to generate all possible
combinations of a fixed size, which
can then be analyzed and compared
to identify important patterns or
relationships.
2. Manufacturing: In manufacturing, it
is often necessary to optimize
production processes by finding the
best combination of input
parameters or resources. This
algorithm can be used to generate

311
Advanced level

• all possible combinations of a


fixed size, which can then be
tested and evaluated to find the
most efficient and cost-effective
solution.
3 . Cryptography: In cryptography,
it is often necessary to generate
all possible combinations of a
given set of characters or keys to
crack a code or password. This
algorithm can be used to generate
all possible combinations of a
fixed size, which can then be
tested to find the correct
solution.
4. Gaming: In game development, it is
often necessary to generate all
possible combinations of game
elements, such as weapons or power-
ups, to create balanced and
challenging gameplay. This
algorithm can be used to generate
all possible combinations of a
fixed size, which can then be
tested and evaluated to find the

312
Advanced level

• most engaging and enjoyable


gameplay.

Overall, the algorithm for generating


all size m combinations of the
integers from 0 to n-1 in sorted
order can be useful in various
scenarios that involve combinatorial
optimization or search problems. Its
applications are not limited to any
specific industry or domain, but
rather can be applied wherever such
problems arise.

313
Advanced level

DSA Challenge 2

Amicable Pairs:
FreeCell, a solitaire card game, was
introduced by Paul Alfille in 1978 on
the PLATO system. Jim Horne, from
Microsoft, renamed it FreeCell and
developed versions for DOS and
Windows. This release included 32,000
numbered deals. Jim Horne shared the
algorithm as FreeCell gained
popularity, leading to its
replication in other implementations.
The deals range from 1 to 32,000,
while newer Microsoft versions
expanded to 1 million (1 to
1,000,000). Some implementations even
support deal numbers beyond this
range.

The algorithm uses this linear


congruential generator from Microsoft
C:
• state 214013xstate+2531011(mod23
nt-1
1) + 1
• randn = staten -r 2A16
• randn is in range 0 to 32767.

315
Advanced level

iterating from 2 to the square root


of the number, adding up any divisors
that evenly divide the number.

// Define a function to find amicable


pairs below a given limit
function findAmicablePairs(limit) {
const pairs = [];
for (let i - 1; i <= limit; i++) {
const a = sumProperDivisors(i);
// Only check for amicable pairs
with i < a to avoid duplicates
if (i < a && sumProperDivnsors(a)
=== i) {
pairs.push([i, a]);
}
}
return pairs;
}

// Find amicable pairs below 20,000


and log them to the console
const amicablePairs =
findAmicablePairs(20000);
console.log(amicablePairs);

316
Advanced level

This function uses the


'sumProperDivisors' function to
calculate the sum of proper divisors
for each number from 1 up to the
given limit. It then checks if the
sum of proper divisors for the first
number is equal to the second number,
and vice versa. If so, it adds the
pair to an array. It also skips any
pairs that have already been checked
to avoid duplicates.
The functicn returns an array of all
amicable pairs below the given limit.
Finally, the code calls this function
with a limit of 20,000 and logs the
resulting array of amicable pairs to
the console.

The algorithm implemented in the code


is a brute force algorithm. It
iterates through all numbers below
the given limit and checks if each
number is part of an amicable pair by
computing the sum of proper divisors
of each number and checking if it
matches with another number's value.

317
Advanced level

Importance of Amicable Pairs in Real-


world Applications:
1. Number theory research: Amicable
pairs are a topic of interest in
number theory, and the code can be
used to explore patterns and
relationships between amicable
numbers. This knowledge can be used
to gain a better understanding of
the behavior of numbers, which can
have implications in various
fields, such as cryptography,
computer science, and physics.
2. Cryptography: In cryptography,
amicable pairs can be used to
generate keys for encryption and
decryption algorithms such as RSA.
These algorithms rely on the fact
that it is difficult to factorize
large composite numbers into their
prime factors, and amicable pairs
can be used to generate such
numbers.
3. Testing: The code can be used in
testing and quality assurance
processes to verify the correctness

318
Advanced level

• of other programs that deal with


numbers and their properties,
including amicable pairs.
4. Education: The code can be used in
educational settings to teach
students about iteration,
conditional statements, and
functions, which are fundamental
concepts in programming. This code
provides an example of how these
concepts can be used to solve a
real-world problem.
5. Entertainment: The code can also be
used for entertainment purposes,
such as creating games or puzzles
that involve amicable pairs.

319
Advanced level

DSA Challenge 3

Deepcopy:
Write a function that returns a deep
copy of a given object. The copy must
not be the same object that was
given.
This task will not test for:
• Objects with properties that are
functions
• Date objects or object with
properties that are Date objects
• RegEx or object with properties
that are RegEx objects
• Prototype copying

Our logic:

function deepCopy(obj) {
let copy = Ar ray.isArray(obj) ? []
: {};
for (let key in obj) {
if (typeof obj[key] === "object"
&& obj[key] !== null) {
copy[key] =
deepCopy(obj[key]);
} else {

321
Advanced level

copy[key] = obj[key];
}

return copy;
}

The deepCopy function uses a


recursive algorithm to copy the
nested objects of the input object.
The function first checks if the
input object is an array or an object
literal and creates an empty copy of
the same type. It then iterates
through the keys of the original
object and checks if each value is an
object. If the value is an object,
the function calls itself recursively
to copy the nested object. If the
value is not an object, it is simply
copied to the new object.

This process continues until all


nested objects have been copied,
creating a complete deep copy of the
original object.

322
Advanced level

Importance of Deepcopy Algorithm in


Real-world Applications:
DeepCopy can be used in web
development to clone objects that
contain data fetched from APIs or
databases. This can help prevent
errors caused by accidentally
modifying the original data.

In the financial services industry,


Deepcopy can be used to create copies
of complex financial data structures.
This can help ensure that data is
accurate and prevent errors when
performing complex calculations.

In the gaming industry, Deepcopy can


be used to create copies of game
objects that have different states.
For example, you might use Deepcopy
to create a copy of a game character
in a different location, or to create
a copy of a game world in a different
state.

323
Advanced level

In the healthcare industry, Deepcopy


can be used to create copies of
patient data for research or
analysis. This can help ensure that
the original patient data remains
secure and private, while allowing
researchers to work with a copy of
the data.

In machine learning, Deepcopy can be


used to create copies of data sets
for training and testing models. This
can help prevent overfitting and
ensure that the original data set
remains intact.

Overall, Deepcopy is a useful


algorithm for creating independent
copies of objects in many different
industries and real-world situations.
It can help prevent errors, ensure
data accuracy, and protect sensitive
information, among other benefits.

324
Advanced level

DSA Challenge 4

Bracket Combinations:
Have the function
BracketCombinations(num) read num
which will be an integer greater than
or equal to zero, and return the
number of valid combinations that can
be formed with num pairs of
parentheses. For example, if the
input is 3, then the possible
combinations of 3 pairs of
parenthesis, namely: ()()(), are ()()
(), ()(()). (())(). ((())). and (()
()). There are □ total combinations
when the input is 3, so your program
should return 5.

Our logic:
To solve this problem, we can use a
recursive approach to generate all
possible combinations of parentheses
and count only the valid ones.
Here's the code for the
Bracketcombinations function:

326
Advanced level

function BracketCombinations(num) {
function
generatecombinations(numOpen,
numClose) {
if (numOpen === 0 && numClose ===
0) {
return 1; // base case: valid
combination found
}
let count = 0;

if (numOpen > 0) {
count +=
generateCombinations(numOpen - 1,
numClose + 1);
}
if (numClose > 0) {
count +=
generateCombinations(numOpen,
numClose - 1);
}

return count;
}

327
Advanced level

// call recursive function with


initial values
return generateCombinations(num,
0);
}

The * generateCombinations' function


takes two arguments, 'numOpen' and
'numClose', which represent the
number of open and close parentheses,
respectively. The base case is when
both 'numOpen' and 'numClose' are
zero, indicating a valid combination
has been found.

For each recursive call, we check if


we can add an open parenthesis
(numOpen > 0) or a close parenthesis
(numClose >0). If we can add an open
parenthesis, we decrement 'numOpen'
and increment 'numClose' to maintain
the balance of parentheses. If we can
add a close parenthesis, we decrement
'numClose'.

328
Advanced level

Finally, we return the count of valid


combinations found.

Importance of Bracket Combinations


Algorithm in Real-world Applications:
The 'Bracketcombinations' function is
important in various fields such as
computer science, programming, and
mathematics. In computer science and
programming, it is useful in solving
problems that involve dealing with
matching parentheses, such as parsing
expressions, validating codes, and
checking syntax errors. For example,
compilers and interpreters use this
function to check whether the code
written by the user has the correct
syntax.

In mathematics, the function is


related to the Catalan numbers, which
arise in many areas such as
combinatorics, geometry, and algebra.

329
Advanced level

The Catalan numbers count the number


of possible ways to arrange various
objects in a particular pattern, and
they appear in many problems
involving counting, optimization, and
probability.

Overall, the Bracketcombinations


function is a powerful tool for
solving problems that involve
counting the number of valid
combinations that can be formed with
parentheses. It is a fundamental
concept in many fields, and its
importance lies in its ability to
help solve various problems in a fast
and efficient manner.

330
Advanced level

DSA Challenge 5

Minimum Number of Jumps:


Given an array of N integers arr[]
where each element represents the
maximum length of the jump that can
be made forward from that element.
This means if arr[i] = x, then we can
jump any distance y such that y <
x.Find the minimum number of jumps to
reach the end of the array (starting
from the first element). If an
element is 0, then you cannot move
through that element.Note: Return -1
if you can’t reach the end of the
array.

Our logic:
function minJumps(arr) {
// Get the length of the array
let n = arr.length;

// Set the initial maximum reachable


index to the first element of the
array
let maxReach = arr[0];

332
Advanced level

let step = arr[0];


let j amp = 1 ;

for (let i = 1; i < n; i++) {


if (i == n - 1) {
return jump;
}

maxReach = Math,max(maxReach, i +
arr[i]);

step--;

if (step == 0) {
jump++;

if (i >= maxReach) {
return -1;
}

step = maxReach - i;
}
}

return -1;
}

333
Advanced level

The code implements an algorithm to


find the minimum number of jumps
required to reach the end of an array
where each element represents the
maximum length of the jump that can
be made from that element. If an
element is 0, it means that no jump
can be made from that element.

The function takes two arguments:


'arr', which is the array of integers
representing the maximum length of
the jump that can be made from each
element, and n, which is the size of
the array.

The function first checks if the size


of the array is less than or equal to
1. If it is, it means that the end is
already reached, so it returns 0.

Next, the function checks if the


first element of the array is 0. If
it is, it means that no jump can be
made from the first element, so it
returns -1.

334
Advanced level

The function then initializes three


variables: 'maxReach', which
represents the farthest index that
can be reached from the current
index, 'step', which represents the
number of steps that can still be
taken from the current index, and
'jump', which represents the number
of jumps taken so far.

The function then loops through the


array from the second element to the
last element. For each element, it
updates the 'maxReach' variable to
the maximum value between its current
value and the sum of the current
index and the value of the element.

It then decrements the step variable,


which represents the number of steps
that can still be taken from the
current index. If step becomes 0, it
means that a jump is needed, so the
function increments the jump variable
and checks if the current index is
greater than or equal to maxReach.

335
Advanced level

If it is, it means that the end


cannot be reached, so the function
returns -1. Otherwise, it updates the
step variable to the difference
between the 'maxReach' variable and
the current index.

Finally, if the loop completes


without reaching the end of the
array, it means that the end cannot
be reached, so the function returns
-1 .

Overall, the algorithm has a time


complexity of 0(n), where n is the
size of the array, as it loops
through the array only once.

336
Advanced level

Importance of Minimum Number of Jumps


Algorithm in Real-world Applications:
The Minimum number of jumps algorithm
has several real-world applications,
such as in gaming, robotics, and
navigation systems. For example, in a
game, the algorithm can be used to
determine the minimum number of moves
required for a player to reach a
goal. In robotics, the algorithm can
be used to determine the minimum
number of steps a robot needs to take
to reach a specific location. In
navigation systems, the algorithm can
be used to determine the minimum
distance or time required to reach a
destination, taking into account any
obstacles or barriers that may be in
the way.

Overall, the Minimum number of jumps


algorithm is a useful tool for any
application that involves finding the
most efficient path or route to a
particular goal.

337
Advanced level

DSA Challenge 6

Check for BST:


Given the root of a binary tree.
Check whether it is a BST or
not.Note: We are considering that
BSTs can not contain duplicate
Nodes.A sST is defined as follows:
• The left subtree of a node contains
only nodes with keys less than the
node's key.
• The right subtree of a node
contains only nodes with keys
greater than the node's key.
• Both the left and right subtrees
must also be binary search trees.

Your Task:
You don't need to read input or print
anything. Your task is to complete
the function isBST() which takes the
root of the tree as a parameter and
returns true if the given binary tree
is BST, else returns false.

339
Advanced level

Out logic:

class Solution {
//Function to check whether a Binary
Tree is BST or not.
isBST(root) {
//helper function to check if a
subtree is a valid BST
const isValidBST = (node, minVal,
maxVal) => {
//base case: empty tree is a
valid BST
if (!node) {
return true;

}
if (node.data <= minVal ||
node.data >= maxVal) {
return false;

340
Advanced level

//recurse on left and right subtrees


return isValidBST(node.left,
minVal, node.data) &&
isValidBST(node.right, node.data,

maxVal);

};
//call helper function on root
node

return isValidBST(root, -Infinity,


Infinity);

}
}

The code above defines a class named


'Solution' with a single method named
'isBST' that takes the root of a
binary tree as input parameter. Inside
the method,

341
Advanced level

it checks if the root is null or not,


and if it's null, then it returns true
since an empty tree is considered a
BST.

If the root is not null, it calls a


recursive helper function ‘isBSTUtil’
passing the root, a minimum and a
maximum value as input parameters.
This function is responsible for
traversing the binary tree and
checking if each node satisfies the
BST property.

The 'isBSTUtil' function checks if the


current node is null or not, and if
it's null, then it returns true since
a null node is considered a BST. If
the current node's value is less than
the minimum value or greater than the
maximum value, then it returns false
since it violates the BST property.

If the current node's value is within


the range of the minimum and maximum

342
Advanced level

values, then it recursively calls the


'isBSTUtil' function for the left and
right child of the current node,
passing the minimum and maximum values
accordingly. If both calls return
true, then it returns true indicating
that the subtree rooted at the current
node is a BST. Otherwise, it returns
false.

Importance of Check for BST Algorithm


in Real-world Applications:
The check for BST (Binary Search Tree)
algorithm is important in various
real-world applications that involve
binary trees. Some examples are:

1 . Database indexing: In database


systems, BST is often used for
indexing data. The check for BST
algorithm is used to ensure that
the indexing process is efficient
and accurate.
2. Sorting: BST can be used for
sorting data, and the check for BST

343
Advanced level

• algorithm is used to ensure that


the data is sorted correctly.
3. Compiler Design: In compiler
design, the check for BST algorithm
is used to create syntax trees for
programming languages.
4. Network routing: The check for BST
algorithm can be used in network
routing to efficiently route data
packets through a network.
5. Data compression: The check for BST
algorithm can also be used in data
compression algorithms to reduce
the size of data sets.

Overall, the check for BST algorithm


is an important tool in computer
science and has numerous practical
applications.

344
Advanced level

DSA Challenge 7

Left View of Binary Tree:


Given a Binary Tree, return Left view
of it. Left view of a Binary Tree is
set of nodes visible when tree is
visited from Left side. The task is
to complete the function
'leftView()', which accepts root of
the tree as argument.

Left view of following tree is:


1 2 4 8.

\
3
/ \ / \
4 5 6 7
\
8

346
Advanced level

Our logic:

class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;

}
}

class Solution {
leftView(root) {
const result = [ ] ;
if (!root) {

return result;

}
const queue = [root];
while (queue.length > 0) {

347
Advanced level

for (let i = 0; i < size; i++) {


const node = queue.shift();
if (i === 0) {
result.push(node.data);

if (node.left) {
queue.push(node.left);

if (node.right) {
queue.push(node.right) ;

}
}
}

348
Advanced level

return result;

The above code defines a 'Node' class


that is used to create nodes of a
binary tree. Each node has a data
property that stores the value of the
node and left and right properties
that point to the left and right child
nodes respectively.

The code also defines a 'Solution'


class that has a 'leftview' method.
This method takes the root of a binary
tree as an argument and returns an
array of nodes that are visible when
the tree is visited from the left
side.

To achieve this, the 'leftview' method


initializes an empty array called
'result' and a queue that holds the

349
Advanced level

root node. It then iterates over the


iterates over the queue and at each
iteration, it removes the first node
in the queue, adds its value to the
result array if it is the first node
in the current level, and then adds
its left and right child nodes to the
queue.

By the time the queue is empty, the


result array contains the values of
the left view of the binary tree,
which is then returned.

Importance of Left View of Binary Tree


Algorithm in Real-world Applications:
The Left View of Binary Tree algorithm
can be useful in various real-world
applications. One such application is
in the field of network topology. In a
computer network, devices such as
routers and switches are connected to
each other in a hierarchical manner,
forming a tree-like structure. The

350
Advanced level

Left View of Binary Tree algorithm can


be used to traverse this tree-like
structure and extract information
about the devices connected to the
network.

Another application of this algorithm


is in the visualization of
hierarchical data structures such as
file systems or organizational charts.
In these applications, the Left View
of Binary Tree can be used to display
the top-level nodes of the tree,
providing users with an overview of
the entire structure.

Overall, the Left View of Binary Tree


algorithm can be a useful tool in any
situation where it is necessary to
extract or display the top-level nodes
of a tree-like structure.

351
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 8

Remove Loop in Linked List:


Given □ linked list of N nodes such
that it may contain a loop.
A loop here means that the last node
of the link list is connected to the
node at position X(1-based index). If
the link list does not have any loop,
X=0.

Remove the loop from the linked list,


if it is present, i.e. unlink the
last node which is forming the loop.

Our logic:

class Node {
constructor(data) {
this.data = data;

this.next = null;

353
Advanced level

class Solution {
// Function to remove a loop in the
linked list.
removeLoop(head) {

if (’head || Shead.next) {
// No loop present in the linked
list.
return;

}
let slow = head;
let fast = head;

// Detect the loop.


while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;

354
Advanced level

if (slow === fast) {


slow = head;
while (slow !== fast) {
slow = slow.next;
fast = fast.next;

}
let temp = fast;
while (temp.next !== fast) {
temp = temp.next;

}
temp.next = null;
return;

}
}
}

355
Advanced level

This code implements a solution to


remove a loop in a linked list. The
linked list is represented using a
'Node' class that has a constructor to
initialize the 'data' and the 'next'
pointer. The Solution class has a
'removeLoop' function that takes the
'head' of the linked list as input.

The function first checks if the


linked list has one or zero nodes. If
so, it returns as there is no loop in
the list. If there are two or more
nodes, it uses the two-pointer
technique with a slow and fast pointer
to detect if there is a loop in the
linked list. If the two pointers meet
at some point, there is a loop in the
linked list.

The function then finds the node where


the loop starts by resetting the slow
pointer to the head of the linked list
and moving both pointers one step at a
time until they meet. Once it finds
the node where the loop starts, it

356
Advanced level

unlinks the last node that is forming


the loop by setting its next pointer
to null.

Importance of this Algorithm in Real-


world Applications:
The Remove Loop in Linked List
algorithm is an important algorithm in
computer scLence, specifically in the
field of data structures and
algorithms. It is used to remove loops
that might be present in linked lists,
which is a common data structure used
in many real-world applications.

Linked lists are used to implement


many data structures such as stacks,
queues, and graphs. They are also used
in many applications such as music
players, image viewers, and web
browsers. In these applications,
linked lists are used to store data
and perform various operations such as
inserting, deleting, and searching for
data.

357
Advanced level

The Remove Loop in Linked List


algorithm is used to ensure that
linked lists are free from loops.
Loops in linked lists can cause
infinite loops or incorrect behavior
in algorithms that operate on the
linked list. The algorithm ensures
that linked lists are free from loops
and prevent these issues from
occurring.

In summary, the Remove Loop in Linked


List algorithm plays a critical role
in real-world applications that
leverage linked lists. By eliminating
loops in the linked lists, it
safeguards against infinite loops and
incorrect behavior in algorithms that
manipulate the linked list. Therefore,
this algorithm is indispensable in
ensuring the reliability and
efficiency of software systems that
use linked lists.

358
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 9

Detect Loop in Linked List:


Given a linked list of N nodes. The
task is to check if the linked list
has a loop. Linked list can contain
self loop.

Your Task:
The task is to write a function
'detectloop()' which contains
reference to the 'head' as only
argument. This function should return
true if linked list contains loop,
else return false.

Our logic:
class Node {

constructor(data) {
this.data = data;
this.next = null;

}
}

360
Advanced level

class Solution {

// Function to check if the linked


list has a loop.
detectLoop(head) {
if (!head) {
return false;

}
let slow = head;
let fast = head.next;
while (fast && fast.next) {
if (slow === fast) {
return true;

slow = slow.next;
fast = fast.next.next;

361
Advanced level

return false;

}
}

The detectLoop' function uses the


Floyd's cycle-finding algorithm to
detect if a linked list has a loop. It
starts with two pointers, 'slow' and
'fast', both initially pointing to the
'head node. Then, 'slow' moves one
step at a time while 'fast' moves two
steps at a time. If there is a loop in
the linked list, 'fast' will
eventually catch up to 'slow', and the
function will return true. If there is
no loop, 'fast' will reach the end of
the linked list and the function will
return false.

362
Advanced level

Importance of this Algorithm in Real-


world Applications:
The Detect Loop in Linked List
algorithm is an important tool in
real-world applications where linked
lists are used. It helps to ensure
that linked lists are free from loops,
which can cause infinite loops or
incorrect behavior in algorithms that
operate on the linked list.

For example, in computer networking,


the Detect Loop algorithm can be used
to detect and prevent loops in network
topologies. In operating systems, the
algorithm can be used to detect and
resolve deadlocks caused by circular
dependencies. In web development, it
can be used to prevent infinite loops
in recursive functions that manipulate
linked data structures.

363
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 10

0-1 Knapsack Problem:


You are given weights and values of N
items, put these items in a knapsack
of capacity W to get the maximum
total value in the knapsack. Note
that we have only one quantity of
each item.In other words, given two
integer arrays val[0..N-1] and
wt[0..N~1] which represent values and
weights associated with N items
respectively. Also given an integer W
which represents knapsack capacity,
find out the maximum value subset of
val[] such that sum of the weights of
this subset is smaller than or equal
to W. You cannot break an item,
either pick the complete item or dont
pick it (0-1 property).

Your Task: Complete the function


'knapSack()' which takes maximum
capacity W, weight array wt[], value
array val[], and the number of items
n as a parameter and returns the
maximum possible value you can get.

365
Advanced level

Our logic:

function knapSack(W, wt, val, n) {

let dp = Array(n + 1)

.map(() => Array(W + 1).fill(0));

for (let i = 1; i <= n; i++) {

for (let w = 1; w <= W; w++) {

if (wt[i - 1] <= w) {

dp[i][w] = Math.max(val[i - 1]
+ dp[i - 1][w - wt[i - 1]], dp[i - 1]

[w]);
} else {

dp[i][w] = dp[i - 1][w];

366
Advanced level

}
return dp[n][W];

The function takes in four parameters:


• 'W' : The maximum capacity of the
knapsack.
• 'wt': An array of weights of n'
items.
• 'val : An array of values of n'
items.
• 'n': The number of items.
The function returns the maximum
possible value that can be obtained by
picking items with total weight not
exceeding 'W'.

The algorithm works by building a


dynamic programming table 'dp', where
'dp[i][w]' represents the maximum
possible value that can be obtained
using the first i' items and a
knapsack of capacity 'w'.

367
Advanced level

The base case is when there are no


items or the knapsack capacity is
zero, in which case the maximum
possible value is zero.

For each item, we consider two


options: either we pick the item or we
don't. If we pick the item, the
maximum possible value is the value of
the item plus the maximum possible
value using the remaining items and
the remaining capacity of the
knapsack. If we don't pick the item,
the maximum possible value is the
maximum possible value using the
remaining items and the same capacity
of the knapsack.

We take the maximum of these two


options and store it in ’dp[i][w]'.
Finally, the maximum possible value
using all the items and the maximum
capacity of the knapsack is stored in
'dp[n][W]' and returned.
Note that this is a brute force

368
Advanced level

approach to the problem and can be


optimized further using techniques
like memoization or space
optimization.

Importance of 0 - 1 Knapsack Problem


Algorithm in Real-world Applications:
The 0-1 Knapsack Problem algorithm is
widely used in real-world applications
because it provides a way to optimize
limited resources. Here are some
examples of its importance:

1. Resource allocation: The algorithm


can be used to allocate limited
resources such as budget, manpower,
and equipment to maximize profits,
productivity, or efficiency.
2. Production planning: In
manufacturing, the algorithm can be
used to optimize production
planning by selecting the most
profitable products to manufacture
and the optimal quantity to

369
Advanced level

• produce.
3. Portfolio optimization: In finance,
the algorithm can be used to
optimize investment portfolios by
selecting the most profitable
assets to invest in given limited
funds.
4. Supply chain management: The
algorithm can be used to optimize
the distribution of goods in supply
chains by selecting the optimal
products to transport given limited
capacity and demand.

Overall, the 0-1 Knapsack Problem


algorithm is a valuable tool for
decision-making in various fields
where resource optimization is
essential.

370
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 11

Regular Expression Matching:


Given an input string 's' and a
pattern 'p', implement regular
expression matching with support for
1 . 1 and 1*’ where:

• Matches any single character.


• Matches zero or more of the
preceding element.

The matching should cover the entire


input string (not partial).

Our logic!

function isMatch(s, p) {
// Create a 2D array and initialize
it with false
let dp = Array.from({ length:
s.length + 1 }, () => Array(p.length +
1).fill(false));

372
Advanced level

// The empty string matches the empty


pattern

dp[0][0] = true;
// Handling patterns with at the
start
for (let i - 1; i <= p.length; i++)

{
if (p.charAt(i - 1) === '*') {
dp[0][i] = dp[0][i - 2];

}
}
// Iterate over the entire string and
pattern
for (let i = 1; i <= s.length; i++)

373
Advanced level

for (let j = 1; j <= p.length; j++) {


// If the current characters
match or the pattern has a ' . '
if (s.charAt(i - 1) ===
p.charAt(j - 1) || p.charAt(j - 1) ===

{
dp[i][j] = dp[i - 1 ][j - 1 ] ;

}
// If the pattern has a 1 *'
else if (p.charAt(j - 1) ===

’*') {
// Check if the pattern before
the 1 *' matches the current character

if (s.charAt(i - 1) === p.charAt(j -


2) || p.charAt(j - 2) === ' . ' ) {

374
Advanced level

dp[i][j] = dp[i - 1 ] [ j ] || dp[i][j

- 2];

}
else {
dp[i][j] = dp[i][j - 2];

}
}
else {
dp[i][j] = false;

}
// Return the final result
return dp[s.length][p.length];

375
Advanced level

This function takes two arguments 's'


and 'p' which represent the input
string and the pattern, respectively.
The function returns true if the input
string matches the pattern, and false
otherwise.

The function uses dynamic programming


to solve the problem. It creates a 2D
array 'dp' of size '(s.length + 1) x
(p.length + 1)' to store the
intermediate results. The function
initializes the array with false,
except for 'dp[0][0]' which is set to
true since the empty string matches
the empty pattern.

The function then iterates over the


pattern and sets dp[0][i]' to true
for patterns with at the start. It
then iterates over the entire string
and pattern and checks if the current
characters match or the pattern has a

376
Advanced level

If they match, it sets 'dp[i][j]‘ to


'dp[i-1][j-1]'. If the pattern has a
it checks if the pattern before
the matches the current character,
and sets 'dp[i][j]' accordingly. If
the characters do not match and there
is no or it sets ‘dp[i][j]'
to false.

Finally, the function returns


'dp[s.length][p.length]', which
represents whether the input string
matches the pattern.

Importance of this Algorithm in Real-


world Applications:
The regular expression matching
algorithm has a wide range of
applications in various fields such as
text processing, natural language
processing, search engines, and
database systems. In text processing,
regular expressions are used to match
patterns in text, such as finding all

377
Advanced level

email addresses in a large text file,


or searching for specific keywords in
a web page.

In natural language processing,


regular expressions are used to
extract meaningful information from
text, such as extracting all the names
of people mentioned in a news article
or detecting the sentiment of a
review. Search engines also use
regular expressions to implement
advanced search functionality, such as
searching for a phrase within a
specific range of words.

In database systems, regular


expressions are used for searching and
filtering data based on specific
patterns, such as finding all email
addresses in a database or filtering
phone numbers by area code.

378
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 12

Remove Nth Node from End of List:


Given the head of a linked list,
write a function to remove the nth
node from the end of the list and
return its head.

Our logic:
To solve this problem, we can use a
two-pointer approach. We will
maintain two pointers, 'slow' and
'fast', where 'fast' is 'n' nodes
ahead of 'slow'. Once 'fast' reaches
the end of the list, 'slow' will be
pointing at the node we want to
remove.

We will first move the 'fast' pointer


'n' nodes ahead of the 'slow'
pointer. Then, we will move both
pointers forward until 'fast' reaches
the end of the list. At this point,
'slow' will be pointing at the node
we want to remove. We can simply

380
Advanced level

update the 'next' pointer of slow'


to skip over the node to remove.

If the node to remove is the head of


the list, we need to update the head
of the list to be the next node. We
can use a dummy node as the head of
the list to simplify this step.

Here's the JavaScript function that


implements this approach:

function removeNthFromEnd(head, n) {
const dummy = new ListNode(0);
dummy.next = head;
let slow = dummy;
let fast = dummy;

for (let i = 0; i < n; i++) {


fast = fast.next;

}
while (fast.next) {

381
Advanced level

slow = slow.next;
fast = fast.next;

slow.next = slow.next.next;

return dummy.next;

Note that we are assuming that 'n' is


valid, i.e., it is not greater than
the length of the list. If n‘ is
greater than or equal to the length
of the list, we can simply return the
head of the list.

382
Advanced level

Importance of this Algorithm in Real-


world Applications:
The algorithm for removing the nth
node from the end of a linked list has
various real-world applications.

It is commonly used in the


implementation of many software tools,
especially those dealing with data
processing, data analysis, and data
mining.

It can be used in the removal of items


from a database or the removal of
invalid data from a dataset.

Additionally, it can be used in the


development of software for managing
medical records, financial
transactions, and other types of data.

The algorithm's ability to efficiently


remove items from a list without
needing to traverse the entire list
can also make it useful in situations
where performance is critical.

383
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 13

Merge K Sorted Lists:


You are given an array of k linked-
lists lists, each linked-list is
sorted in ascending order.
Your Task: Merge all the linked-lists
into one sorted linked-list and
return it.

Our logic:
The function 'mergeKLists(lists)'
below uses a priority queue (min-
heap) to merge the lists efficiently.
It takes an array of k linked lists
'lists', each of which is sorted in
ascending order. It then merges all
the linked lists into one sorted
linked list and returns it.

To do this, the function first


creates an empty linked list
'mergedList'. It then iterates over
each linked list in the input 'lists'
array and appends each node to the
'mergedList*. As it does this, it

385
Advanced level

maintains the order of the nodes by


comparing their values.

Finally, the function returns the


head of the merged linked list.

The time complexity of the algorithm


is 0(N log k), where N is the total
number of nodes and k is the number
of input lists.

function mergeKLists(lists) {
// Remove empty lists from the input
array
lists = lists.filter(list => list !
== null);
// Create a dummy head node and a
tail node
let dummy = new ListNode(-1) ;
let tail = dummy;

386
Advanced level

// Create a priority queue (min-heap)


using the first node of each list
let heap = new MinHeap();
for (let list of lists) {
heap.push(list);

}
// Merge the lists until the
priority queue is empty
while (!heap.isEmpty()) {
// Remove the minimum element from
the priority queue
let node = heap.pop();

// Append the minimum element to


the tail of the output list
tail.next = node;
tail = tail.next;

387
Advanced level

// Add the next node of the same


list to the priority queue
if (node.next !== null) {
heap.push(node.next);

}
}
// Return the head of the output
list
return dummy.next;

}
// Definition for singly-linked
list.
function ListNode(val, next) {
this.val = (val === undefined ? 0 :
val);

this.next = (next === undefined ?

388
Advanced level

null : next);

}
// MinHeap class for the priority
queue
class MinHeap {
constructor() {
this.heap = [];

}
push(node) {
if (node !== null) {
this.heap.push(node) ;
this . heapifyllp( this . heap. length

- 1):
}
}

389
Advanced level

pop() {
if (this.heap.length === 0) {
return null;

}
let min = this.heap[0] ;
let last = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = last;
this.heapifyDown(0) ;

}
return min;

heapifyllp(i) {
while (i > 0) {
let j = Math.floor((i - 1) / 2);

390
Advanced level

if (this.heapfi].val <
this.heap[j].val) {
[this.heap[i], this.heap[j]] =
[this.heap[j], this.heap[i]];

i = j;
} else {
break;

}
}

}
heapifyDown(i) {
while (true) {
let left = 2 * i + 1 ;

let right = 2 * i + 2;
let smallest = i;

391
Advanced level

if (left < this.heap.length &&


this.heap[left].val <
this.heap[smallest].val) {
smallest = left;

}
if (right < this.heap.length &&
this.heap[right].val <

this.heap[smallest].val) {
smallest = right;

}
if (smallest !== i) {
[this.heap[i],
this.heap[smallest] ] =
[this.heap[smallest], this.heap[i]] ;
i = smallest;

392
Advanced level

else {
break;

}
}
}

IsEmptyO {
return this.heap.length === 0;

}
}

Importance of this Algorithm in Real-


world Applications:
The algorithm for merging k sorted
linked lists is widely used in various
applications such as in databases,
sorting algorithms, and network

393
Advanced level

routing. In databases, this algorithm


is used to merge sorted tables. In
sorting algorithms, this algorithm is
used to merge sorted sub-arrays during
the merge sort algorithm. In network
routing, this algorithm is used to
find the shortest path between two
nodes in a graph.

Overall, this algorithm is essential


in many real-world applications that
involve sorting or merging of data
structures.

394
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 14

N-Queens:
The n-queens puzzle is the problem of
placing 'n' queens on an 'n x n'
chessboard such that no two queens
attack each other.

Given an integer 'n', write a program


that will return all distinct
solutions to the n-queens puzzle. You
may return the answer in any order.

Each solution contains a distinct


board configuration of the n-queens1
placement, where ‘Q1 and both
indicate a queen and an empty space,
respectively.

396
Advanced level

Example:

Input: n = 4
Output: [[",Q.. ..Q","Q....Q."],
[ ' . .Q. *', "Q. -Q", " .Q. ." ] ]
Explanation: There exist two distinct
solutions to the 4-queens.

Our logic:
Here's a JavaScript program that
solves the n-queens puzzle and
returns all distinct solutions:

function solveNQueens(n) {
const solutions = [ ] ;
const board = Array.from({ length: n
}, () => Array(n).fill('.'));
backtrack(0, board);
function backtrack(row, board) {
if (row === n) {

397
Advanced level

solutions.push(board.map(row =>

row.join('')));
return;

}
for (let col = 0; col < n; col++)

{
if (isValidMove(row, col,
board)) {
board[row][col] = ' Q’ ;
backtrack(row + 1, board);
board[row][col] = * . ' ;

}
}
}
function isValidMove(row, col,
board) {

398
Advanced level

for (let i = 0; i < row; i++) {


if (board[i][col] === 'Q1) {
return false;

}
const j = row - i;

if (col - j >= 0 && board[i][col

- j] === 'Q') {
return false;

}
if (col + j < n && board[i][col

+ j] === 'Q') {
return false;

}
}
return true;

399
Advanced level

return solutions;

// Example usage
const n = 4;
const solutions = solveNQueens(n);
console.log(solutions);

This program uses backtracking to


explore all possible configurations of
queens on the chessboard. It starts by
placing queens row by row, checking
the validity of each move. If a valid
solution ±s found (all rows are filled
with queens), it is added to the
solutions array. Finally, the program
returns the array of distinct
' solutions'.
You can adjust the value of n' to
solve the n-queens puzzle for
different board sizes.

400
Advanced level

Importance of N-Queens Algorithm in


Real-world Applications:
The N-Queens algorithm, although
primarily a puzzle or problem-solving
exercise, has several real-world
applications. While the direct
application may not be common, the
algorithm demonstrates important
concepts and techniques that are
valuable in various fields. Here are a
few reasons why the N-Queens algorithm
is significant:

1. Algorithmic Problem Solving: The N-


Queens problem helps develop
problem-solving skills, algorithmic
thinking, and efficient code
implementation. These skills are
essential in computer science and
programming professions.
2. Backtracking and Optimization: The
N-Queens algorithm utilizes
backtracking, which is a powerful
technique for exploring all
possible solutions to a problem and
finding an optimal or satisfactory

401
Advanced level

solution. Backtracking is a
fundamental concept used in various
algorithms and real-world scenarios.
3. Constraint Satisfaction Problems:
The N-Queens problem belongs to a
class of problems known as
constraint satisfaction problems
(CSPs). CSPs are prevalent in
fields like artificial
intelligence, operations research,
scheduling, and logistics.
Understanding the N-Queens
algorithm helps in designing and
solving similar CSPs.
4. Parallel Computing: The N-Queens
algorithm can be parallelized to
improve performance by utilizing
multiple processors or cores. This
concept is valuable in high-
performance computing, distributed
systems, and optimizing resource
utilization.
5. Education and Research: The N-
Queens problem serves as an
educational tool to teach and
illustrate various concepts in

402
Advanced level

• computer science, such as


recursion, data structures, search
algorithms, and complexity
analysis. It also serves as a
subject for academic research in
algorithm analysis and
optimization.

While the N-Queens algorithm may not


have direct applications in specific
real-world scenarios, the underlying
principles and techniques it employs
are widely applicable. It helps in
developing problem-solving skills,
understanding algorithmic concepts,
and applying efficient techniques to
solve complex problems in various
domains.

403
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 15

Permutation Sequence:
Given a set of numbers from 1 to n,
where 'n' is a positive integer, the
set contains n! (n factorial) unique
permutations. By listing and labeling
all the permutations in a specific
order, we obtain a sequence of
permutations. For example, when n =
3', the sequence would be: "123"
132", 213", "231", "312", 321

Now, write a program that takes


inputs for 'n' and 'k', and returns
the kth permutation sequence from the
set.

Example 1:

Input: n = 3, k = 3
Output: "213"

405
Advanced level

Example 2:

Input: n = 4, k = 9
Output: "2314"

Example 3:

Input: n = 3, k = 1
Output: "123"

Our Ic- jic:


Here's a JavaScript program that
solves the problem of finding the kth
permutation sequence for a given n:

function getPermutation(n, k) {
const nums = Array.from({ length: n

}, (_, index) => index +1);


let result =
while (n > 0) {

406
Advanced level

const factorial = getFactorial(n - 1);


const index = Math.ceil(k /

factorial) - 1;
result += nums[index];
nums.splice(index, 1);
k = k % factorial;
n--;

}
return result;

}
function getFactorial(num) {
let factorial = 1;
for (let i = 2; i <= num; i++) {
factorial *= i;

}
return factorial;

407
Advanced level

// Example usage:
console.log(getPermutation(3, 3));

// Output: "213"

This program defines a function called


'getPermutation' that takes two
parameters, 'n' and k', representing
the total number of digits and the
desired index of the permutation
sequence, respectively. It generates
the kth permutation sequence by using
a factorial-based approach.

The program initializes an array


'nums' with numbers from 1 to n. It
calculates the factorial of n using
the ’getFactorial' helper function.
The variable 'k' is adjusted to be
zero-based.

408
Advanced level

Inside the main loop, the code divides


the factorial by the length of the
'nums' array. It determines the index
of the digit to select based on the
division result and appends it to the
result string. The selected digit is
removed from the 'nums' array. The
value of 'k' is updated by taking the
remainder of the division.

Finally, the function returns the


resulting permutation sequence as a
string.

Importance of this Algorithm in Real-


world Applications:
The permutation sequence algorithm
finds application in various real-
world scenarios where the arrangement
or ordering of elements is important.
Some examples include:

1. Combinatorial Optimization:
Permutation algorithms are used in

409
Advanced level

• optimization problems, such as


scheduling, routing, and resource
allocation, where finding the best
arrangement or order of elements
leads to optimal solutions.
2. Cryptography: Permutations play a
crucial role in cryptographic
algorithms, where the rearrangement
of elements helps in generating
secure keys, ensuring data
integrity, and providing encryption
and decryption mechanisms.
3. Data Analysis: Permutations are
utilized in statistical analysis
and data mining to generate random
samples for hypothesis testing,
simulation studies, and generating
permutations of data for
significance testing and feature
selection.
4. Genetic Algorithms: In evolutionary
computation and genetic algorithms,
permutations are employed to
represent and manipulate candidate
solutions, allowing for crossover,
mutation, and selection operations

410
Advanced level

• that mimic natural selection and


evolution.
5. Game Theory: Permutations are used
in game theory to analyze and model
strategic decision-making, as
players' strategies can be
represented by different
permutations of actions.
6. Network Routing: Permutations are
employed in network routing
algorithms to determine the most
efficient path or sequence of nodes
to transmit data packets across a
network.

Overall, the permutation sequence


algorithm finds utility in diverse
fields, including optimization,
cryptography, data analysis,
evolutionary computation, game theory,
and network routing, contributing to
solving complex problems and improving
efficiency in various real-world
applications.

411
Advanced level

DSA Challenge 16

Text Justification:
Given an array of strings 'words and
a width 'maxWidth', write a program
that will format the text such that
each line has exactly 'maxWidth'
characters and is fully (left and
right) justified.

You should pack your words in a


greedy approach; that is, pack as
many words as you can in each line.
Pad extra spaces when necessary so
that each line has exactly ‘maxWidth’
characters.

Extra spaces between ‘words' should


be distributed as evenly as possible.
If the number of spaces on a line
does not divide evenly between words,
the empty slots on the left will be
assigned more spaces than the slots
on the right.

For the last line of text, it should

413
Advanced level

Note:
• A word is defined as a character
sequence consisting of non-space
characters only.
• Each word's length is guaranteed to
be greater than 0' and not exceed
'maxWidth'.
• The input array words' contains at
least one word.

Example:

Input: words = ["This", "is", ’an",


"example", "of", "text",
justification."], maxWidth = 16
Output: [
"This is an",
"example of text",
justification.
]

414
Advanced level

Our logic:
Here's a JavaScript program that
formats the text according to the
given requirements:

function justifyText(words, maxWidth)

{
const lines - [];
let currentLine = [];
let currentwidth = 0;
for (let i = 0; i < words.length; i+

+) {
const word = words[i];

if (currentwidth +
currentLine.length + word.length <=
maxWidth) {
currentLine.push(word);
currentwidth += word.length;

415
Advanced level

} else {
lines.push(j ustifyLine(currentLine,
currentwidth, maxWidth));

currentLine = [word];
currentwidth = word.length;

}
lines.push(j ustifyLastLine(currentLi
ne, currentwidth, maxWidth));
return lines;

function j ustifyLine(line,
currentwidth, maxWidth) {
const numWords = line.length;
const numSpaces = maxWidth -
currentwidth;
if (numWords === 1) {

416
Advanced level

return line[0] + *
' .repeat(numSpaces);

}
const numGaps = numWords - 1 ;
const spacesPerGap =
Math.floor(numSpaces / numGaps);
const extraSpaces = numSpaces %
numGaps;
let justifiedLine = '1;
for (let i = 0; i < numWords; i++) {
justifiedLine += line[i];
if (i < numGaps) {
justifiedLine += 1
' .repeat(spacesPerGap);
if (i < extraSpaces) {
justifiedLine += ' ' ;

417
Advanced level

}
}
}

return j ustifiedLine;

function justifyLastLine(line,
currentwidth, maxWidth) {
let justifiedLine = line.join(' ');
const numSpaces = maxWidth -
currentwidth - (line.length - 1);
justifiedLine += '
' . repeat(numSpaces);

return justifiedLine;

418
Advanced level

This algorithm takes an array of words


and a maximum width as input. It
iterates through the words, packing
them into lines while maintaining the
maximum width constraint. It then
justifies the lines by distributing
the extra spaces between the words.

The last line is left-justified


without any extra spaces. The function
'justifyText' returns an array of the
justified lines.

Importance of Text Justification


Algorithm in Real-world Applications:
The text justification algorithm is
important in various real-world
applications where formatting and
presentation of text are crucial. Here
are a few examples:

1 . Document Processing: Text


justification is essential in word
processors, document editors, and

419
Advanced level

• typesetting systems to ensure


visually appealing and
professional-looking documents.
2. Text Display: In graphical user
interfaces (GUI), websites, and
mobile applications, text
justification plays a significant
role in displaying content neatly,
especially in text boxes, captions,
and headings.
3. Print Media: Newspapers, magazines,
and books rely on text
justification to create visually
balanced and readable paragraphs,
enhancing the overall reading
experience.
4. Reporting and Documentation: When
generating reports or formatting
documentation, the text
justification algorithm helps
organize information systematically
and present it in a structured
manner.
5. Accessibility: Text justification
also contributes to enhancing
accessibility for individuals with

420
Advanced level

• visual impairments. Properly


aligned and spaced text improves
readability for screen readers and
other assistive technologies.

Overall, the text justification


algorithm ensures optimal use of
available space, improves readability,
and enhances the visual appeal of text
in various applications across
different industries.

421
Advanced level

DSA Challenge 17

Partition List:
Given the head of a linked list and a
value 'x', partition it such that all
nodes less than x' come before nodes
greater than or equal to x'.

You should preserve the original


relative order of the nodes in each
of the two partitions.

Example:

Input: head = [1,4,3,2,5,2], x = 3


Output:[1,2,2,4,3,5]

423
Advanced level

Our logic:
To solve this problem, we can use a
two-pointer approach. We maintain two
separate lists: one for nodes less
than x and another for nodes greater
than or equal to x. We iterate
through the original linked list and
append nodes to their respective
lists based on the given condition.

Here's a JavaScript implementation:

class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;

}
>
function partition(head, x) {
// Create two dummy nodes for the
two partitions

424
Advanced level

const beforeHead = new ListNode(0);


const afterHead = new ListNode(O);

let before = beforeHead;


let after = afterHead;

while (head) {
if (head.val < x) {
// Node less than x, append to
before partition
before.next = head;
before = before.next;
} else {
// Node greater than or equal to
x, append to after partition
after.next = head;
after = after.next;

425
Advanced level

// Move to the next node


head = head.next;

}
// Connect the two partitions and
set the last node's next as null
after.next = null;
iefore.next = afterHead.next;

// Return the modified list


return beforeHead.next;

This algorithm has a time complexity


of 0(n), where n is the number of
nodes in the linked list. It
effectively partitions the list based
on the given condition while
preserving the original relative order
of the nodes in each partition.

426
Advanced level

Importance of Partition List Algorithm


in Real-world Applications:
The partition list algorithm plays an
important role in various real-world
applications that involve data
organization and manipulation. Here
are a few examples:

1. Data Filtering and Segregation: In


data analysis and processing tasks,
the partition list algorithm can be
used to separate and categorize
data based on specific criteria. It
allows for efficient filtering of
data elements, such as segregating
transactions by date, grouping
customers by demographics, or
categorizing products by price
range.
2. Database Operations: When working
with databases, the partition list
algorithm can be utilized to
optimize query performance. By
partitioning the data based on
certain conditions, such as ranges
of values or specific attributes,

427
Advanced level

• it becomes easier to retrieve and


manipulate relevant subsets of data
efficiently.
3. Resource Allocation: In resource
management systems, such as task
scheduling or memory allocation,
the partition list algorithm can
help in organizing and allocating
resources effectively. For example,
in a multitasking operating system,
it can be used to partition
available memory for different
processes, ensuring efficient
utilization and allocation of
system resources.
4. Sorting and Searching: The
partition list algorithm serves as
a fundamental step in various
sorting and searching algorithms.
For instance, in quicksort, the
partition algorithm is used to
divide the array into smaller
subarrays for further sorting. In
binary search, it helps identify
the appropriate partition where the
desired element might be located.

428
Advanced level

Overall, the partition list algorithm


provides a flexible and efficient
approach to separate and organize
data, enabling streamlined operations,
improved performance, and better
resource utilization in a wide range
of real-world applications.

429
Advanced level

DSA Challenge 18

Restore IP Addresses:
A valid IP address consists of
exactly four integers separated by
single dots. Each integer is between
'0' and '255' (inclusive) and cannot
have leading zeros.
• For example, ”0.1.2.201" and
"192.168.1.1" are valid IP
addresses, but "0.011.255.245",
"192.168.1.312" and "192.168@1.1"
are invalid IP addresses.

Given a string s' containing only


digits, return all possible valid IP
addresses that can be formed by
inserting dots into 's'. You are not
allowed to reorder or remove any
digits in s. You may return the valid
IP addresses in any order.

Example:

Input: s = "25525511135"
Output:
["255.255.11.135","255.255.111 .35"]

431
Advanced level

Our logic:
To solve the problem of generating
valid IP addresses from a given
string, we can use a recursive
approach.

Here s a JavaScript program that


accomplishes this:

function restorelpAddresses(s) {
const result = [];
backtraces, [], result);
return result;
>

function backtrack(remaining,
ipAddress, result) {
if (ipAddress.length === 4 &&
remaining === '') {
result.push(ipAddress.join(1 . ' )) ;

return;

432
Advanced level

}
if (ipAddress.length === 4 ||
remaining === '') {
return;

}
for (let i = 1; i <= 3 && i <=
remaining.length; i++) {
const segment = remaining.slice(0,

i);
if (isValidSegment(segment)) {
ipAddress.push(segment);
backtrack(remaining.slice(i),
ipAddress, result);
ipAddress.pop() ;

}
}

433
Advanced level

function IsValidSegment(segment) {
if (segment.length > 1 && segment[0]

=== '0') {
return false;

}
const num = parselnt(segment);
return num >= 0 && num <= 255;

// Example usage:
console.log(restorelpAddresses("255255

11135'));

This program uses a recursive


backtracking algorithm to generate all
possible valid IP addresses. It starts
with an empty list to store the
segments of the IP address. In each
iteration, it tries to take 1, 2, or 3
digits from the remaining string and
checks if the segment is valid. If it

434
Advanced level

IP address, and the process continues


recursively with the remaining digits.
If all four segments are formed and
there are no remaining digits, the
current IP address is added to the
result.

The program handles cases where


leading zeros are not allowed and
checks that each segment falls within
the valid range of 0 to 255. The valid
IP addresses are collected in the
'result' array and returned at the
end.

Usefulness of this Algorithm in Real-


world Applications:
The IP addresses algorithm described
above, which generates all possible
valid IP addresses from a given
string, can be important in various
real-world applications. Here are a
few examples:

1. Network Administration: The

435
Advanced level

• algorithm can be used in network


administration tools to validate
and parse user input when
configuring network settings. It
ensures that entered IP addresses
are valid and helps in detecting
any input errors or
inconsistencies.
2. Log Analysis: In log analysis
systems, IP addresses are often
recorded and analyzed. The
algorithm can be utilized to
extract and validate IP addresses
from log files, ensuring that only
valid IP addresses are processed
for further analysis.
3. Security Applications: TP addresses
play a crucial role in security
applications such as firewalls,
intrusion detection systems, and
access control mechanisms. The
algorithm can be employed to
validate and filter incoming or
outgoing IP addresses, ensuring
that only valid IP addresses are
allowed or denied access.
4. Web Development: Web applications

436
Advanced level

• often deal with IP addresses for


various purposes, such as user
authentication, geolocation, and
access control. The algorithm can
assist in parsing and validating IP
addresses entered by users,
preventing potential security
vulnerabilities or input errors.
5. Data Analysis: In data analysis
scenarios, IP addresses may be
included as part of datasets. The
algorithm can help validate and
preprocess IP addresses, ensuring
data consistency and accuracy
before performing analytical tasks.

In all these applications, having a


reliable algorithm to generate and
validate IP addresses is essential for
maintaining the integrity, security,
and proper functioning of the systems
that rely on IP addresses.

437
Advanced level

DSA Challenge 19

Unique Binary Search Trees:


Given an integer 'n', return the
number of structurally unique BST’s
(binary search trees) which has
exactly 'n' nodes of unique values
from '1' to 'n'.

Example 1:

Input: n = 3
Output: 5

Example 2:

Input: n = 1
Output: 1

439
Advanced level

Our logic:
Here's a JavaScript program that
calculates the number of structurally
unique BSTs for a given integer n:

function countllniqueBSTs(n) {
const dp = new Array(n +

dp[0] = 1;
dp[1] = 1;

for (let i = 2; i <= n; i++) {

for (let j = 1; j <= i; j++) {


dp[i] += dp[j - 1] * dp[i - j];

return dp[n];

440
Advanced level

// Example usage:
console.log(countUniqueBSTs(3)); //
Output: 5

The program uses dynamic programming


to calculate the number of unique
BSTs. It initializes an array dp' of
size n+1' and sets the base cases
dp[0j = 1' and dp[1] = 1'. It then
Iterates from 2' to *n't calculating
the number of unique BSTs for each
value using the formula 'dp[i] +=
dp[j - 1] * dpfi - j]'. Finally, it
returns ’dp[n]', which represents the
number of structurally unique BSTs
with 'n' nodes.

441
Advanced level

Usefulness of this Algorithm in Real-


world Applications:
The algorithm for counting the number
of structurally unique binary search
trees (BSTs) has several real-world
applications. Here are a few examples:

1. Compiler Design: The algorithm can


be used in compiler design to
optimize code generation. It helps
in determining the number of unique
ways to arrange expressions or
variables in a syntax tree, which
can improve the efficiency of the
generated code.
2. Network Routing: In network routing
algorithms, the unique BST
algorithm can be utilized to
calculate the number of possible
routing paths or configurations. It
assists in determining the optimal
routing strategy based on the
number of available nodes.
3. Data Structures: The algorithm is
relevant to data structures such as
binary search trees, AVL trees, and

442
Advanced level

• red-black trees. Understanding the


number of structurally unique BSTs
helps in analyzing the performance
and efficiency of these data
structures.
4. Game Theory: The algorithm finds
applications in game theory for
analyzing and evaluating different
strategies. It aids in determining
the number of possible game states
or decision trees, which is crucial
for game planning and strategic
analysis.

Overall, the unique BST algorithm is


valuable in various fields involving
tree structures, optimization, and
decision-making processes. It provides
insights into the number of unique
configurations, enabling efficient
resource allocation, code
optimization, and strategic planning.

443
Advanced level

DSA Challenge 20

Validate Binary Search Tree:


Given the root of a binary tree,
determine if it is a valid binary
search tree (BST).
A valid BST is defined as follows:
• The left
• subtree
• of a node contains only nodes with
keys less than the node's key.
• The right subtree of a node
contains only nodes with keys
greater than the node's key.
• Both the left and right subtrees
must also be binary search trees.

Example:

Input: root = [2,1,3]


Output: true

445
Advanced level

Our logic:
To determine if a binary tree is a
valid binary search tree (BST), you
can use the following algorithm:
1 . Implement a recursive helper
function that takes a node, along
with a minimum and maximum value.
2. If the current node is null, return
true since an empty tree is
considered a valid BST.
3. If the current node's value is not
within the valid range defined by
the minimum and maximum, return
false.
4. Recursively call the helper
function for the left subtree,
passing the updated maximum value
as the new maximum.
5. Recursively call the helper
function for the right subtree,
passing the updated minimum value
as the new minimum.
6. If both recursive calls return
true, it means the left and right
subtrees are valid BSTs, so return
true.

446
Advanced level

7. If any of the conditions fail,


return false.

Here's a JavaScript program that


implements the algorithm:

function isValidBST(root) {
return isValidBSTHelper(root, null,
null) ;

}
function isValidBSTHelper(node, min,
max) {
if (node === null) {
return true;

}
if ((min !== null && node.val <=
min) || (max !== null && node.val >=
max)) {
return false;

447
Advanced level

}
return (
isValidBSTHelper(node.left, min,
node.val) &&
isValidBSTHelper(node.right,
node.val, max)

);

Importance of this Algorithm in Real-


world Applications:
The usefulness of the validate binary
search tree (BST) algorithm in real-
world applications is significant.
Here are a few examples:

1. Data Validation: The algorithm is


commonly used to validate the
correctness of data structures or
databases that are designed to

448
Advanced level

• adhere to the BST property. It


ensures that the data is organized
in a way that enables efficient
searching and retrieval operations.
2. Algorithm Design: The validate BST
algorithm serves as a building
block for various algorithms that
operate on BSTs. It can be used as
a subroutine in algorithms like
tree traversal, tree modification,
and tree balancing. By validating
the BST property, it helps ensure
the correctness and efficiency of
these algorithms.
3. Compiler Design: In compiler
design, the algorithm is employed
for syntax checking and semantic
analysis. It can be used to
validate the correctness of syntax
trees or symbol tables that rely on
the BST property to store and
retrieve program identifiers or
expressions.
4. Database Systems: BSTs are
frequently used in database
indexing structures such as B-trees

449
Advanced level

• and AVL trees. The validate BST


algorithm is essential for
maintaining the integrity of these
index structures and ensuring the
correct ordering of data for
efficient querying and sorting.
5. Binary Search: The algorithm can be
applied in various search and
retrieval scenarios where binary
search is utilized. By validating
that a given tree is a valid BST,
it guarantees the correct ordering
of elements, enabling efficient
binary search operations.

Overall, the validate BST algorithm


plays a crucial role in ensuring the
correctness, efficiency, and
reliability of various applications
that involve binary search trees. It
helps maintain data integrity,
facilitates efficient searching and
sorting, and serves as a foundation
for developing more complex algorithms
and data structures.

450
Advanced level

DSA Challenge 21

Recover Binary Search Tree:


You are given the 'root' of a binary
search tree (BST), where the values
of exactly two nodes of the tree were
swapped by mistake. Recover the tree
without changing its structure.

Example:

Input: root = [1,3,null,null,2]


Output: [3,1,null,null,2]
Explanation: 3 cannot be a left child
of 1 because 3 > 1. Swapping 1 and 3
makes the BST valid.

452
Advanced level

Our logic:
To recover a binary search tree (BST)
where the values of exactly two nodes
were swapped, you can use the
following JavaScript program:

class TreeNode {
constructor(val, left, right) {
this.val = val;

this.left - left || null;


this.right = right || null;

function recoverTree(root) {
let firstNode = null;
let secondNode = null;
let prevNode = new TreeNode(-
Infinity);

453
Advanced level

// Helper function to find the two


swapped nodes
function findSwappedNodes(node) {
if (!node) return ;
// In-order traversal
findSwappedNodes(node.left);
// Check for the swapped nodes
if ((firstNode && prevNode.val >=
node.val) {
firstNode = prevNode;

}
if (firstNode && prevNode.val >=
node.val) {
secondNode = node;

}
prevNode = node;

454
Advanced level

findSwappedNodes(node.right);

}
// Find the two swapped nodes in the
BST
findSwappedNodes(root);
// Swap the values of the two nodes
const temp = firstNode.val;
firstNode.val = secondNode.val;
secondNode.val = temp;
return root;

This program uses in-order traversal


to find the two swapped nodes in the
BST. It maintains the previously
visited node ('prevNode') and checks
for any violations of the BST
property. Once the two swapped nodes
are identified, their values are
swapped to recover the original tree

455
Advanced level

structure.

Note: This program assumes that


exactly two nodes were swapped and
that the tree structure remains the
same.

Importance of this Algorithm in Real-


world Applications:
The recover binary search tree
algorithm is useful in real-world
applications where a binary search
tree (BST) has been corrupted due to
the values of two nodes being swapped
by mistake. It allows you to fix the
BST and restore its original structure
without changing the overall
arrangement of the nodes.

Here are some potential use cases:


1. Data Integrity: Binary search trees
are commonly used in data storage
and retrieval systems.

456
Advanced level

• If a BST is used to store important


data, a corruption in the tree's
structure can lead to incorrect or
inconsistent information. The
recover BST algorithm can be
employed to rectify such
corruptions and ensure data
integrity.
2. Database Management: BSTs are
employed in database indexing and
query optimization. If a BST used
for indexing becomes corrupted, it
can impact the efficiency and
accuracy of queries. The recover
BST algorithm can help restore the
proper indexing structure,
enhancing database performance.
3. Software Development: Binary search
trees are fundamental data
structures used in various
algorithms and software components.
During software development, bugs
or errors might introduce
inconsistencies in the BSTs used.
The recover BST algorithm can aid

457
Advanced level

• in identifying and fixing such


issues, ensuring the correct
functioning of the software.
4. Data Analysis: BSTs are employed in
various data analysis techniques,
such as binary search tree
traversal algorithms or binary
search tree-based statistjcal
computations. If the BST used for
data analysis becomes corrupted,
the results and conclusions drawn
from the analysis may be
inaccurate. The recover BST
algorithm can help rectify the
corruption and ensure reliable data
analysis.

Overall, the recover binary search


tree algorithm is valuable in
situations where the integrity of a
binary search tree has been
compromised, ensuring the accurate
representation and functionality of
the data structure in real-world
applications.

458
Fi Edi Select Vie

|-Pl ”• HDSA.js X
Advanced level

DSA Challenge 22

Binary Tree Level Order Traversal:


Given the 'root' of a binary tree,
return the level order traversal of
its nodes' values. (i.e., from left
to right, level by level).

Example 1:

Input: root = [3,9,20,0011,0011,15,7]


Output: [[3],[9,20],[15,7]]

Example 2:

Input: root =
Output: [[1]]

460
Advanced level

Our logic:
Here's a JavaScript program that
performs a level order traversal on a
binary tree and returns the nodes'
values in an array:

class TreeNode {
constructor(val, left = null, right
= null) {
this.val = val;
this.left = left;
this.right = right;

function levelOrderTraversal(root) {
if (!root) {
return [ ];

461
Advanced level

const queue = [root];


while (queue.length > 0) {
const levelSize - queue.length ;
const currentLevel = [ ] ;
for (let i = 0; i < levelSize; i+

+) {
const node = queue.shift();
currentLevel.push(node.val);
if (node.left) {
queue.push(node.left) ;

}
if (node.right) {
queue.push(node.right) ;

}
}
result.push(currentLevel) ;

462
Advanced level

return result;

}
// Example usage:
const root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);

console.loc(levelOrderT raversal(root
)); // Output: [[3], [9, 20], [15, 7]]

This program utilizes a breadth-first


search (BFS) approach to traverse the
binary tree level by level. The nodes'
values at each level are stored in
separate arrays, and all these arrays
are collected in the final result
array.

463
Advanced level

Importance of this Algorithm in Real-


world Applications:
The Binary Tree Level Order Traversal
algorithm has several real-world
applications, including:

I.Tree analysis: It allows us to


examine the structure and contents
of a binary tree in a systematic
way. By traversing the tree level
by level, we can gain insights into
its organization and identify
patterns or irregularities.
2. Breadth-first search: The level
order traversal algorithm is a key
component of the breadth-first
search algorithm. BFS is widely
used in graph algorithms and
applications such as social network
analysis, web crawling, shortest
path finding, and network routing.
3. Tree visualization: The level order
traversal provides a natural way to
visualize the nodes of a binary
tree. By printing the values level

464
Advanced level

• by level, we can create a


hierarchical representation of the
tree, which is helpful for
debugging, data exploration, and
presentation purposes.
4. Tree-based processing: Level order
traversal allows us to process the
nodes of a binary tree in a
specific order. For example, in a
binary tree with multiple levels of
dependencies, we can process the
nodes level by level to ensure that
the dependencies are satisfied
before proceeding to the next
level.

Overall, the Binary Tree Level Order


Traversal algorithm is a fundamental
tool for exploring and understanding
binary trees, and it forms the basis
for various tree-based algorithms and
applications in computer science and
beyond.

465
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 23

Binary Tree Zigzag Level Order


Traversal:
Given the 'root' of a binary tree,
return the zigzag level order
traversal of its nodes' values,
(i.e., from left to right, then right
to left for the next level and
alternate between).

Example:

Input: root = [3,9,20,null,null,15,7]


Output: [[3],[20,9],[15,7]]

467
Advanced level

Our logic:
Here's a JavaScript program that
performs the zigzag level order
traversal of a binary tree:

class TreeNode {
constructor(val, left = null, right
= null) {
this.val = val;
this.left = left;
this.right = right;

}
}
function zigzagLevelOrder(root) {
if (!root) return [];
const result = [];
const queue = [root];

let level = 0;
while (queue.length > 0) {

468
Advanced level

const levelSize = queue.length;


const levelNodes = [ ] ;
for (let i = 0; i < levelSize; i+

+) {
const node = queue.shift() ;
if (level % 2 === 0) {
levelNodes.push(node.val); //
Left to right
} else {
levelNodes.unshift(node.val) ;
// Right to left

}
if (node.left)
queue.push(node.left) ;
if (node.right)
queue.push(node.right) ;

469
Advanced level

result.push(levelNodes);
level++;

}
return result;
I

// Example usage:
const root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);

console.log(zigzagLevelOrder(root));
// Output: [[3], [20, 9], [15, 7]]

The zigzagLevelOrder function performs


a level order traversal of the binary
tree while alternating between adding

470
Advanced level

nodes from left to right and right to


left at each level. The result is
stored in the result array, which is
returned at the end.

Importance of this Algorithm in Real-


world Applications:
The Binary Tree Zigzag Level Order
Traversal algorithm has several real-
world applications, including:

1 . Tree Visualization: Zigzag level


order traversal helps in
visualizing the structure of a
binary tree. By traversing the tree
in a zigzag manner, it provides a
different perspective on the tree's
layout, which can be useful for
understanding and analyzing complex
tree structures.
2. User Interface Design: In
applications that involve
displaying hierarchical data, such
as file systems or organizational
charts, the zigzag level order

471
Advanced level

• traversal can help in presenting


the data in an organized and
visually appealing manner. It
allows for an intuitive
representation of the tree
structure, where nodes at the same
level are grouped together and
alternate their direction.
3. Data Analysis: Binary trees are
often used to represent
hierarchical relationships in
various domains, such as genealogy,
organizational hierarchies, or
decision trees. The zigzag level
order traversal can aid in
analyzing and extracting meaningful
insights from the data stored in
these binary trees. It allows for
examining the data at different
levels and patterns within the tree
structure.
4. Breadth-First Search (BFS): The
zigzag level order traversal
algorithm is an adaptation of the
traditional BFS algorithm. BFS is a
fundamental algorithm used in graph
traversal and can be applied to

472
Advanced level

• binary trees as well. By modifying


the BFS approach to zigzag level
order traversal, it provides a
different order of visiting nodes
and can be advantageous in certain
scenarios.

Overall, the Binary Tree Zigzag Level


Order Traversal algorithm is valuable
for visualizing, presenting,
analyzing, and manipulating binary
trees in various applications and
domains. It offers an alternative
perspective on tree structures and
facilitates efficient data processing
and exploration.

473
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 24

Construct Binary Tree from Preorder


and Inorder Traversal:
Given two integer arrays 'preorder'
and 'inorder' where 'preorder' is the
preorder traversal of a binary tree
and 'inorder' is the inorder
traversal of the same tree, write a
javascript program to construct and
return the binary tree.

Example:

Input: preorder = [3,9,20,15,7],


inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

475
Advanced level

Our logic:
Here's a JavaScript program to
construct a binary tree from its
preorder and inorder traversals:

class TreeNode {
constructor(val, left = null, right
= null) {
this.val = val;
this.left = left;
this.right = right;

}
}
function buildTree(preorder, inorder)

{
if (preorder.length === 0 ||
inorder.length === 0) {
return null; // Base case: empty
arrays

476
Advanced level

}
const rootVal = preorder[0];
const root = new TreeNode(rootVal);
const rootindex =
inorder.indexOf(rootVal);
const leftinorder = inorder.slice(0,
rootindex);
const rightinorder =
inorder.slice(rootindex + 1);
const leftPreorder =
preorder.slice(1, 1 +
leftinorder.length) ;
const rightPreorder =
preorder.slice(1 +
leftInorder.length);
root.left = buildTree(leftPreorder,

477
Advanced level

leftinorder);
root.right =
buildT ree(rightPreorder,
rightinorder);
return root;

// Example usage:
const preorder = [3, 9, 20, 15, 7];
const inorder = [9, 3, 15, 20, 7];
const root = buildTree(preorder,
inorder);
console.log(root);

The buildTree' function takes two


arrays, 'preorder' and 'inorder',
representing the 'preorder' and
'inorder' traversals of a binary tree,
respectively.

478
Advanced level

It recursively constructs the binary


tree based on these traversals.

First, it checks for the base case


where either of the arrays is empty,
indicating a null node. In this case,
it returns 'null'. Next, it retrieves
the root value from the 'precrder'
array, creates a new 'TreeNode' with
that value, and finds its index in the
'inorder' array. It then splits the
'inorder' array into left and right
subtrees based on the root index, and
does the same for the preorder'
array.

Recursively, it calls 'buildTree' with


the left subtree's 'preorder' and
'inorder' arrays to construct the left
subtree, and does the same for the
right subtree.

Finally, it assigns the left and right


subtrees to the root node and returns
the root.

479
Advanced level

Importance of this Algorithm in Real-


world Applications:
The "Construct Binary Tree from
Preorder and Inorder Traversal"
algorithm is important in real-world
applications involving binary tree
manipulation and reconstruction. Here
are a few examples of its usefulness:

1 . Compiler Construction: In compiler


design, this algorithm can be used
to construct an abstract syntax
tree (AST) from the preorder and
inorder traversals of a source code
program. The AST is a crucial data
structure used in various stages of
compilation, including parsing,
semantic analysis, and code
generation.
2. Database Systems: Binary trees are
commonly used in database systems
to implement indexing structures
such as B-trees and binary search
trees (BSTs). This algorithm can
help in constructing and

480
Advanced level

• reconstructing these index trees


efficiently, based on their
preorder and inorder traversals.
3. Graphical User Interfaces (GUIs):
Tree-like structures are often used
in GUI development, where elements
such as menus, navigation trees,
and hierarchical data are
represented. The algorithm can aid
in constructing these tree
structures from their preorder and
inorder traversals, enabling
efficient rendering and
manipulation of GUI components.
4. Artificial Intelligence: Decision
trees and binary trees play a vital
role in various Al algorithms,
including decision tree learning
and game-playing algorithms. This
algorithm can be utilized to
construct and reconstruct decision
trees from their preorder and
inorder traversals, enabling
efficient decision-making and
strategy formulation.

481
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 25

Convert Sorted List to Binary Search


T ree:
Given the 'head' of a singly linked
list where elements are sorted in
ascending order, convert it to a
height-balanced (i.e. where the depth
of the two subtrees of every node
never differs by more than one)
binary search tree.

Example:

483
Advanced level

Input: head = [-10,-3,0,5,9]


Output: [0,-3,9,-10,null,5]
Explanation: One possible answer is
[0,-3,9,-10,null,5], which represents
the shown height balanced BST.

Our logic:
Here's a JavaScript program to
construct a binary tree from its
preorder and inorder traversals:

class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;

}
}
class TreeNode {
constructor(val, left = null, right
= null) {

484
Advanced level

this.val = val;
this.left = left;
this.right = right;

}
>

function sortedListToBST(head) {
if (!head) {
return null;

}
// Find the middle node of the
linked list
const midNode = findMiddle(head);
// Create a new TreeNode with the
middle value
const rootNode = new
TreeNode(midNode.val);

485
Advanced level

// Base case: If there's only one


node in the linked list
if (head === midNode) {
return rootNode;

}
// Recursively convert the left and
right halves of the linked list to BST
rootNode.left =
sortedListToBST(head) ;
rootNode.right =
sortedListToBST(midNode.next);
return rootNode;

function findMiddle(head) {
let slow = head;
let fast = head;
let prev = null;

486
Advanced level

while (fast && fast.next) {


prev = slow;
slow = slow.next;
fast = fast.next.next;

}
if (prev) {
prev.next = null; // Break the
linked list into two halves

}
return slow;

}
// Example usage:
const linkedList = new ListNode(-10);
linkedList.next = new ListNode(-3);
linkedList.next.next = new
ListNode(0);
linkedList.next.next.next - new

487
Advanced level

ListNode(5) ;
linkedList.next.next.next.next = new
ListNode(9);
const bst =
sortedListToBST(linkedList);
console.log(bst);

This program converts a sorted singly


linked list into a height-balanced
binary search tree. It finds the
middle node of the linked list using
the slow and fast pointer technique.
Then, it creates a new 'TreeNode' with
the value of the middle node and
recursively converts the left and
right halves of the linked list into
the left and right subtrees of the
BST, respectively.

The algorithm ensures that the


resulting binary search tree is
balanced, which means the heights of
the left and right subtrees differ by

488
Advanced level

the left and right subtrees differ by


at most one.

Importance of this Algorithm in Real-


world Applications:
The "Convert Sorted List to Binary
Search Tree" algorithm has several
applications in real-world scenarios:

1. Balanced Binary Search Tree


Construction: The algorithm
constructs a height-balanced binary
search tree from a sorted linked
list. Balanced binary search trees
are essential in scenarios where
efficient searching and retrieval
operations are required, such as
database indexing or maintaining
sorted data structures.
2. Data Analysis and Visualization:
The algorithm can be used to
transform sorted linked list data
into a binary search tree
representation. This conversion
facilitates data analysis and

489
Advanced level

• visualization tasks, enabling


efficient traversal, querying, and
manipulation of the data for
various purposes, such as
generating reports, performing
statistical analyses, or building
decision trees.
3. Algorithm Design and Analysis: The
algorithm exemplifies the use of
recursion and linked list
manipulation to solve a specific
problem. It demonstrates important
concepts such as dividing a problem
into smaller subproblems, tree
construction, and maintaining
balance. As such, it serves as an
illustrative example for algorithm
design and analysis studies,
helping students and researchers
understand and analyze similar
algorithms.

Overall, the "Convert Sorted List to


Binary Search Tree" algorithm plays a
vital role in transforming sorted
linked list data into a balanced

490
Advanced level

binary search tree, enabling efficient


search and manipulation operations.
Its applications span various domains,
including data processing, analysis,
algorithm design, and problem-solving.

491
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 26

Flatten Binary Tree to Linked List:


Given the 'root' of a binary tree,
flatten the tree into a "linked
list*1 *
• The "linked list" should use the
same 'TreeNode' class where the
'right' child pointer points to the
next node in the list and the
'left' child pointer is always
' null' .
• The "linked list" should be in the
same order as a pre-order traversal
of the binary tree.

Example:

493
Advanced level

Input: root = [1,2,5,3,4,0011,6]


Output:
[1,null,2,null,3,null,4,null,5,null,6]

Our logic:
Here's a JavaScript program that
flattens a binary tree into a linked
list using a pre-order traversal:

class TreeNode {

constructor(val, left = null, right


= null) {

this.val = val;
this.left = left;
this.right = right;

function flattenTree(root) {

494
Advanced level

if (!root) return ;
let stack = [root];
let prev = null;
while (stack.length) {
let node = stack.pop();
if (node.right) {
stack.push(node.right);

}
if (node.left) {
stack.push(node.left);

}
if (prev) {

prev.right = node;
prev.left = null;

}
prev = node;

495
Advanced level

This program uses a stack to perform a


pre-order traversal of the binary
tree. It iteratively processes each
node, setting the right child pointer
to the next node in the linked list
and the left child pointer to 'null'.
Finally, it prints the values of the
linked list in the same order as the
pre-order traversal of the binary
tree.

Importance of this Algorithm in Real-


world Applications:
The Flatten Binary Tree to Linked List
algorithm is useful in various real-
world applications, especially those
involving tree-based data structures.
Here are some examples of its
importance:

1. Efficient Storage: By converting a


binary tree into a linked list, the
resulting structure requires less
memory compared to the original

497
Advanced level

• tree. This can be beneficial in


scenarios where memory efficiency
is crucial, such as in embedded
systems or environments with
limited resources.
2. Tree Traversal: The flattened
linked list preserves the pre-order
traversal order of the original
binary tree. This can be
advantageous in applications that
require efficient traversal of tree
nodes in a specific order. For
example, in certain tree processing
algorithms or when performing
operations like searching, sorting,
or filtering on the tree elements.
3. Tree Manipulation: The flattened
linked list representation
simplifies various tree
manipulation operations. For
instance, it can make it easier to
perform modifications on the tree
structure, such as rearranging
nodes, reordering elements, or
applying certain algorithms that
work more efficiently on linear

498
Advanced level

• data structures.
4. Integration with Other Data
Structures: The flattened linked
list can be seamlessly integrated
with other data structures or
algorithms that expect linear
structures. This enables efficient
data processing and interoperation
with existing algorithms or systems
that operate on sequential data.

Overall, the Flatten Binary Tree to


Linked List algorithm provides a
versatile transformation that
optimizes memory usage, simplifies
tree manipulation, and enables
efficient traversal and integration
with other algorithms. Its importance
lies in its ability to enhance the
performance and versatility of
applications that involve binary tree
structures.

499
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 27

Distinct Subsequences:
Given two strings 's' and 't'( return
the number of distinct
subsequences (i.e. a string that is
derived from another string by
deleting some or no characters
without changing the order of the
remaining characters) of 's' which
equals 't'.

Example:

Input: s = "babgbag", t = "bag"


Output: 5
Explanation:
As shown below, there are 5 ways you
can generate "bag" from s.
babqbaq
babqbag
jabqbr-g
babgbag
babqbag

501
Advanced level

Our logic:
Here's a JavaScript program that
calculates the number of distinct
subsequences of s' that equal 't':

function numDistinct(s, t) {

const m = s.length;
const n = t.length;
// Create a 2D array to store the
dynamic programming results
const dp = Array(m +
1).fill(O).map(() => Array(n +
1).fill(0));
// Initialize the first column with
1s since an empty string is a
subsequence of any string
for (let i = 0; i <= m; i++) {
dp[i][0] = 1 ;

502
Advanced level

// Calculate the number of distinct


subsequences using dynamic programming
for (let i = 1; i <= m; i++) {

for (let j = 1; j <= n; j++) {


// If the characters match, we
can either include or exclude the
current character
if (s[i - 1] === t[j - 1 ]) {
dp[i][j ] = dp[i - 1][j - 1 ] +

dp[i - 1][j];
} else {
// If the characters don't
match, we can only exclude the current
character
dp[i][j ] = dp[i - 1 ] [ j ];

}
}

503
Advanced level

}
return dp[m][n];

}
// Example usage:
console.log(numDistinct("babgbag",
"bag")); // Output: 5

The 'numDistinct' function uses


dynamic programming to calculate the
number of distinct subsequences of 's'
that equal 't'. It initializes a 2D
array 'dp' to store the intermediate
results. The algorithm iterates over
's' and 't' and fills in the 'dp'
array based on the following logic:

• If the characters match, we have


two choices: include the current
character ('dp[i - 1][j - 1]') or
exclude the current character
(' dp[i - 1 ] [ j ]').
• If the characters don't match, we
can only exclude the current

504
Advanced level

character ('dp[i - 1 ] [ j ] ') .

Finally, the function returns 'dp[m]


[n]', which represents the number of
distinct subsequences of 's' that
equal 't'.

The algorithm has a time complexity of


0(m * n), where m and n are the
lengths of strings 's' and 't',
respectively.

Importance of Distinct Subsequences


Algorithm in Real-world Applications:
The Distinct Subsequences algorithm
has various real-world applications in
fields such as natural language
processing, bioinformatics, and data
analysis. Here are a few examples of
its importance:

1. DNA Sequence Analysis: In


bioinformatics, the algorithm can
be used to identify the occurrence

505
Advanced level

• of specific DNA subsequences within


a larger DNA sequence. It helps in
understanding genetic patterns,
gene expression, and identifying
mutations or variations.
2. Text Mining and Information
Retrieval: The algorithm is useful
in text mining tasks where we need
to identify specific patterns or
subsequences within a large corpus
of text. It can aid in information
retrieval, sentiment analysis,
named entity recognition, and other
natural language processing tasks.
3. Plagiarism Detection: The algorithm
can be employed to compare two
pieces of text and determine the
number of distinct subsequences
they share. This can be useful in
plagiarism detection systems that
aim to identify copied or similar
content across documents.
4. Data Analysis and Pattern
Recognition: Distinct subsequences
play a crucial role in data
analysis and pattern recognition

506
Advanced level

• tasks. The algorithm can be


utilized to find recurring patterns
or motifs within time series data,
enabling the discovery of trends,
anomalies, or repeating events.
5. Speech and Audio Processing: In
speech and audio processing, the
algorithm can be applied to
identify specific phonetic
sequences or acoustic patterns
within spoken language. It aids in
speech recognition, speaker
identification, and audio content
analysis.

Overall, the Distinct Subsequences


algorithm has broad applications
across various domains, contributing
to tasks such as sequence analysis,
pattern recognition, information
retrieval, and data analysis. Its
ability to identify distinct
subsequences efficiently makes it
valuable in extracting meaningful
insights from complex data.

507
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 28

T rianqle:
Given a 'triangle' array, return the
minimum path sum from top to bottom.

For each step, you may move to an


adjacent number of the row below.
More formally, if you are on index i
on the current row, you may move to
either index 'i' or index 'i + 1' on
the next row.

Example:

Input: triangle = [[2],[3,4],[6,5,7],


[4,1,8,3]]
Output: 11
Explanation:
The triangle looks like:
2
3 4
6 5 7
4 18 3
The minimum path sum from top to bottom
is 2 + 3 + 5 + 1 =11 (underlined
above).

509
Advanced level

Our logic:
Here's a JavaScript program that
calculates the minimum path sum in a
triangle array:

function minimumTotal(triangle) {

const rows = triangle.length;


// Create a 2D array to store the
minimum path sums
const dp = [...Ar ray(rows)].map(()
=> Array(rows).fill(0));
// Initialize the bottom row of the
dp array with the values of the
triangle
for (let i = 0; i < rows; i++) {
dp[rows - 1][i] = triangle[rows -

11 [i];

510
Advanced level

// Calculate the minimum path sum from


bottom to top
for (let i = rows - 2; i >= 0; i--)

{
for (let j = 0; j <= i; j++) {
// Choose the smaller of the two
adjacent numbers below and add it to
the current number
dp[i][j] = Math.min(dp[i + 1]
[j], dp[i + 1](j + 1]) + triangle[i]

[ i ];

}
// The minimum path sum will be
stored at the top of the dp array
return dp[0][0];

511
Advanced level

// Example usage:
const triangle = [

[2],
[3, 4],
[6, 5, 7],
[4, 1, 8, 3]

];
console.log(minimumTotal(triangle));
// Output: 11

The algorithm uses dynamic programming


to build a 2D array ('dp') that stores
the minimum path sums. It starts from
the bottom row of the triangle and
calculates the minimum path sum for
each element by considering the two
adjacent numbers in the row below.
Finally, it returns the minimum path
sum at the top of the dp array, which

512
Advanced level

represents the minimum path sum from


top to bottom in the triangle.

The program has a time complexity of


0(nA2), where n is the number of rows
in the triangle. This makes it
efficient even for large triangle
arrays.

Importance of the Triangle Algorithm


in Real-world Applications:
The Triangle algorithm, which
calculates the minimum path sum in a
triangle array, has several real-world
applications:

1. Path Optimization: The algorithm


can be used to find the minimum
cost or distance path in various
scenarios, such as transportation
networks, logistics planning, and
routing algorithms. It helps
optimize routes and minimize costs

513
Advanced level

• in scenarios where moving between


adjacent elements is allowed.
2. Graph Traversal: The triangle array
can be seen as a directed acyclic
graph, where each element
represents a node, and the adjacent
elements in the row below represent
the edges. The algorithm's approach
of calculating minimum path sums
can be applied to other graph
traversal problems, such as finding
the shortest path or optimizing a
certain metric across nodes.
3. Financial Modeling: The Triangle
algorithm can be used in financial
modeling and analysis. For example,
it can be applied to scenarios
involving cash flow optimization,
investment planning, or portfolio
management, where minimizing costs
or maximizing returns is a key
objective.
4. Resource Allocation: The
algorithm's ability to calculate
the minimum path sum can be

514
Advanced level

• leveraged in scenarios where


resources need to be allocated
efficiently. This can include
resource planning in manufacturing
processes, task scheduling in
project management, or optimizing
resource utilization in various
operational contexts.

Overall, the Triangle algorithm


provides a fundamental solution for
finding the minimum path sum in a
triangle array, and its underlying
principles of dynamic programming and
graph traversal have broad
applications across different
industries and problem domains.

515
Advanced level

DSA Challenge 29

Best Time to Buy and Sell Stock I:


You are given an integer array
'prices' where 'prices[i]' is the
price of a given stock on the ith
day.

On each day, you may decide to buy


and/or sell the stock. You can only
hold at most one share of the stock
at any time. However, you can buy it
then immediately sell it on the same
day.

Find and return the maximum


you can achieve.

Example 1:

Input: prices = [7,6,4,3,1]


Output: 0
Explanation: There is no way to make a
positive profit, so we never buy the
stock to achieve the maximum profit of
0.

517
Advanced level

Example 2:

Input: prices = [1,2,3,4,5]


Output: 4
Explanation: Buy on day 1 (price = 1)
and sell on day 5 (price = 5), profit =
5-1 = 4.
Total profit is 4.

Example 3:

Input: prices = [7,1,5,3,6,4]


Output: 7
Explanation: Buy on day 2 (price = 1)
and sell on day 3 (price = 5), profit =
5-1 = 4.
Then buy on day 4 (price = 3) and sell
on day 5 (price = 6), profit = 6-3 = 3.
Total profit is 4 + 3 = 7.

518
Advanced level

Our logic:
Here's a JavaScript program that that
considers all possible transactions:

function maxProfit(prices) {
let maxProfit = 0;
for (let i = 1; i < prices.length;

i++) {
// Add the difference between
current and previous price if it's
positive
if (prices[i] > prices[i - 1]) {
maxProfit +- prices[i] -
prices[i - 1];

}
}
return maxProfit;

519
Advanced level

// Example usage:
const prices = [7, 1, 5( 3, 6, 4];
console.log(maxProfit(prices)) ; //
Output: 7

The program iterates through the


‘prices' array and keeps track of the
minimum price seen so far ('minPrice')
and the maximum profit that can be
achieved ('maxProfit').

At each iteration:
• It checks if the current price is
lower than the minPrice' and
updates it if necessary.
• Then it calculates the potential
profit by subtracting the
'minPrice' from the current price.
• It updates the 'maxProfit' if a
higher profit is found.

Finally, the program returns the


'maxProfit' value.

520
Advanced level

This efficient algorithm solves the


problem by iterating through the array
only once, resulting in a time
complexity of 0(n), where n is the
length of the 'prices' array.

Importance of this Algorithm in Real-


world Applications:
The "Best Time to Buy and Sell Stock"
algorithm is important in various
real-world applications, especially in
the field of finance and stock
trading. Here are some of its key
importance:

1. Stock Trading Strategies: The


algorithm helps traders and
investors determine the optimal
time to buy and sell stocks to
maximize their profits. By
analyzing historical price data,
the algorithm assists in
identifying profitable
opportunities and making informed

521
Advanced level

• trading decisions.
2. Portfolio Management: Portfolio
managers utilize the algorithm to
optimize their investment
portfolios. By identifying the best
times to buy and sell stocks, they
can rebalance their portfolios and
allocate their resources
effectively to achieve higher
returns.
3. Algorithmic Trading: The algorithm
is widely used in algorithmic
trading systems, where automated
programs execute trades based on
predefined strategies. It enables
the systems to make real-time
buying and selling decisions,
improving trading efficiency and
profitability.
4. Risk Management: The algorithm aids
in risk management by helping
investors and traders identify
potential losses and take
preventive measures. By considering
the best time to exit a position,
it minimizes the risk of holding

522
Advanced level

• stocks during downturns and market


fluctuations.
5. Financial Analysis: The algorithm
is utilized in financial analysis
to evaluate the performance of
stocks and investment strategies.
By comparing the historical prices
and calculating the maximum profit
that could have been achieved,
analysts can assess the
effectiveness of different trading
approaches.

In summary, the "Best Time to Buy and


Sell Stock" algorithm plays a crucial
role in optimizing investment
decisions, maximizing profits, and
managing risks in various financial
applications. It assists traders,
investors, portfolio managers, and
financial analysts in making informed
and profitable choices in the stock
market.

523
Advanced level

DSA Challenge 30

Best Time to Buy and Sell Stock II:


You are given an integer array
'prices' where 'prices[i]' is the
price of a given stock on the ith
day.

Find the maximum profit you can


achieve. You may complete at most two
transactions.

Note: You may not engage in multiple


transactions simultaneously (i.e.,
you must sell the stock before you
buy again).

Example 1:

Input: prices = [7,6,4,3,1]


Output: 0
Explanation: In this case, no
transaction is done, i.e. max
0.

525
Advanced level

Example 2:

Input: prices = [1,2,3,4,5]


Output: 4
Explanation: Buy on day 1 (price = 1)
and sell on day 5 (price = 5), profit =
5-1 = 4.
Note that you cannot buy on day 1, buy
on day 2 and sell them later, as you
are engaging multiple transactions at
the same time. You must sell before
buying again.

Example 3:

Input: prices = [3,3,5,0,0,3,1,4]


Output: 6
Explanation: Buy on day 4 (price = 0)
and sell on day 6 (price = 3), profit =
3-0 = 3.
Then buy on day 7 (price = 1) and sell
on day 8 (price = 4), profit = 4-1 = 3.

526
Advanced level

Our logic:
Here's a JavaScript program that
solves the problem:

function maxProfit(prices) {
const n = prices.length;
let buyl = -prices[0]; // maximum
profit after the first buy
let selll = 0; // maximum profit
after the first sell
let buy2 = -prices[0]; // maximum
profit after the second buy
let sell2 =0; // maximum profit
after the second sell

for (let i = 1; i < n; i++) {


buyl = Math.max(buy1, -prices[i]);
selll = Math.max(sell1, buyl +
prices[i]);

527
Advanced level

buy2 = Math.max(buy2, selll -


prices[i]);
se!12 = Math.max(sell2, buy2 +
prices[i]);

}
return sell2;

// Example usage:
const prices = [3, 3, 5, 0, 0, 3, 1,

4];
const maxProfitValue =
maxProfit(prices) ;
console.log(maxProfitValue);

This program uses dynamic programming


to keep track of the maximum profit at
each stage. It iterates through the
'prices' array, updating the variables

528
Advanced level

'buyl', 'selll', 'buy2', and 'sell2'


accordingly. The final result is the
maximum profit after the second sell,
which is returned by the 'maxProfit'
function.

The time complexity of this program is


0(n), where n is the length of the
'prices' array, since we iterate
through the array only once.

Importance of this Algorithm in Real-


world Applications:
The algorithm for finding the maximum
profit with at most two transactions
in stock trading has several real-
world applications. Here are a few
examples:

1. Stock Trading: Traders and


investors can utilize this
algorithm to optimize their stock
trading strategies. By identifying

529
Advanced level

• the maximum profit that can be


achieved with two transactions,
they can make informed decisions on
when to buy and sell stocks for
maximum gains.
2. Financial Analysis: Financial
analysts can use this algorithm to
analyze historical stock price data
and determine the maximum potential
profit that could have been made
with at most two transactions. This
analysis can provide insights into
the performance of stocks and help
in making investment
recommendations.
3. Algorithmic Trading: Algorithmic
trading systems often rely on
efficient algorithms to analyze
market data and make trading
decisions. This algorithm can be
incorporated into such systems to
optimize trading strategies and
maximize profits within the
constraints of at most two
transactions.

530
Advanced level

4. Portfolio Management: Fund managers


and portfolio analysts can utilize
this algorithm to optimize their
portfolio holdings. By considering
the potential profits from multiple
transactions, they can adjust their
portfolio allocations and make
decisions that align with their
investment objectives.

Overall, the algorithm for finding the


maximum profit with at most two
transactions in stock trading has
practical applications in various
financial domains, assisting traders,
analysts, and investors in making
informed decisions and maximizing
their returns.

531
Advanced level

DSA Challenge 31

Word Ladder:
Given two words, 'beginWord' and
'endWord', and a dictionary
'wordList', return the number of
words in the shortest transformation
sequence from ‘beginWord* to
'endWord*, or *0' if no such sequence
exists.

Example 1:

Input: beginWord = "hit", endWord =


cog", wordList =
[Ihot","dot","dog","lot","log","cog"]
Output: 5
Explanation: One shortest
transformation sequence is "hit" ->
"hot" -> "dot" -> "dog" -> cog", which
is 5 words long.

533
Advanced level

Example 2:

Input: beginWord = "hit", endWord =


"cog", wordlist =
["hot","dot","dog","lot","log"]
Output: 0
Explanation: The endWord "cog" is not
in wordList, therefore there is no
valid transformation sequence.

Our logic:
Here's a JavaScript program that
solves the problem using a breadth-
first search algorithm:

function ladderLength(beginWord,
endWord, wordList) {
const wordSet = new Set(wordList);
if (!wordSet.has(endWord)) {

return 0;

534
Advanced level

const queue = [[beginWord, 1]]; //


Start with the beginWord and its
transformation length
const visited = new Set();
visited.add(beginWord);
while (queue.length > 0) {
const [word, length] =
queue.shift();
for (let i = 0; i < word.length;

i++) {
const wordArray =
word.split('');
for (let j = 97; j <= 122; j++)

{
const char =
String.fromCharCode(j);

535
Advanced level

wordArray[i] = char;
const newWord =
wordArray.join('’ );
if (newWord === endWord) {
return length + 1 ; //
Transformation sequence found

}
if (wordSet.has(newWord) && !
visited.has(newWord)) {
queue.push([newWord, length

+ U);
visited.add(newWord);

536
Advanced level

return 0; // No transformation
sequence found

To use this program, you can call the


'ladderLength' function with the
'beginWord', 'endWord', and 'wordList'
as arguments. It will return the
number of words in the shortest
transformation sequence from
'beginWord' to 'endWord', or '0' if no
such sequence exists.

Note: This program assumes that all


words in the 'wordList' and
'beginWord' are lowercase alphabetical
strings of the same length.

The algorithm works by performing a


breadth-first search starting from the
'beginWord'. It explores all possible
transformations of the 'beginWord' by
changing one character at a time and

537
Advanced level

checks if the transformed word exists


in the 'wordList'. If it does, the
transformed word is added to the queue
for further exploration. The process
continues until either the 'endWord'
is found or the queue becomes empty.

Importance of this Algorithm in Real-


world Applications:
The Word Ladder algorithm has several
real-world applications, especially in
natural language processing and
computational linguistics. Here are a
few examples:

1 . Word Similarity: The Word Ladder


algorithm can be used to measure
the similarity or distance between
two words based on the number of
transformations required to convert
one word into another. This can be
helpful in applications such as
spell-checking, autocomplete

538
Advanced level

• suggestions, and word


recommendation systems.
2. Word Sense Disambiguation: In
natural language processing,
determining the correct sense or
meaning of a word can be
challenging. The Word Ladder
algorithm can be used to find a
path of related words that connects
the ambiguous word to words with
known meanings, providing insights
into possible interpretations.
3. Language Translation: When
translating text between languages,
finding an equivalent word or
phrase can be challenging. The Word
Ladder algorithm can be applied to
identify a sequence of related
words that bridge the gap between
languages, aiding in the
translation process.
4. Language Learning: The Word Ladder
algorithm can be used in
educational applications to help
learners expand their vocabulary
and understand word relationships.

539
Advanced level

• By providing word transformation


exercises, learners can enhance
their language skills and grasp the
connections between words.
5. Text Analysis and Information
Retrieval: In text mining and
information retrieval tasks, the
Word Ladder algorithm can be
utilized to find paths between
words to improve text
categorization, document
clustering, and search algorithms.
It enables the identification of
related terms, enabling more
accurate analysis and retrieval of
relevant information.

In summary, the Word Ladder algorithm


is essential in natural language
processing and linguistics, aiding
word similarity measurement, word
sense disambiguation, language
translation, and text analysis. It
supports language-related tasks and
enhances language learning.

540
Advanced level

DSA Challenge 32

Longest Consecutive Sequence:


Given an unsorted array of integers
'nums', return the length of the
longest consecutive elements
sequence.

You must write an algorithm that runs


in '0(n)' time.

Example 1:

Input: nums = [100,4,200,1,3,2]


Output: 4
Explanation: The longest consecutive
elements sequence is [1, 2, 3, 4].
Therefore its length is 4.

Example 2:

Input: nums = 10,3,7,2,5,8,4,6,0,1]


Output: 9

542
Advanced level

Our loiic:
Here's a JavaScript program that
finds the length of the longest
consecutive elements sequence in an
unsorted array 'nums':

function longestConsecutive(nums) {
if (nums.length === 0) {
return 0;

}
let numSet = new Set(nums);
let longestStreak = 0;

for (let num of numSet) {


if (’numSet.has(num - 1)) {
let currentNum = num;
let currentstreak = 1 ;

while (numSet.has(currentNum +

1)) {
currentNum++;

543
Advanced level

currentStreak++;

}
longestStreak =
Math.max(longestStreak,
currentstreak);

}
}
return longestStreak;

// Example usage:
let nums = [100, 4, 200, 1, 3, 2];
console.log(longestConsecutive(nuns));
// Output: 4

544
Advanced level

The algorithm uses a set to


efficiently check for the presence of
numbers. It iterates through the array
and for each number, checks if it is
the start of a sequence (i.e., the
previous number doesn't exist in the
set). If it is, it extends the
sequence as long as consecutive
numbers exist in the set. The
algorithm keeps track of the longest
streak encountered and returns it as
the result.

The time complexity of the algorithm


is 0(n), where n is the size of the
input array.

Importance of this Algorithm in Real-


world Applications:
The Longest Consecutive Sequence
algorithm has several real-world
applications, including:

1. Data Analysis: It can be used to

545
Advanced level

• identify patterns and sequences


within a dataset, helping to gain
insights and make informed
decisions based on consecutive
elements.
2. Database Operations: The algorithm
can be employed in databases to
find and analyze consecutive
values, such as tracking customer
orders, timestamps, or sequential
IDs.
3. Financial Analysis: In finance, it
can be useful for detecting
consecutive trends or sequences in
stock prices, allowing investors to
make informed trading decisions.
4. Genomic Analysis: In
bioinformatics, the algorithm can
aid in identifying consecutive
sequences of DNA or protein
patterns, helping researchers study
genetic structures and
relationships.
5. Gaming and Puzzles: The algorithm
can be used to create challenging

546
Advanced level

• or games that involve finding the


longest consecutive sequence of
elements.

Overall, the Longest Consecutive


Sequence algorithm provides valuable
insights into sequential data and has
applications in various domains such
as data analysis, databases, finance,
bioinformatics, and gaming.

547
Advanced level

DSA Challenge 33

Surrounded Regions:
Given an 'm x n' matrix board
containing 'X' and 'O', capture all
regions that are 4-directionally
surrounded by 'X1.

A region is captured by flipping all


'0's into 'X's in that surrounded
region.

Example 1:

Input: board = [["X”]]


Output: [["X"]]

Example 2:

X X X X
X o o X
X X o X
X o X X

549
Advanced level

Example 2:

Input: board = [["X","X","X","X"],


['■X","0","0","X"], ["X", "X", "0", "X"],
[,'X",”0,’,"X,,,,,X"]]
Output:[["X","X","X”,"X"],
[ ii x i* ’* x11 11X11 11 X11 ] [" X" " X11 "X11 11X11 ]
["X',Mo","X","X"] 1
Explanation: Notice that an '0' should
not be flipped if:
- It is on the border, or
- It is adjacent to an '0* that should
not be flipped.
The bottom '0J is on the border, so it
is not flipped.
The other three 'O' form a surrounded
region, so they are flipped.

Our logic:
Here's a JavaScript program that
captures the surrounded regions in a
matrix:

550
Advanced level

function solve(board) {
if (board.length === 0) return;
const m = board.length;
const n = board[0].length;
// Define helper function to perform
depth-first search
function dfs(i, j) {
if(i<0||i>=m||j<0||j
>= n || board[i][j] !== '0') return;
board[i][j] = ; // Mark the '0'
as visited
// Explore the four directions
dfs(i - 1, j);
dfs(i + 1, j);
dfs(i, j - 1);
dfs(i, j + 1);

551
Advanced level

}
// Traverse the first and last
columns
for (let i = 0; i < m; i++) {
dfs(i, 0);
dfs(i, n - 1);

}
// Traverse the first and last rows
for (let j =1; j < n - 1; j++) {
dfs(0, j);

dfs(m - 1, j);

}
// Convert remaining 'O' to 'X' and
restore '#' back to 'O'

for (let i = 0; i < m; i++) {

552
Advanced level

for (let j = 0; j < n; j++) {


if (board[i][j] === 1 O' ) {
board[i][j] = ' X' ;

} else if (board[i][j] === '#')

{
boa rd[i] [ j] = 'O';

}
}
}

// Example usage:
const board = [
['X', 'X', 'X', 'X'],
['X', 'O', 'O', 'X'],
['X', 'X', 'O', 'X' ],

553
Advanced level

['X', 'O', 'X', 'X'

];
solve(board) ;

console.log(board);

This program uses a depth-first search


(DFS) approach to identify and capture
the surrounded regions in the given
matrix. It marks the visited cells as
'#' to differentiate them from 'O' and
performs a series of DFS traversals
starting from the boundary cells.
Finally, it converts the remaining 'O'
cells to 'X1 and restores the back
to 1 0 * .

Importance of the Surrounded Regions


Algorithm in Real-world Applications:
The Surrounded Regions algorithm has
several real-world applications in
areas such as image processing,
computer vision, and pattern

554
Advanced level

• recognition. Some specific use


cases include:

1. Image segmentation: The algorithm


can be used to identify and
separate regions of interest in an
image that are surrounded by a
specific boundary. This is useful
for tasks like object detection,
image analysis, and feature
extraction.
2. Pattern recognition: By capturing
surrounded regions, the algorithm
can help in identifying patterns or
shapes within a larger data set.
This can be valuable in various
domains, including medical imaging,
fingerprint recognition, and
document analysis.
3. Graph theory and network analysis:
The algorithm can be used to
identify connected components or
clusters within a network or graph
structure. This has applications in
social network analysis, network

555
Advanced level

• visualization, and graph-based


algorithms.

Overall, the Surrounded Regions


algorithm provides a powerful tool for
identifying and capturing specific
regions of interest, which can be
applied in a wide range of real-world
scenarios involving data analysis,
image processing, and pattern
recognition.

556
Advanced level

DSA Challenge 34

Palindrome Partitioning:
Given a string 's', partition s
such that every substring of the
partition is a palindrome.

Return the minimum cuts needed for a


palindrome partitioning of s.

Example 1:

Input: s = "aab
Output: 1
Explanation: The palindrome
partitioning ["aa","b"] could be
produced using 1 cut.

Example 2:

Input: s = "a
Output:0

558
Advanced level

Our logic:
Here's a JavaScript program that
solves the problem using dynamic
programming:

function minCut(s) {
const n = s.length;
const isPalindrome = Array.from({
length: n }, () =>
Array(n).fill(false));
const dp = Array(n).fill(n);

for (let i = 0; i < n; i++) {


for (let j = 0; j <= i; j++) {
if (s[i] === s[j] && (i - j <= 1
|| isPalindrome[j + 1][i - 1])) {
isPalindrome[j][i] = true;

if (j === 0) {
dp[i] = 0;

559
Advanced level

} else {
dp[i] = Math.min(dp[i], dp[j

- 1] + 1);

}
}
}
}

return dp[n - 1 ] ;

// Example usage:
const s = "aab";
const result = minCut(s);
console.log(result); // Output: 1

560
Advanced level

The dynamic programming approach


employed in this program efficiently
determines the minimum cuts required
for a palindrome partitioning of the
string 's'. By maintaining the
'isPalindrome' array to track
palindrome substrings and the 'dp'
array to store the minimum cuts, the
program achieves its objective.

The program iterates through each


character of 's', identifying
palindromes by checking substrings
from index 'j' to 'i'. Whenever a
palindrome is found, both the
'isPalindrome' and 'dp' arrays are
updated accordingly.

Ultimately, the program returns the


minimum cuts stored in 'dp[n - 1]',
where 'n' represents the length of the
string 's'.

The time complexity of the algorithm


is 0(nA2), where n is the length of

561
Advanced level

the input string s.

Importance of this Algorithm in Real-


world Applications:
The Palindrome Partitioning algorithm
plays a significant role in various
real-world applications, especially in
the field of natural language
processing and text analysis. Some of
its key importance includes:

1. Spell Checking: Palindrome


partitioning can be used to
identify potential errors in words
by analyzing their palindromic
substructures. It helps in
detecting and suggesting
corrections for misspelled words.
2. Word Segmentation: In languages
without explicit word delimiters,
such as Chinese or Thai, palindrome
partitioning can aid in word
segmentation. By identifying
palindromic substrings, it helps

562
Advanced level

• determine possible word boundaries


within a sentence.
3. DNA Analysis: Palindrome
partitioning is used in DNA
analysis to identify repeated
patterns or sequences within the
DNA strands. It helps in
understanding genetic structures,
gene mutations, and genetic
disorders.
4. Data Compression: Palindrome
partitioning algorithms are
employed in data compression
techniques, such as Burrows-Wheeler
Transform (BWT) and Lempel-Ziv-
Welch (LZW), to identify repeated
patterns in data and achieve higher
compression ratios.
5. Pattern Recognition: Palindrome
partitioning can be applied in
pattern recognition tasks, such as
image processing and speech
recognition, to identify symmetric
or recurring patterns in signals or
images.

563
Advanced level

Overall, the Palindrome Partitioning


algorithm has broad applications in
linguistic analysis, data precessing,
and pattern recognition, contributing
to improved language processing
systems, efficient data compression
techniques, and advancements in
genetic research.

564
Fi Edi Select Vie

|-Pl ”• HDSA.js X
Advanced level

DSA Challenge 35

Gas Station:
There are 'n' gas stations along a
circular route, where the amount of
gas at the 'ith' station is gas[i]‘.

You have a car with an unlimited gas


tank and it costs 'cost[i]' of gas to
travel from the ith' station to its
next '(i + 1)th‘ station. You begin
the journey with an empty tank at one
of the gas stations.

Given two integer arrays ‘gas' and


'cost', return the starting gas
station's index if you can travel
around the circuit once in the
clockwise direction, otherwise return
'-1'. If there exists a solution, it
is guaranteed to be unique

566
Advanced level

Example:

Input: gas = [1,2,3,4,5], cost =


[3,4,5,1,2]
Output: 3
Explanation: Start at station 3 (index
3) and fill up with 4 unit of gas. Your
tank =0+4=4
Travel to station 4. Your tank =4-1
+ 5 = 8
Travel to station 0. Your tank =8-2
+ 1=7
Travel to station 1. Your tank =7-3
+ 2 = 6
Travel to station 2. Your tank =6-4
+ 3 = 5
Travel to station 3. The cost is 5.
Your gas is just enough to travel back
to station 3.

Therefore, return 3 as the starting


index.

567
Advanced level

Our logic:
Here's a JavaScript program that
solves the problem:

function canCompleteCircuit(gas, cost)

{
const n = gas.length;
let totalGas = 0;
let currentGas = 0;
let startstation = 0;
for (let i = 0; i < n; i++) {
totalGas += gas[i] - cost[i];
currentGas += gas[i] - cost[i];

if (currentGas < 0) {
// Cannot reach the next
station, reset the start station
startstation = i + 1 ;

currentGas = 0;

568
Advanced level

}
}

// If the total gas is negative,


it's impossible to complete the
circuit
return totalGas >= 0 ? startstation

: -i;
}

The program iterates through the gas


stations in a clockwise direction,
keeping track of the total gas and
the current gas. If the current gas
becomes negative at any station, it
means we cannot reach the next
station, so we reset the start
station to the next station and start
with an empty tank.

569
Advanced level

Finally, we check if the total gas is


non-negative. If it is, we return the
start station index; otherwise, we
return -1 to indicate that it is
impossible to complete the circuit.

The time complexity of this algorithm


is 0(n), where n is the number of gas
stations.

Importance of the Gas Station


Algorithm in Real-world Applications:
The gas station algorithm has
practical significance in various
real-world applications, especially in
the transportation and logistics
domain. Here are some examples:
1. Route Planning: The algorithm can
be used to determine the optimal
starting point for a vehicle to
ensure it can travel through a
series of gas stations without
running out of fuel. This is

570
Advanced level

• crucial for efficient route


planning and avoiding unnecessary
delays.
2. Fleet Management: For businesses
that operate a fleet of vehicles,
such as delivery services or
transportation companies, the
algorithm can help optimize fuel
consumption and reduce costs by
identifying the most suitable gas
stations for refueling along the
planned routes.
3. Fuel Efficiency Analysis: By
applying the algorithm to
historical data of gas station
locations and fuel consumption,
insights can be gained into the
fuel efficiency of vehicles,
identifying patterns and optimizing
fuel management strategies.
4. Emergency Preparedness: During
emergency situations, such as
natural disasters or crises, where
fuel availability might be limited,
the algorithm can assist in

571
Advanced level

• in determining the critical gas


stations to ensure essential
services like emergency response
vehicles or evacuation
transportation can operate
effectively.

Overall, the gas station algorithm


plays a valuable role in improving
fuel management, optimizing routes,
and enhancing transportation
logistics, leading to cost savings,
improved efficiency, and better
resource utilization in real-world
applications.

572
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 36

Candy:
There are 'n' children standing in a
line. Each child is assigned a rating
value given in the integer array
'ratings'.

You are giving candies to these


children subjected to the following
requirements:

• Each child must have at least one


candy.
• Children with a higher rating get
more candies than their neighbors.

Return the minimum number of candies


you need to have to distribute the
candies to the children.

574
Advanced level

Example 1:

Input: ratings = [1,0,2]


Output: 5
Explanation: You can allocate to the
first, second and third child with 2,
1, 2 candies respectively.

Example 2:

Input: ratings = [1,2,2]


Output: 4
Explanation: You can allocate to the
first, second and third child with 1,
2, 1 candies respectively.
The third child gets 1 candy because it
satisfies the above two conditions.

575
Advanced level

Our logic:
Here's a JavaScript program that
solves the problem:

function candy(ratings) {
const n = ratings.length;
const candies = new
Array(n).fill(1); // Initialize
candies array with minimum values
// Traverse from left to right, and
assign more candies to the right
neighbor if their rating is higher
for (let i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;

}
}

576
Advanced level

// Traverse from right to left, and


update the candy count based on the
neighbor's rating
for (let i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] =
Math.max(candies[i], candies[i + 1] +

i);
}
}
// Calculate the total number of
candies
let totalCandies = 0;
for (let i = 0; i < n; i++) {
totalCandies += candies[i];

577
Advanced level

}
return totalCandies;

// Example usage:
const ratings = [1, 2, 3, 2, 1];
console.log(candy(ratings)) ; //
Output: 9

This program uses a two-pass approach


to distribute candies to children
based on their ratings. It assigns the
minimum number of candies initially
and then adjusts the counts to satisfy
the rating requirements.

The time complexity of this algorithm


is 0(n), where n is the number of
children (length of the ratings
array) .

578
Advanced level

Importance of the Candy Algorithm in


Real-world Applications:
The Candy algorithm, which determines
the minimum number of candies needed
to distribute among children based on
their ratings, has several real-world
applications:

1. Education and Grading Systems: The


algorithm can be used in
educational systems to assign
grades or rewards to students based
on their performance. Students with
higher ratings or scores can be
given more candies or rewards,
ensuring a fair and consistent
grading system.
2. Recommendation Systems: In
recommendation systems, the
algorithm can be applied to assign
personalized ratings or scores to
items or products based on user
preferences. This helps in
determining the ranking or priority

579
Advanced level

• of items to be recommended to
users.
3. Resource Allocation: The algorithm
can be used in resource allocation
problems, where resources need to
be distributed among different
entities based on their
requirements or priorities. For
example, in project management, it
can help allocate resources to
tasks or team members based on
their complexity or importance.
4. Fair Distribution: The algorithm
can be applied in situations where
resources or rewards need to be
distributed among individuals in a
fair and equitable manner. By
considering the ratings or
preferences of individuals, it
ensures that the distribution is
proportional to their merit or
contribution.

Overall, the Candy algorithm provides

580
Advanced level

a flexible and efficient approach for


assigning values or rewards based on
ratings or preferences, making it
applicable in various domains where
fair and optimized distribution is
required.

581
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 37

Word Break:
Given a string s' and a dictionary
of strings 'wordDict', add spaces in
s to construct a sentence where each
word is a valid dictionary word.
Return all such possible sentences in
any order.

Note that the same word in the


dictionary may be reused multiple
times in the segmentation.

Example 1:

Input: s = "catsanddog", wordDict =


["cat","cats","and","sand","dog"]
Output: ["cats and dog","cat sand
dog" ]

583
Advanced level

Example 2:

Input: s = "catsandog", wordDict =


["cats", "dog", "sand", "and", "cat"]
Output: []

Example 3:

Input: s = "pineapplepenapple",
wordDict =
["apple","pen","applepen","pine","pinea
pple"]
Output: s = "pineapplepenapple ,
wordDict =
["apple", ' pen", 'applepen , pine", pinea
pple”]
Explanation: Note that you are allowed
to reuse a dictionary word.

584
Advanced level

Our logic:
Here's a JavaScript program that
solves the given problem:

function wordBreak(s, wordDict) {


const wordSet = new Set(wordDict);
const memo = new Map();
function backtrack(s) {
if (memo.has(s)) {
return memo.get(s);

}
const result = [ ] ;
if (wordSet.has(s)) {
result.push(s);

}
for (let i = 1; i < s.length; i++)

585
Advanced level

const prefix = s.substring(0, i);


if (wordSet.has(prefix)) {
const suffix = s.substring(i) ;
const suffixBreaks =
backtrack(suffix);
for (const suffixBreak of
suffixBreaks) {
result.push(prefix + ' ' +
suffixBreak);

}
}
}
memo.set(s, result);
return result;

586
Advanced level

return backtrack(s);

}
// Example usage:
const s = "catsanddog ';
const wordDict = ["cat", "cats",
"and", "sand”, "dog”];
const sentences = wordBreak(s,
wordDict);
console.log(sentences);

This program uses backtracking with


memoization to find all possible
sentences by adding spaces in the
input string 's' based on the provided
word dictionary 'wordDict'. It
recursively explores all possible
prefixes and suffixes of 's', checking
if the prefix is a valid word in the
dictionary. If it is, the program
recursively calls itself on the suffix

587
Advanced level

and combines the prefix with all


possible breaks of the suffix.

The time complexity of this algorithm


depends on the number of possible
sentences and the length of each
sentence. In the worst case, where
there are many valid combinations of
words, the time complexity can be
exponential. However, the use of
memoization helps to avoid redundant
computations and improve performance
in practice.

Importance of the Word Break Algorithm


in Real-world Applications:
The Word Break algorithm has several
important applications in real-world
scenarios:

1. Natural Language Processing: The


ability to segment sentences into
individual words is crucial in
various natural language processing

588
Advanced level

• tasks, such as text classification,


sentiment analysis, machine
translation, and speech
recognition. The Word Break
algorithm can be used as a
preprocessing step to tokenize
sentences and extract meaningful
units for further analysis.
2. Spell Checking and Auto-Completion:
Word segmentation is essential for
accurate spell checking and auto­
completion features in applications
like word processors, search
engines, and messaging platforms.
The Word Break algorithm can be
employed to split user input into
valid words, enabling intelligent
suggestions and error detection.
3. Information Retrieval: In
information retrieval systems,
breaking down search queries into
individual keywords is fundamental
for retrieving relevant documents
or web pages. The Word Break
algorithm helps parse and tokenize
user queries, enhancing the

589
Advanced level

• accuracy and efficiency of search


algorithms.
4. Text Analysis and Text Mining:
Analyzing large volumes of text
data often involves extracting
meaningful units like words or
phrases. The Word Break algorithm
is a fundamental step in text
analysis and mining tasks,
including topic modeling, sentiment
analysis, named entity recognition,
and keyword extraction.
5. Language Processing and Grammar
Checking: Language learning
applications, grammar checkers, and
automated writing assistants
heavily rely on word segmentation
to identify grammatical errors,
suggest corrections, and improve
overall writing quality. The Word
Break algorithm plays a crucial
role in these language processing
tasks.

590
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 38

Max Points on a Line:


Given an array of ’points' where
'points[i] = [xi, yi]' represents a
point on the X-Y plane, return the
maximum number of points that lie on
the same straight line.

Example 1:

1---------- :------------ 1------------ 1-------------- 1------------ r


0 1 2 3 4 5

Input: points = [ [ 1,1 ],[2,2],[3,3]]


Output: 3

592
Advanced level

Example 2:

2-

i----------- 1-------------- 1-------------- 1-------------- 1--------- *


0 1 2 3 4 S

Input: points = [ [ 1,1],[3,2],[5,3],


[4,1],[2,3],[1,4]]
Output: 4

593
Advanced level

Our logic:
To solve the problem of finding the
maximum number of points that lie on
the same straight line, you can use
the following JavaScript program:

function maxPoints(points) {
if (points.length <= 2) {
return points.length;

}
let maxCount = 0;
for (let i = 0; i < points.length;

i++) {
const slopes = new Map();
let samePointCount = 1 ;
let currMax = 0;
for (let j = i + 1; j <
points.length; j++) {

594
Advanced level

const xDiff = points[j][0] -

points[i][0];
const yDiff = points!j][1] -
points[i][1];
if (xDiff === 0 && yDiff === 0)

{
samePointCount++;
continue;

let slope;
if (xDiff === 0) {
slope = Infinity;
} else {
slope = yDiff / xDiff;

595
Advanced level

slopes.set(slope, (slopes.get(slope)

Il 0) + i);
currMax = Math.max(currMax,
slopes.get(slope));

}
maxCount = Math.max(maxCount,
currMax + samePointCount);

}
return maxCount;

The program utilizes a nested loop to


compare each point with all other
points. It calculates the slope
between two points and keeps track of
the number of points that have the
same slope. The maximum count of
points on the same line is updated for
each iteration.

596
Advanced level

The time complexity of this algorithm


is 0(nA2), where n is the number of
points in the input array. This is
because we compare each point with all
other points.

Importance of this Algorithm in Real-


world Applications:
The Max Points on a Line algorithm has
several real-world applications,
particularly in computer graphics,
image processing, and geometric
analysis. Some of the areas where this
algorithm can be useful include:

1. Line Detection: The algorithm can


be used to detect and analyze
straight lines in images or videos.
It helps in tasks like edge
detection, object recognition, and
shape analysis.

597
Advanced level

2. Pattern Recognition: By finding the


maximum number of points on a line,
the algorithm can aid in
recognizing patterns and structures
in datasets. It can be applied in
various domains such as data
analysis, machine learning, and
computer vision.
3. Computer Graphics: In computer
graphics applications, the
algorithm can be used for rendering
and modeling geometric shapes. It
helps in efficiently determining
the lines and curves that define
the shape.
4. Route Planning: The algorithm can
assist in finding the optimal
routes or paths in navigation
systems. By identifying lines that
pass through multiple points, it
can optimize the routing process
and improve efficiency.
5. Geographic Analysis: In geographic
information systems (GIS), the
algorithm can be used to analyze

598
Advanced level

• spatial data. It helps in


identifying clusters or patterns of
points that lie on the same line,
which can provide valuable insights
for urban planning, transportation
analysis, and other geographical
studies.

Overall, the Max Points on a Line


algorithm plays a significant role in
various applications that involve
analyzing and processing geometric
data, detecting patterns, and
optimizing routes. It enables
efficient computations and helps in
extracting valuable information from
datasets.

599
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 39

Maximum Gap:
Given an integer array 'nums', return
the maximum difference between two
successive elements in its sorted
form. If the array contains less than
two elements, return '0'.

You must write an algorithm that runs


in linear time and uses linear extra
space.

Example 1:

Input: nums = [3,6,9,1]


Output: 3
Explanation: The sorted form of the
array is [1,3,6,9], either (3,6) or
(6,9) has the maximum difference 3.

Example 2:

Input: points = [ [1,1],[3,2],[5,3],


[4,1],[2,3],[1,4]]
Output: 4

601
Advanced level

Our logic:
Here's a JavaScript program that
solves the problem using the bucket
sort algorithm:

function maximumDifference(nums) {
const n = nums.length;
if (n < 2) return 0;
let minVal = Infinity;
let maxVal = -Infinity;
// Find the minimum and maximum
values in the array
for (let num of nums) {
minVal = Math.min(minVal, num);
maxVal = Math.max(maxVal, num);

}
// Calculate the interval size
const interval = Math.ceil((maxVal -

602
Advanced level

minVal) / (n - 1)) ;
// Create n-1 buckets to store the
minimum and maximum values
const minBucket = new Array(n -
1).fill(Infinity);
const maxBucket = new Array(n -
1).fill(-Infinity);
// Distribute elements into the
buckets
for (let num of nums) {
if (num === minVal || num ===
maxVal) continue;
const index = Math.floor((num -
minVal) / interval);
minBucket[index] =
Math.min(minBucket[index], num);

603
Advanced level

maxBucket[index] =
Math.max(maxBucket[index], num) ;

// Calculate the maximum difference


between successive non-empty buckets
let prevMax = minVal;
let maxDiff = 0;

for (let i = 0; i < n - 1; i++) {


if (minBucket[i] === Infinity &&
maxBucket[i] === -Infinity) continue;
maxDiff = Math.max(maxDiff,
minBucket[i] - prevMax);
prevMax = maxBucket[i] ;

604
Advanced level

// Calculate the maximum difference


with the last element
maxDiff - Math.max(maxDiff, maxVal -
prevMax);
return maxDiff;

}
// Example usage:
const nums = [3, 6, 9, 1];
console.log(maximumDifference(nums));
// Output: 3

The algorithm utilizes the bucket sort


technique to find the maximum
difference between two successive
elements in a given integer array. It
first finds the minimum and maximum
values in the array. Then it creates
n-1 buckets to distribute the elements
based on their values.

605
Advanced level

Next, it calculates the maximum


difference between successive non­
empty buckets, considering the minimum
and maximum values. Finally, it
returns the maximum difference as the
result. The algorithm runs in linear
time and uses linear extra space,
ensuring efficient performance.

Importance of the Maximum Gap


Algorithm in Real-world Applications:
The Maximum Gap algorithm is important
in various real-world applications
that involve data analysis,
optimization, and resource allocation.
Some potential applications include:

1. Financial Analysis: The algorithm


can be used to analyze stock market
data and identify the maximum gap
between consecutive stock prices.
This information can provide
insights into market volatility and

606
Advanced level

• potential investment opportunities.


2. Data Mining: In data mining tasks,
the algorithm can help identify the
maximum gap between consecutive
data points, which can be useful in
anomaly detection and pattern
recognition.
3. Resource Allocation: The algorithm
can be applied in resource
allocation problems where
maximizing the gap between resource
usages is desired. For example, in
scheduling tasks or allocating
bandwidth in communication
networks, maximizing the gap
between resource utilization can
lead to better efficiency and
performance.
4. Time Series Analysis: The algorithm
can be used to analyze time series
data, such as temperature or energy
consumption measurements, to
identify the maximum gap between
consecutive data points. This can
help detect significant changes or
anomalies in the time series.

607
Fi Edi Select Vie

i-n — n dsa.js x
Advanced level

DSA Challenge 40

Repeated DNA Sequences:


The DNA sequence is composed of a
series of nucleotides abbreviated as
’A1( ' C ‘’G', and ’T'.
• For example, "ACGAATTCCG" is a DNA
sequence.
When studying DNA, it is useful to
identify repeated sequences within
the DNA.

Given a string 's' that represents a


DNA sequence, return all the '10-
letter-long' sequences (substrings)
that occur more than once in a DNA
molecule. You may return the answer
in any order.

Example 1:

Input: s = 11AAAAAAAAAAAAA"
Output: ["AAAAAAAAAA"]

609
Advanced level

Example 2:

Input: s =
"AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
Output: ["AAAAACCCCC","CCCCCAAAAA"]

Our logic:
Here's a JavaScript program that
returns all the 10-letter-long
sequences that occur more than once
in a given DNA sequence:

function findRepeatedSequences(s) {
const sequenceMap = new Map();
const result = [ ] ;

for (let i = 0; i <= s.length - 10;

i++) {
const sequence = s.substring(i, i

+ 10);

610
Advanced level

sequenceMap.set(sequence,
(sequenceMap.get(sequence) || 0) + 1);

if (sequenceMap.get(sequence) ===

2) {
result.push(sequence);

}
}

return result;

// Example usage:
const dnaSequence = "ACGAATTCCG";
const repeatedSequences =
findRepeatedSequences(dnaSequence);
console.log(repeatedSequences);

611
Advanced level

This program uses a sliding window


approach to iterate through the DNA
sequence and find all 10-letter-long
sequences. It uses a 'Map' to keep
track of the count of each sequence
encountered. If a sequence occurs more
than once, it is added to the 'result'
array.

The program has a time complexity of


0(n), where n is the length of the DNA
sequence. It efficiently finds and
returns all the repeated sequences in
the DNA molecule.

Note: This program assumes that the


input DNA sequence is at least 10
characters long.

612
Advanced level

Importance of this Algorithm in Real-


world Applications:
The Repeated DNA Sequences algorithm
is important in various real-world
applications involving DNA analysis
and genetic research. Here are a few
examples:

1 . Genomic Studies: Identifying


repeated DNA sequences is crucial
in understanding the structure and
function of genomes. By detecting
recurring patterns, scientists can
gain insights into gene regulation,
chromosomal abnormalities, and
evolutionary relationships.
2. Disease Research: Certain genetic
diseases are associated with
specific repeated DNA sequences.
Detecting these sequences can aid
in diagnosing genetic disorders,
predicting disease susceptibility,
and developing targeted therapies.
3. Forensic Analysis: Repeated DNA
sequences, such as short tandem

613
Advanced level

• repeats (STRs), are commonly used


in forensic DNA profiling. By
identifying and comparing these
sequences, forensic scientists can
establish links between individuals
and crime scenes, assisting in
criminal investigations.
4. Evolutionary Studies: Repeated DNA
sequences play a role in
evolutionary processes, including
genetic variation, speciation, and
phylogenetic analysis. By examining
the frequency and distribution of
repeated sequences, researchers can
uncover evolutionary patterns and
relationships among species.
5. Bioinformatics: Repeated DNA
sequences are important in
computational biology and
bioinformatics. They are used as
markers for genome assembly,
sequence alignment, and annotation
of genetic elements. Efficient
algorithms for finding repeated
sequences contribute to the
development of computational tools

614
Advanced level

• and databases used in genomics


research.

Overall, the Repeated DNA Sequences


algorithm contributes to our
understanding of genetics, disease
mechanisms, forensic analysis, and
evolutionary biology. It enables
researchers and scientists to uncover
valuable information encoded in DNA
sequences and apply it to various
real-world applications.

615
Advanced level

DSA Challenge 41

House Robber:
You are a professional robber
planning to rob houses along a
street. Each house has a certain
amount of money stashed, the only
constraint stopping you from robbing
each of them is that adjacent houses
have security systems connected and
it will automatically contact the
police if two adjacent houses were
broken into on the same night.

Given an integer array 'nums'


representing the amount of money of
each house, return the maximum amount
of money you can rob tonight without
alerting the police.

Example 1:

Input: nums = [1,2,3,1]


Output: 4
Explanation: Rob house 1 (money = 1)
and then rob house 3 (money = 3).
Total amount you can rob =1 +3=4.

617
Advanced level

Example 2:

Input: nums = [2,7,9,3,1]


Output: 12
Explanation: Rob house 1 (money = 2),
rob house 3 (money = 9) and rob house 5
(money = 1).
Total amount you can rob =2+9+1 =
12.

Our logic:
To solve the problem of robbing
houses without alerting the police
and maximizing the stolen money, you
can use a dynamic programming
approach. Here s a JavaScript program
that implements the solution:

function rob(nums) {
const n = nums.length;
if (n === 0) return 0;

618
Advanced level

if (n === 1) return nums[0];

// Create an array to store the


maximum stolen money at each house
const dp = [];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);

for (let i = 2; i < n; i++) {


// At each house, choose the
maximum between robbing the current
house plus the maximum stolen money

from two houses ago,


// or skipping the current house
and taking the maximum stolen money
from the previous house

619
Advanced level

dp[i] = Math.max(nums[i] + dp[i-2],


dp[i-1 ]);

}
return dp[n-1 ];

The program iterates through the array


of house money and uses dynamic
programming to calculate the maximum
stolen money at each house. At each
house, the program chooses the maximum
between robbing the current house plus
the maximum stolen money from two
houses ago, or skipping the current
house and taking the maximum stolen
money from the previous house.

Finally, the program returns the


maximum stolen money from the last
house.

The time complexity of this algorithm

620
Advanced level

is 0(n), where n is the number of


houses, as it iterates through the
array once. The space complexity is
also 0(n) as we use an array to store
the maximum stolen money at each
house.

Importance of the House Robber


Algorithm in Real-world Applications:
The House Robber algorithm has real-
world applications in various fields,
including finance, security, and
resource allocation. Here are a few
examples of its importance:

1. Home Security: The algorithm can be


used to analyze the vulnerability
of residential areas and devise
security measures to prevent
consecutive burglaries. It helps in
understanding the impact of house-
to-house connections on the
security system's effectiveness.

621
Advanced level

2. Financial Planning: The algorithm


provides insights into optimizing
investment decisions and asset
allocation. By considering adjacent
relationships, it helps individuals
and organizations determine the
best strategy to maximize returns
while managing risks.
3. Resource Allocation: The algorithm
can be applied to scenarios where
resources need to be allocated
efficiently. For instance, it can
assist in determining the optimal
distribution of limited resources
among various projects or
departments to achieve maximum
overall benefit.
4. Risk Management: Understanding the
algorithm helps in identifying and
mitigating risks associated with
consecutive or correlated events.
It aids in designing risk
management strategies that account
for dependencies and minimize
potential losses.

622
Advanced level

DSA Challenge 42

Dungeon Game:
The demons had captured the princess
and imprisoned her in the bottom­
right corner of a dungeon'. The
'dungeon' consists of 'm x n' rooms
laid out in a 2D grid. Our valiant
knight was initially positioned in
the top-left room and must fight his
way through 'dungeon' to rescue the
princess.

The knight has an initial health


point represented by a positive
integer. If at any point his health
point drops to '0' or below, he dies
immediately.

Some of the rooms are guarded by


demons (represented by negative
integers), so the knight loses health
upon entering these rooms; other
rooms are either empty (represented
as 0) or contain magic orbs that
increase the knight's health
(represented by positive integers).

624
Advanced level

To reach the princess as quickly as


possible, the knight decides to move
only rightward or downward in each
step.

Write a program that will return the


knight's minimum initial health so
that he can rescue the princess.

Note that any room can contain


threats or power-ups, even the first
room the knight enters and the
bottom-right room where the princess
is imprisoned.

625
Advanced level

Input: dungeon = [[-2,-3,3],


[-5,-10,1],[10,30,-5]]
Output: 7
Explanation: The initial health of the
knight must be at least 7 if he
follows the optimal path: RIGHT->
RIGHT -> DOWN -> DOWN.

Example 2:

Input: dungeon = [[0]]


Output: 1

Our logic:
Here's a JavaScript program that
solves the problem:

function calculateMinimumHP(dungeon) {
const m = dungeon.length;

626
Advanced level

const n = dungeon[0].length;
const dp = new Array(m +
1).f111(0).map(() => new Array(n +
1).fill(Infinity));

dp[m][n - 1] = 1 ;
dp[m - 1][n] = 1 ;

for (let i - m - 1; i >- 0; i--) {


for (let j = n - 1; j >= 0; j--) {
const minHealth = Math.min(dp[i

+ dp[i][j + 1]) - dungeon[i]

[j];
dp[i][j ] = Math.max(1,
minHealth);

}
}

627
Advanced level

return dp[0][0];

}
// Example usage:
const dungeon = [
[-2, -3, 3],

[-5, -10, 1],


[10, 30, -5]

I;
const minlnitialHealth =
calculateMinimumHP(dungeon);
console.log(minlnitialHealth);

The program uses a dynamic programming


approach to calculate the knight's
minimum initial health. It creates a
'dp' array to store the minimum health
required at each room. Starting from
the bottom-right corner, it iterates
through the aungeon grid and

628
Advanced level

calculates the minimum health needed


to reach the princess from each room.
Finally, it returns the minimum health
required at the top-left corner, which
represents the knight's minimum
initial health.

The time complexity of this algorithm


is 0(m * n), where m and n are the
dimensions of the dungeon grid.

Importance of the Dungeon Game


Algorithm in Real-world Applications:
The Dungeon Game algorithm has several
real-world applications in game
development and optimization problems.
Here are some of its importance:

1. Game Development: In game


development, the algorithm can be
used to calculate the minimum
health or resources required for a
player or character to complete a
level or defeat an enemy. It helps

629
Advanced level

• in designing challenging and


balanced gameplay experiences.
2. Resource Management: The algorithm
can be applied to scenarios where
resources need to be allocated
efficiently. For example, in
resource planning for a project, it
can determine the minimum amount of
resources required to complete
tasks while considering
dependencies and constraints.
3. Optimization Problems: The
algorithm can be used as a building
block in solving optimization
problems, such as finding the
minimum or maximum values under
certain constraints. It provides
insights into the decision-making
process and helps in finding
optimal solutions in various
domains.

630
Glossary

• Algorithm: A step-by-step procedure


or method for solving a problem or
performing a specific task.

• Data Structure: A way of organizing


and storing data in a computer to
facilitate efficient operations and
data manipulation.

• Array: A data structure that stores


a fixed-size sequence of elements
of the same type, accessible using
an index.

• Linked List: A data structure


consisting of a sequence of nodes,
where each node contains data and a
reference to the next node.

• Stack: A data structure that


follows the Last-In-First-Out
(LIFO) principle, allowing
operations such as push (add) and
pop (remove).

632
Glossary

* Queue: A data structure that


follows the First-In-First-Out
(FIFO) principle, allowing
operations such as enqueue (add)
and dequeue (remove).

• Binary Tree: A hierarchical data


structure composed of nodes, where
each node has at most two children.

• Hash Table: A data structure that


maps keys to values using a hash
function for efficient retrieval
and storage.

* Sorting: The process of arranging


elements in a specific order, such
as ascending or descending.

* Searching: The process of finding a


specific element or value in a
collection of data.

633
Glossary

• Graph: A data structure composed of


a set of vertices (nodes) and edges
that connect them.

• Dynamic Programming: A technique


for solving complex problems by
breaking them into smaller
overlapping subproblems.

• Recursion: The process of solving a


problem by breaking it down into
smaller instances of the same
problem.

• Big 0 Notation: A notation used to


describe the performance and
scalability of an algorithm in
terms of its input size.

• Complexity Analysis: The process of


evaluating the efficiency and
resource usage of an algorithm or
data structure.

634
Glossary

• Greedy Algorithm: An algorithmic


paradigm that makes locally optimal
choices at each step to achieve a
global optimum.

• Divide and Conquer: An algorithmic


technique that breaks a problem
into smaller subproblems, solves
them independently, and combines
their solutions.

• Dynamic Data Structure: A data


structure that can grow or shrink
in size during program execution.

• Heaps: A specialized tree-based


data structure that satisfies the
heap property, commonly used in
priority queues.

• Graph Traversal: The process of


visiting all nodes in a graph,
typically done using techniques
such as breadth-first search (BFS)
or depth-first search (DFS).

635
Notes

636

You might also like