This Algorithms with Javascript tutorial is designed to help you understand and implement fundamental algorithms using the versatile JavaScript programming language. Whether you are a beginner in programming or looking to enhance your algorithmic skills, this guide will walk you through essential concepts, algorithms, and their implementations.
The algorithm is defined as a process or set of well-defined instructions that are typically used to solve a particular set of problems or perform a specific type of calculation. To explain it in simpler terms, it is a set of operations performed step-by-step to execute a task.
How to Start learning Algorithms in JavaScript?
Follow the below mentioned points for how to learn Algorithms in JavaScript:
- Know the fundamentals of Algorithms inside and out.
- Know exactly what happens in an algorithm.
- Understand the examples and grasp the algorithm’s steps.
- Clearly know complexity analysis of algorithms in best, average and worst case.
- Solve problems based on the algorithms on your own.
Must know Algorithms in DSA using JavaScript Tutorial
The algorithms are divided into several categories, as shown below:
1. Searching Algorithms in Javascript:
Searching algorithms are used to find a specific element in an array, string, linked list, or some other data structure.
The most common search algorithms are:
In the Linear searching algorithm, we check for the element iteratively one by one from start to end to the other.
Linear search
How Linear Search Works?
- Step 1: First, read the array’s search element (Target element).
- Step 2: Set an integer i = 0 and repeat steps 3 to 4 until i reaches the array’s end.
- Step 3: Match the key with arr[i].
- Step 4: If the key matches, return the index. Otherwise, increment i by 1.
Below is the implementation of Linear Search in javascript:
Javascript
function linearSearch(arr, n, x) {
let i;
for (i = 0; i < n; i++)
if (arr[i] == x)
return i;
return -1;
}
function searchInArr(arr, n, x) {
let result = linearSearch(arr, n, x);
if (result == -1)
console.log( "Element is not present in array" );
else
console.log( "Element is present at index " + result);
}
let arr = [10, 30, 50, 60, 70];
let n = arr.length;
let x1 = 50;
searchInArr(arr, n, x1);
let x2 = 5;
searchInArr(arr, n, x2);
|
Output
Element is present at index 2
Element is not present in array
Complexity Analysis of Linear Search Algorithm
- Time Complexity of Linear Search: O(N), where N is the number of elements in the Array
- Auxiliary Space Complexity of Linear Search: O(1)
- In this type of searching algorithm, we break the data structure into two equal parts and try to decide in which half we need to find the element target element.
Binary Search
How does Binary Search work?
To understand the working of binary search, consider the following illustration:
- First Step:
- Initially, the search space is from 0 to 9.
- Let’s denote the boundary by L and H where L = 0 and H = 9 initially.
- Now mid of this search space is M = 4.
- So compare the target with arr[M].
- Second Step:
- As arr[4] is less than the target, switch the search space to the right of 16, i.e., [5, 9].
- Now L = 5, H = 9, and M becomes 7.
- Compare target with arr[M].
- Third Step:
- arr[7] is greater than the target.
- Shift the search space to the left of M, i.e., [5, 6].
- So, now L = 5, H = 6 and M = 6.
- Compare arr[M] with the target.
- Here arr[M] and target are the same.
- So, we have found the target.
Here is the implementation of the above approach:
Javascript
let iterativeFunction = function (arr, x) {
let start=0, end=arr.length-1;
while (start<=end){
let mid=Math.floor((start + end)/2);
if (arr[mid]===x) return true ;
else if (arr[mid] < x)
start = mid + 1;
else
end = mid - 1;
}
return false ;
}
let arr = [1, 3, 5, 7, 8, 9];
let x = 5;
if (iterativeFunction(arr, x, 0, arr.length-1))
console.log( "Element found!" );
else console.log( "Element not found!" );
x = 6;
if (iterativeFunction(arr, x, 0, arr.length-1))
console.log( "Element found!" );
else console.log( "Element not found!" );
|
Output
Element found!
Element not found!
Time Complexity: O(logN)
Auxiliary Space: O(1)
A Sorting Algorithm is used to arrange a given array or list of elements according to a comparison operator on the elements. The comparison operator is used to decide the new order of elements in the respective data structure.
The most common sorting algorithms are:
Bubble sort
Below is the implementation of bubble sort in javascript:
Javascript
function swap(arr, xp, yp)
{
var temp = arr[xp];
arr[xp] = arr[yp];
arr[yp] = temp;
}
function bubbleSort( arr, n)
{
var i, j;
for (i = 0; i < n-1; i++)
{
for (j = 0; j < n-i-1; j++)
{
if (arr[j] > arr[j+1])
{
swap(arr,j,j+1);
}
}
}
}
function printArray(arr, size)
{
var i;
for (i=0; i < size; i++)
console.log(arr[i]+ " " );
}
var arr = [5, 1, 4, 2, 8];
var n = 5;
console.log( "UnSorted array:" );
printArray(arr, n);
bubbleSort(arr, n);
console.log( "Sorted array: " );
printArray(arr, n);
|
Output
UnSorted array:
5
1
4
2
8
Sorted array:
1
2
4
5
8
Insertion sort
Below is the implementation of Insertion sort in javascript:
Javascript
function insertionSort(arr, n)
{
let i, key, j;
for (i = 1; i < n; i++)
{
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key)
{
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
function printArray(arr, n)
{
let i;
for (i = 0; i < n; i++)
console.log(arr[i] + " " );
}
let arr = [12, 11, 13, 5, 6 ];
let n = arr.length;
console.log( "Elements before sorting:" )
printArray(arr, n);
insertionSort(arr, n);
console.log( "Elements after sorting:" )
printArray(arr, n);
|
Output
Elements before sorting:
12
11
13
5
6
Elements after sorting:
5
6
11
12
13
Selection sort
Below is the implementation of selection sort in javascript:
Javascript
function swap(arr,xp, yp)
{
var temp = arr[xp];
arr[xp] = arr[yp];
arr[yp] = temp;
}
function selectionSort(arr, n)
{
var i, j, min_idx;
for (i = 0; i < n-1; i++)
{
min_idx = i;
for (j = i + 1; j < n; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
swap(arr,min_idx, i);
}
}
function printArray( arr, size)
{
var i;
for (i = 0; i < size; i++)
console.log(arr[i] + " " );
}
var arr = [64, 25, 12, 22, 11];
var n = 5;
console.log( "UnSorted array: " );
printArray(arr, n);
selectionSort(arr, n);
console.log( "Sorted array:" );
printArray(arr, n);
|
Output
UnSorted array:
64
25
12
22
11
Sorted array:
11
12
22
25
64
The process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. Using a recursive algorithm, certain problems can be solved quite easily. Examples of such problems are Towers of Hanoi (TOH), Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc
Tower of Hanoi
Below is the implementation of Tower of Hanoi in javascript:
Javascript
function towerOfHanoi(n, from_rod, to_rod, aux_rod)
{
if (n == 0)
{
return ;
}
towerOfHanoi(n - 1, from_rod, aux_rod, to_rod);
console.log( "Move disk " + n + " from rod " + from_rod +
" to rod " + to_rod);
towerOfHanoi(n - 1, aux_rod, to_rod, from_rod);
}
var N = 3;
towerOfHanoi(N, 'A' , 'C' , 'B' );
|
Output
Move disk 1 from rod A to rod C
Move disk 2 from rod A to rod B
Move disk 1 from rod C to rod B
Move disk 3 from rod A to rod C
Move disk 1 from rod B to rod A
Move disk 2 from rod B to rod C
Move disk 1 from rod A to rod C
DFS of a Graph
Below is the implementation of DFS in javascript:
Javascript
class Graph
{
constructor(v) {
this .V = v;
this .adj = new Array(v).fill([]);
}
AddEdge(v, w) {
this .adj[v].push(w);
}
DFSUtil(v, visited)
{
visited[v] = true ;
console.log(v + " " );
for (const n of this .adj[v]) {
if (!visited[n]) this .DFSUtil(n, visited);
}
}
DFS()
{
var visited = new Array( this .V).fill( false );
for ( var i = 0; i < this .V; ++i)
if (visited[i] == false ) this .DFSUtil(i, visited);
}
}
var g = new Graph(4);
g.AddEdge(0, 1);
g.AddEdge(0, 2);
g.AddEdge(1, 2);
g.AddEdge(2, 0);
g.AddEdge(2, 3);
g.AddEdge(3, 3);
console.log( "Following is Depth First Traversal<br>" );
g.DFS();
|
Output
Following is Depth First Traversal<br>
0
1
2
3
Fibonacci number
Below is the implementation of the Fibonacci number in javascript:
Javascript
let n = 9;
function fib(n) {
if (n <= 1)
return n;
return fib(n-1) + fib(n-2);
}
console.log(fib(n));
|
Backtracking can be defined as a general algorithmic technique that considers searching every possible combination in order to solve a computational problem
Sudoku Algorithm
Below is the implementation of the sudoku algorithm in javascript:
Javascript
let N = 9;
function solveSudoku(grid, row, col)
{
if (row == N - 1 && col == N)
return true ;
if (col == N)
{
row++;
col = 0;
}
if (grid[row][col] != 0)
return solveSudoku(grid, row, col + 1);
for (let num = 1; num < 10; num++)
{
if (isSafe(grid, row, col, num))
{
grid[row][col] = num;
if (solveSudoku(grid, row, col + 1))
return true ;
}
grid[row][col] = 0;
}
return false ;
}
function isSafe(grid, row, col, num)
{
for (let x = 0; x <= 8; x++)
if (grid[row][x] == num)
return false ;
for (let x = 0; x <= 8; x++)
if (grid[x][col] == num)
return false ;
let startRow = row - row % 3,
startCol = col - col % 3;
for (let i = 0; i < 3; i++)
for (let j = 0; j < 3; j++)
if (grid[i + startRow][j + startCol] == num)
return false ;
return true ;
}
let grid = [ [ 3, 0, 6, 5, 0, 8, 4, 0, 0 ],
[ 5, 2, 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 8, 7, 0, 0, 0, 0, 3, 1 ],
[ 0, 0, 3, 0, 1, 0, 0, 8, 0 ],
[ 9, 0, 0, 8, 6, 3, 0, 0, 5 ],
[ 0, 5, 0, 0, 9, 0, 6, 0, 0 ],
[ 1, 3, 0, 0, 0, 0, 2, 5, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 7, 4 ],
[ 0, 0, 5, 2, 0, 6, 3, 0, 0 ] ];
if (solveSudoku(grid, 0, 0))
console.log(grid);
else
console.log( "no solution exists " );
|
Output
[
[
3, 1, 6, 5, 7,
8, 4, 9, 2
],
[
5, 2, 9, 1, 3,
4, 7, 6, 8
],
[
4, 8, 7, 6, 2,
9, 5, 3, 1
],
[
2, 6, 3, 4, 1,
5, 9, 8, 7
],
[
9, 7, 4, 8, 6,
3, 1, 2, 5
],
[
8, 5, 1, 7, 9,
2, 6, 4, 3
],
[
1, 3, 8, 9, 4,
7, 2, 5, 6
],
[
6, 9, 2, 3, 5,
1, 8, 7, 4
],
[
7, 4, 5, 2, 8,
6, 3, 1, 9
]
]
M Coloring Problem
Below is the implementation of the M-coloring Problem in javascript:
Javascript
let V = 4;
function printSolution(color)
{
console.log( "Solution Exists:" +
" Following are the assigned colors " );
for (let i = 0; i < V; i++)
console.log( " " + color[i]);
console.log( " " );
}
function isSafe(graph,color)
{
for (let i = 0; i < V; i++)
for (let j = i + 1; j < V; j++)
if (graph[i][j] && color[j] == color[i])
return false ;
return true ;
}
function graphColoring(graph,m,i,color)
{
if (i == V) {
if (isSafe(graph, color))
{
printSolution(color);
return true ;
}
return false ;
}
for (let j = 1; j <= m; j++)
{
color[i] = j;
if (graphColoring(graph, m, i + 1, color))
return true ;
color[i] = 0;
}
return false ;
}
let graph=[[ false , true , true , true ],
[ true , false , true , false ],
[ true , true , false , true ],
[ true , false , true , false ]];
let m = 3;
let color = new Array(V);
for (let i = 0; i < V; i++)
color[i] = 0;
if (!graphColoring(graph, m, 0, color))
console.log( "Solution does not exist" );
|
Output
Solution Exists: Following are the assigned colors
1
2
3
2
N Queen Problem
Below is the implementation of the N-Queen Problem in javascript:
Javascript
const N = 4
function printSolution(board)
{
for (let i = 0; i < N; i++)
{
for (let j = 0; j < N; j++)
{
if (board[i][j] == 1)
console.log( "Q " )
else
console.log( ". " )
}
}
}
function isSafe(board, row, col)
{
for (let i = 0; i < col; i++){
if (board[row][i] == 1)
return false
}
for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
if (board[i][j])
return false
for (i = row, j = col; j >= 0 && i < N; i++, j--)
if (board[i][j])
return false
return true
}
function solveNQUtil(board, col){
if (col >= N)
return true
for (let i=0;i<N;i++){
if (isSafe(board, i, col)== true ){
board[i][col] = 1
if (solveNQUtil(board, col + 1) == true )
return true
board[i][col] = 0
}
}
return false
}
function solveNQ(){
let board = [ [0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0] ]
if (solveNQUtil(board, 0) == false ){
console.log( "Solution does not exist" )
return false
}
printSolution(board)
return true
}
solveNQ()
|
Output
.
.
Q
.
Q
.
.
.
.
.
.
Q
.
Q
.
.
Dynamic Programming is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of subproblems, so that we do not have to re-compute them when needed later. This simple optimization reduces time complexities from exponential to polynomial.
Dynamic Programming
Standard problems on Dynamic Programming:
- Fibonacci numbers
- nth Catalan Number
- Bell Numbers (Number of Ways to Partition a Set)
- Binomial Coefficient
- Coin change problem
- Subset Sum Problem
Catalan numbers are defined as a mathematical sequence that consists of positive integers, which can be used to find the number of possibilities of various combinations.
The nth term in the sequence denoted Cn, is found in the following formula:
The first few Catalan numbers for n = 0, 1, 2, 3, … are : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, …
Below is the implementation of the Nth Catalan number in javascript:
Javascript
function catalan(n)
{
if (n <= 1)
return 1;
let res = 0;
for (let i = 0; i < n; i++)
res += catalan(i) *
catalan(n - i - 1);
return res;
}
for (let i = 0; i < 10; i++)
console.log(catalan(i) + " " );
|
Output
1
1
2
5
14
42
132
429
1430
4862
A binomial coefficient C(n, k) can be defined as the coefficient of x^k in the expansion of (1 + x)^n.
A binomial coefficient C(n, k) also gives the number of ways, disregarding order, that k objects can be chosen from among n objects more formally, the number of k-element subsets (or k-combinations) of a n-element set.
Below is the implementation of the Binomial coefficient in javascript:
Javascript
function binomialCoeff(n, k)
{
var C = Array(n + 1).fill(0).map(
x => Array(k + 1).fill(0));;
var i, j;
for (i = 0; i <= n; i++)
{
for (j = 0; j <= min(i, k); j++)
{
if (j == 0 || j == i)
C[i][j] = 1;
else
C[i][j] = C[i - 1][j - 1] +
C[i - 1][j];
}
}
return C[n][k];
}
function min(a, b)
{
return (a < b) ? a : b;
}
var n = 5, k = 2;
console.log( "Value of C(" + n + "," + k +
") is " + binomialCoeff(n, k));
|
Output
Value of C(5,2) is 10
Given a set of non-negative integers and a value sum, the task is to check if there is a subset of the given set whose sum is equal to the given sum.
Below is the implementation of the Subset Sum Problem in javascript:
Javascript
function isSubsetSum(set, n, sum)
{
let subset = new Array(sum + 1);
for (let i = 0; i < sum + 1; i++)
{
subset[i] = new Array(sum + 1);
for (let j = 0; j < n + 1; j++)
{
subset[i][j] = 0;
}
}
for (let i = 0; i <= n; i++)
subset[0][i] = true ;
for (let i = 1; i <= sum; i++)
subset[i][0] = false ;
for (let i = 1; i <= sum; i++) {
for (let j = 1; j <= n; j++) {
subset[i][j] = subset[i][j - 1];
if (i >= set[j - 1])
subset[i][j] = subset[i][j]
|| subset[i - set[j - 1]][j - 1];
}
}
return subset[sum][n];
}
let set = [ 3, 34, 4, 12, 5, 2 ];
let sum = 9;
let n = set.length;
if (isSubsetSum(set, n, sum) == true )
console.log( "Found a subset" + " with given sum" );
else
console.log( "No subset with" + " given sum" );
|
Output
Found a subset with given sum
Standard problems on Mathematical algorithms:
- Prime Numbers
- Sieve of Eratosthenes
- LCM of array
- GCD of array
- Program to add two polynomials
- Check divisibility by 7
- Euclidean algorithms
- Generate Pythagorean Triplets
Prime numbers
Below is the implementation of the Prime number in javascript:
Javascript
function isPrime(n) {
if (n <= 1)
return false ;
for (let i = 2; i < n; i++)
if (n % i == 0)
return false ;
return true ;
}
isPrime(11)
? console.log( "true" )
: console.log( "false" );
|
Given an array of n numbers, find the LCM of it.
Below is the implementation of the LCM of array elements in javascript:
Javascript
function gcd(a, b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
function findlcm(arr, n)
{
let ans = arr[0];
for (let i = 1; i < n; i++)
ans = (((arr[i] * ans)) /
(gcd(arr[i], ans)));
return ans;
}
let arr = [ 2, 7, 3, 9, 4 ];
let n = arr.length;
console.log(findlcm(arr, n));
|
The Euclidean algorithm is a way to find the greatest common divisor of two positive integers. GCD of two numbers is the largest number that divides both of them. A simple way to find GCD is to factorize both numbers and multiply common prime factors.
Below is the implementation of the Euclidean algorithm in javascript:
Javascript
function gcdExtended(a, b,
x, y)
{
if (a == 0)
{
x = 0;
y = 1;
return b;
}
let gcd = gcdExtended(b % a,
a, x, y);
x = y - (b / a) * x;
y = x;
return gcd;
}
let x = 0;
let y = 0;
let a = 35;
let b = 15;
let g = gcdExtended(a, b, x, y);
console.log( "gcd(" + a);
console.log( ", " + b + ")" );
console.log( " = " + g);
|
Standard Problems on Bit Algorithms:
- Count set bits in an integer
- Add two-bit strings
- Turn off the rightmost set bit
- Rotate bits of a number
- Program to find parity
- Check if a number is Bleak
count set bit
Below is the implementation of the Count set bits in an integer in javascript:
Javascript
function countSetBits(n)
{
var count = 0;
while (n > 0)
{
n &= (n - 1);
count++;
}
return count;
}
var i = 9;
console.log(countSetBits(i));
|
Given two bit sequences as strings, write a function to return the addition of the two sequences. Bit strings can be of different lengths also. For example, if string 1 is “1100011” and second string 2 is “10”, then the function should return “1100101”.
Below is the implementation of the Add two-bit strings in javascript:
Javascript
function makeEqualLength(str1, str2)
{
var len1 = str1.length;
var len2 = str2.length;
if (len1 < len2)
{
for ( var i = 0 ; i < len2 - len1 ; i++)
str1 = '0' + str1;
return len2;
}
else if (len1 > len2)
{
for ( var i = 0 ; i < len1 - len2 ; i++)
str2 = '0' + str2;
}
return len1;
}
function addBitStrings(first, second )
{
var result = "" ;
var length = makeEqualLength(first, second);
var carry = 0;
for ( var i = length-1 ; i >= 0 ; i--)
{
var firstBit = first[i] - '0' ;
var secondBit = second[i] - '0' ;
var sum = (firstBit ^ secondBit ^ carry) + 48;
result += String.fromCharCode(sum);
carry = (firstBit & secondBit) | (secondBit & carry) | (firstBit & carry);
}
if (carry)
result += '1' ;
return result;
}
var str1 = "1100011" ;
var str2 = "10" ;
console.log( "Sum is " + addBitStrings(str1, str2));
|
Parity: Parity of a number refers to whether it contains an odd or even number of 1-bits. The number has “odd parity” if it contains an odd number of 1-bits and is “even parity” if it contains an even number of 1-bits.
Below is the implementation of the find parity in javascript:
Javascript
function getParity(n)
{
var parity = false ;
while (n != 0)
{
parity = !parity;
n = n & (n - 1);
}
return parity;
}
var n = 7;
console.log( "Parity of no " + n + " = " +
(getParity(n) ? "odd" : "even" ));
|
Output
Parity of no 7 = odd
Frequently Asked Questions (FAQs) – DSA using JavaScript Tutorial
1. What is the prerequisite knowledge for this tutorial?
- Basic understanding of JavaScript syntax, variables, loops, and functions is recommended. Familiarity with fundamental programming concepts is beneficial.
2. Do I need any specific software or tools to follow along?
- A code editor (e.g., Visual Studio Code, Sublime Text) and a JavaScript runtime environment (e.g., Node.js) are sufficient. No special tools are required.
3. Is this tutorial suitable for beginners?
- Yes, this tutorial is designed to be beginner-friendly. It starts with fundamental concepts and gradually progresses to more advanced topics.
4. How can I practice the algorithms covered in the tutorial?
- Practice by implementing the algorithms in a code editor, running them in a JavaScript environment, and experimenting with variations. Leverage coding platforms like LeetCode for additional challenges.
5. Are there any coding exercises or challenges included?
- Yes, each section includes practice problems to reinforce your understanding. Additional challenges are encouraged for further practice.
6. Can I use this tutorial for interview preparation?
- Absolutely! Understanding and practicing these algorithms will significantly contribute to your interview preparation for technical roles.
7. Are there any forums or communities to discuss the tutorial content?
- Join online coding communities like Stack Overflow, Reddit (r/learnjavascript), or platforms like Discord where you can discuss concepts, seek help, and share your solutions.
8. How do I handle difficulties or challenges in understanding certain topics?
- If you encounter challenges, revisit the explanations, experiment with the code, and seek help from online communities. Sometimes, discussing problems with others can provide valuable insights.
9. Is it necessary to complete the tutorial in order, or can I skip sections?
- While it’s recommended to follow the tutorial in order to build a solid foundation, you can skip to specific sections based on your needs. However, ensure you have a good understanding of the skipped topics.
10. What’s the best way to apply these algorithms in real-world projects?
- Identify opportunities in your projects where these algorithms can be applied. For example, sorting and searching algorithms in data processing, or graph algorithms in network analysis. Practice integrating them into practical scenarios.
11. How often should I revisit the tutorial for reinforcement?
- Regular reinforcement is beneficial. Consider revisiting the tutorial, solving additional problems, and exploring advanced topics as you gain more experience in programming and problem-solving.
12. Can I use this tutorial as a reference for technical interviews?
- Yes, this tutorial can serve as a valuable reference for technical interviews. Practice implementing algorithms and explaining your thought process, which is often a crucial aspect of technical interviews.
Feel free to ask additional questions or seek clarification on any topic throughout your learning journey. Happy coding!
Share your thoughts in the comments
Please Login to comment...