JavaScript Algorithms and Coding Challenges: A Beginner's Guide
This chapter introduces fundamental JavaScript algorithms and coding challenge strategies, particularly relevant for coding interviews and building a strong foundation in programming. We will explore ten essential JavaScript algorithm problems designed for beginners, mirroring the style of challenges encountered on platforms like LeetCode.
Introduction to Algorithm Challenges
This course, brought to you by mkar from Coding Monkey, aims to equip you with the skills to confidently tackle coding interview questions at leading technology companies. We will focus on building a solid understanding of JavaScript algorithms through practical examples and step-by-step solutions. These exercises are designed for absolute beginners and will not only enhance your coding abilities but also prepare you for technical interviews at Big Tech companies, where starting salaries can be substantial.
Let’s begin our journey into the world of JavaScript algorithms!
1. Reversing Strings and Integers
Reversing a string or an integer is a classic coding interview question. It tests your understanding of basic data manipulation and algorithmic thinking.
1.1 Reversing a String
Problem: Given a string, return a new string with the characters in reverse order.
Examples:
- Input: “coding money”
- Output: “yenom gnidoc”
Let’s explore several approaches to solve this problem.
1.1.1 Using a Traditional for
Loop
One way to reverse a string is by iterating through it character by character and building the reversed string incrementally.
function reverseStringForLoop(str) {
let reversed = '';
for (let i = 0; i < str.length; i++) {
reversed = str[i] + reversed;
}
return reversed;
}
console.log(reverseStringForLoop("coding money")); // Output: yenom gnidoc
In this approach:
- We initialize an empty string variable
reversed
. - The
for
loop iterates from the first character to the last character of the input stringstr
. - In each iteration, we prepend the current character
str[i]
to thereversed
string. - Finally, we return the
reversed
string.
While functional, this traditional for
loop involves several moving parts (initialization, condition, incrementer), which can be prone to errors.
1.1.2 Using a for...of
Loop (Modern JavaScript Syntax)
A cleaner and more readable approach in modern JavaScript is to use the for...of
loop, which simplifies iteration over iterable objects like strings.
function reverseStringForOfLoop(str) {
let reversed = '';
for (const char of str) {
reversed = char + reversed;
}
return reversed;
}
console.log(reverseStringForOfLoop("coding money")); // Output: yenom gnidoc
This for...of
loop achieves the same result as the traditional for
loop but with more concise syntax. It iterates directly over the characters of the string, making the code easier to understand and less error-prone.
1.1.3 Using Built-in JavaScript Methods: split()
, reverse()
, and join()
JavaScript provides built-in methods that offer a more elegant and efficient way to reverse a string. These methods leverage the power of array manipulation.
function reverseStringBuiltIn(str) {
return str.split('').reverse().join('');
}
console.log(reverseStringBuiltIn("coding money")); // Output: yenom gnidoc
Let’s break down this one-line solution:
-
split('')
: Thesplit('')
method converts the string into an array of individual characters. Passing an empty string as the separator ensures each character becomes a separate element in the array.split()
: This JavaScript string method divides a String into an ordered list of substrings, puts these substrings into an array, and returns the array. The splitting is done by searching for a pattern. -
reverse()
: Thereverse()
method, applied to the array, reverses the order of elements in the array in place.reverse()
: This JavaScript array method reverses an array in place. The first array element becomes the last, and the last array element becomes the first. -
join('')
: Thejoin('')
method converts the reversed array back into a string. Passing an empty string as the argument concatenates the array elements without any separator in between.join()
: This JavaScript array method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string.
This method is concise and leverages JavaScript’s built-in functionalities, making it a highly efficient approach for reversing strings.
Note: While built-in methods offer convenience, interviewers may sometimes restrict their use to assess your understanding of fundamental algorithms. Therefore, understanding the for
loop and for...of
loop approaches is crucial.
1.2 Reversing an Integer
Problem: Given an integer, return the integer with its digits reversed.
Examples:
- Input: 15
- Output: 51
- Input: -15
- Output: -51
Building upon our string reversal knowledge, we can easily reverse an integer. The core idea is to convert the integer to a string, reverse the string, and then convert it back to an integer.
function reverseInteger(n) {
const reversedString = n.toString().split('').reverse().join('');
let reversedInteger = parseInt(reversedString);
// Handle negative sign
reversedInteger = reversedInteger * Math.sign(n);
return reversedInteger;
}
console.log(reverseInteger(15)); // Output: 51
console.log(reverseInteger(-15)); // Output: -51
Let’s break down the reverseInteger
function:
-
n.toString()
: Converts the input integern
into a string. -
.split('').reverse().join('')
: Reverses the string representation of the integer using the same string reversal technique we learned earlier. -
parseInt(reversedString)
: Converts the reversed string back into an integer.parseInt()
: This JavaScript function parses a string and returns an integer. -
Math.sign(n)
: This is crucial for handling negative numbers.Math.sign(n)
returns 1 ifn
is positive, -1 ifn
is negative, and 0 ifn
is zero. Multiplying the reversed integer byMath.sign(n)
ensures that the sign of the original integer is preserved in the reversed integer.Math.sign()
: This JavaScript Math method returns the sign of a number, indicating whether the number is positive, negative, or zero.
This approach effectively reverses both positive and negative integers.
2. Palindrome Check
Problem: Given a string, return true
if the string is a palindrome, or false
if it is not.
Definition: A palindrome is a string that reads the same forwards and backward.
Examples:
- “kayak” is a palindrome (true)
- “madam” is a palindrome (true)
- “coding money” is not a palindrome (false)
Leveraging our string reversal skills, checking for a palindrome becomes straightforward.
function isPalindrome(str) {
const reversedStr = str.split('').reverse().join('');
return str === reversedStr;
}
console.log(isPalindrome("kayak")); // Output: true
console.log(isPalindrome("coding money")); // Output: false
In this function:
- We reverse the input string
str
usingsplit('').reverse().join('')
and store it inreversedStr
. - We then directly compare the original string
str
with the reversed stringreversedStr
. - If they are identical, the function returns
true
(it’s a palindrome); otherwise, it returnsfalse
.
This solution is concise and efficient due to the reuse of our string reversal logic.
Homework/Further Exploration:
-
Two-Pointer Technique: Research and implement a palindrome check using the two-pointer technique. This method avoids creating a reversed string and can be more efficient in certain scenarios.
-
every()
Method: Explore and utilize the JavaScriptevery()
array method to solve the palindrome problem.every()
: This JavaScript array method tests whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value.
3. Finding the Most Common Character in a String
Problem: Given a string, return the character that appears most frequently in the string.
Examples:
- Input: “abcccde”
- Output: “c”
- Input: “11223333”
- Output: “3”
This problem introduces the concept of character frequency analysis and utilizes data structures to efficiently count character occurrences.
3.1 Using a Character Map (Object)
To solve this, we can use a character map (implemented as a JavaScript object) to store the count of each character in the string.
function mostFrequentChar(str) {
const charMap = {};
for (const char of str) {
charMap[char] = charMap[char] + 1 || 1;
}
let maxCount = 0;
let maxChar = '';
for (const char in charMap) {
if (charMap[char] > maxCount) {
maxCount = charMap[char];
maxChar = char;
}
}
return maxChar;
}
console.log(mostFrequentChar("abcccde")); // Output: c
console.log(mostFrequentChar("11223333")); // Output: 3
Let’s break down the mostFrequentChar
function:
-
Character Map Creation (
charMap
Object):- We initialize an empty object
charMap
to store character counts. - We iterate through each character
char
in the input stringstr
using afor...of
loop. - For each character:
charMap[char] = charMap[char] + 1 || 1;
This line efficiently updates the count in thecharMap
.charMap[char] + 1
: If the characterchar
already exists as a key incharMap
, we increment its existing count by 1.|| 1
: IfcharMap[char]
isundefined
(meaning the character is encountered for the first time), the|| 1
(OR operator) sets the initial count to 1.
- We initialize an empty object
-
Finding the Maximum Count and Character:
-
We initialize
maxCount
to 0 andmaxChar
to an empty string to track the highest count and corresponding character. -
We iterate through the keys (characters) of the
charMap
object using afor...in
loop.for...in
loop: This JavaScript loop iterates over all enumerable string properties of an object. -
For each character
char
incharMap
:if (charMap[char] > maxCount)
: We check if the count of the current charactercharMap[char]
is greater than the currentmaxCount
.- If it is, we update
maxCount
to the new higher count andmaxChar
to the characterchar
.
-
-
Returning the Most Frequent Character:
- Finally, we return
maxChar
, which holds the character with the highest frequency.
- Finally, we return
This approach effectively counts character frequencies using an object as a character map and then iterates through the map to find the character with the maximum count.
Alternative Data Structure: Map
JavaScript also offers the Map
data structure, which is similar to objects but can be more suitable for certain scenarios. Map
keys can be of any data type, and it maintains the insertion order of elements. While objects are sufficient for this problem, Map
is worth noting as another option for character mapping.
Map
: TheMap
object in JavaScript is a collection of key-value pairs where keys and values can be of any data type. Maps remember the original insertion order of the keys.
4. Array Chunking
Problem: Given an array and a chunk size, divide the array into many subarrays where each subarray is of length size
.
Examples:
- Array:
[1, 2, 3, 4, 5]
, Chunk Size:2
Output:[[1, 2], [3, 4], [5]]
- Array:
[1, 2, 3, 4, 5, 6, 7, 8]
, Chunk Size:3
Output:[[1, 2, 3], [4, 5, 6], [7, 8]]
This problem involves array manipulation and demonstrates the concept of dividing a larger data structure into smaller, manageable pieces.
4.1 Iterative Approach with slice()
and push()
We can solve this problem iteratively by using a while
loop and the slice()
method to extract chunks from the original array.
function chunkArray(array, size) {
const chunkedArray = [];
let index = 0;
while (index < array.length) {
chunkedArray.push(array.slice(index, index + size));
index += size;
}
return chunkedArray;
}
console.log(chunkArray([1, 2, 3, 4, 5], 2)); // Output: [[1, 2], [3, 4], [5]]
console.log(chunkArray([1, 2, 3, 4, 5, 6, 7, 8], 3)); // Output: [[1, 2, 3], [4, 5, 6], [7, 8]]
Let’s understand the chunkArray
function:
-
Initialization:
chunkedArray = []
: We create an empty arraychunkedArray
to store the resulting subarrays (chunks).index = 0
: We initialize anindex
variable to 0. Thisindex
will track our current position in the originalarray
.
-
while
Loop for Chunking:-
while (index < array.length)
: Thewhile
loop continues as long as ourindex
is within the bounds of the originalarray
. This ensures we process the entire array. -
chunkedArray.push(array.slice(index, index + size))
:-
array.slice(index, index + size)
: Theslice()
method extracts a portion of thearray
starting fromindex
up to (but not including)index + size
. This creates a chunk of the desiredsize
. -
chunkedArray.push(...)
: We use thepush()
method to add this extracted chunk (subarray) to ourchunkedArray
.
push()
: This JavaScript array method adds one or more elements to the end of an array and returns the new length of the array. -
-
index += size
: We increment theindex
bysize
. This moves our starting position in the originalarray
forward by the chunk size, preparing for the extraction of the next chunk.
-
-
Returning the Chunked Array:
return chunkedArray
: After thewhile
loop completes (we’ve processed the entirearray
), we return thechunkedArray
containing all the subarrays (chunks).
This iterative approach effectively divides the input array into chunks of the specified size. The slice()
method plays a key role in extracting portions of the array, and the while
loop ensures we process the entire array.
5. Capitalizing the First Letter of Each Word
Problem: Write a function that accepts a string and capitalizes the first letter of each word in the string.
Examples:
- Input: “i want to open the five tile case”
- Output: “I Want To Open The Five Tile Case”
This problem focuses on string manipulation and word-level processing.
5.1 Iterative Approach with split()
, slice()
, and join()
One way to capitalize the first letter of each word is to split the string into words, capitalize the first letter of each word individually, and then join them back into a string.
function capitalizeWords(str) {
const words = str.split(' ');
const capitalizedWords = [];
for (const word of words) {
capitalizedWords.push(word.charAt(0).toUpperCase() + word.slice(1));
}
return capitalizedWords.join(' ');
}
console.log(capitalizeWords("i want to open the five tile case"));
// Output: I Want To Open The Five Tile Case
Let’s break down the capitalizeWords
function:
-
Splitting the String into Words:
words = str.split(' ')
: We use thesplit(' ')
method to split the input stringstr
into an array of words, using a space (” ”) as the delimiter.
-
Capitalizing Each Word:
-
capitalizedWords = []
: We initialize an empty arraycapitalizedWords
to store the capitalized words. -
for (const word of words)
: We iterate through eachword
in thewords
array. -
capitalizedWords.push(word.charAt(0).toUpperCase() + word.slice(1))
:-
word.charAt(0).toUpperCase()
:-
word.charAt(0)
: Extracts the first character of theword
.
charAt()
: This JavaScript string method returns the character at a specified index (position) in a string.-
.toUpperCase()
: Converts the first character to uppercase.
toUpperCase()
: This JavaScript string method converts a string to uppercase. -
-
word.slice(1)
: Extracts the rest of the word, starting from the second character (index 1) to the end.
slice()
: This JavaScript string method extracts a section of a string and returns it as a new string, without modifying the original string.... + ...
: We concatenate the capitalized first character with the rest of the word.capitalizedWords.push(...)
: We push the capitalized word into thecapitalizedWords
array.
-
-
-
Joining the Capitalized Words Back into a String:
return capitalizedWords.join(' ')
: We use thejoin(' ')
method to join the words in thecapitalizedWords
array back into a single string, using a space (” ”) as the separator.
This approach iterates through each word, capitalizes its first letter, and reconstructs the sentence, effectively capitalizing the first letter of every word.
5.2 Using map()
for a More Concise Solution
The map()
array method provides a more concise way to achieve the same result. map()
creates a new array by calling a provided function on every element in the calling array.
function capitalizeWordsMap(str) {
return str.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
console.log(capitalizeWordsMap("i want to open the five tile case"));
// Output: I Want To Open The Five Tile Case
In this map
-based solution:
str.split(' ')
: We split the string into an array of words, as before..map(word => word.charAt(0).toUpperCase() + word.slice(1))
: We use themap()
method to process eachword
in the array.word => ...
: This is an arrow function that takes eachword
as input.word.charAt(0).toUpperCase() + word.slice(1)
: This is the same logic we used in thefor...of
loop to capitalize the first letter and combine it with the rest of the word.map()
applies this transformation to each word in the array.
.join(' ')
: We join the resulting array of capitalized words back into a string.
This map()
solution is functionally equivalent to the for...of
loop version but is more compact and often considered more idiomatic JavaScript for array transformations.
6. Anagram Check
Problem: Write a function to determine if two provided strings are anagrams of each other.
Definition: Two strings are anagrams if they contain the same characters with the same frequencies, disregarding spaces, punctuation, and case.
Examples:
- “coding money”, “money coding” -> true
- ”rail safety”, “fairy tales” -> true
- ”hello”, “olleh” -> true
- ”hello”, “olleho” -> false
6.1 Character Map Comparison Approach
One approach to check for anagrams is to build character maps for both strings and then compare these maps for equality.
function areAnagrams(stringA, stringB) {
const charMapA = buildCharMap(stringA);
const charMapB = buildCharMap(stringB);
if (Object.keys(charMapA).length !== Object.keys(charMapB).length) {
return false; // Different number of unique characters cannot be anagrams
}
for (const char in charMapA) {
if (charMapA[char] !== charMapB[char]) {
return false; // Character counts don't match
}
}
return true; // Character maps are identical, strings are anagrams
}
function buildCharMap(str) {
const charMap = {};
const cleanedStr = str.toLowerCase().replace(/[^a-z0-9]/g, '');
for (const char of cleanedStr) {
charMap[char] = charMap[char] + 1 || 1;
}
return charMap;
}
console.log(areAnagrams("coding money", "money coding")); // Output: true
console.log(areAnagrams("rail safety", "fairy tales")); // Output: true
console.log(areAnagrams("hello", "olleho")); // Output: false
Let’s analyze the areAnagrams
function and the helper function buildCharMap
:
-
buildCharMap(str)
Helper Function:const charMap = {}
: Initializes an empty objectcharMap
to store character counts.const cleanedStr = str.toLowerCase().replace(/[^a-z0-9]/g, '')
:-
str.toLowerCase()
: Converts the input string to lowercase to handle case-insensitivity. -
.replace(/[^a-z0-9]/g, '')
: Removes non-alphanumeric characters (spaces, punctuation, etc.) using a regular expression.Regular Expression:
/[^a-z0-9]/g
[...]
: Character set.^
: Inside a character set,^
negates the set, meaning “match any character that is not in this set”.a-z0-9
: Matches lowercase letters ‘a’ through ‘z’ and digits ‘0’ through ‘9’.[^a-z0-9]
: Matches any character that is not a lowercase letter or digit./g
: The global flag. It means “find all matches” rather than stopping after the first match.
-
.replace(..., '')
: Replaces all matches found by the regular expression with an empty string, effectively removing them.
-
for (const char of cleanedStr)
: Iterates through each character in thecleanedStr
.charMap[char] = charMap[char] + 1 || 1
: Updates the character count incharMap
, as explained in the “Most Common Character” section.return charMap
: Returns the completed character map.
-
areAnagrams(stringA, stringB)
Function:-
const charMapA = buildCharMap(stringA)
: Creates a character map forstringA
usingbuildCharMap
. -
const charMapB = buildCharMap(stringB)
: Creates a character map forstringB
usingbuildCharMap
. -
if (Object.keys(charMapA).length !== Object.keys(charMapB).length)
: Checks if the number of unique characters (keys in the character maps) is different. If so, they cannot be anagrams, and we returnfalse
.Object.keys()
: This JavaScript method returns an array of a given object’s own enumerable property names, iterated in the same order as a normal loop. -
for (const char in charMapA)
: Iterates through the keys (characters) ofcharMapA
. -
if (charMapA[char] !== charMapB[char])
: For each character, it checks if its count incharMapA
is different from its count incharMapB
. If counts don’t match for any character, they are not anagrams, and we returnfalse
. -
return true
: If all checks pass (same number of unique characters and matching counts for each character), the strings are anagrams, and we returntrue
.
-
This character map comparison approach is robust and correctly handles spaces, punctuation, and case differences.
6.2 Sorted String Comparison Approach
A simpler and more intuitive approach is to sort both strings alphabetically and then compare the sorted strings. If the sorted strings are identical, they are anagrams.
function areAnagramsSorted(stringA, stringB) {
return cleanString(stringA) === cleanString(stringB);
}
function cleanString(str) {
return str.toLowerCase().replace(/[^a-z0-9]/g, '').split('').sort().join('');
}
console.log(areAnagramsSorted("coding money", "money coding")); // Output: true
console.log(areAnagramsSorted("rail safety", "fairy tales")); // Output: true
console.log(areAnagramsSorted("hello", "olleho")); // Output: false
Let’s examine the areAnagramsSorted
function and the helper function cleanString
:
-
cleanString(str)
Helper Function:str.toLowerCase().replace(/[^a-z0-9]/g, '')
: Cleans the string by converting to lowercase and removing non-alphanumeric characters, just like inbuildCharMap
..split('')
: Splits the cleaned string into an array of characters..sort()
: Sorts the array of characters alphabetically in place..join('')
: Joins the sorted array of characters back into a string.return ...
: Returns the cleaned and sorted string.
-
areAnagramsSorted(stringA, stringB)
Function:cleanString(stringA) === cleanString(stringB)
: It callscleanString
on bothstringA
andstringB
to get their cleaned and sorted versions.- It then directly compares the two cleaned and sorted strings using strict equality (
===
). If they are identical, it returnstrue
(anagrams); otherwise,false
.
This sorted string comparison approach is remarkably simple and effective. Sorting ensures that anagrams will have the same character order, making direct string comparison a valid anagram check.
7. Counting Vowels in a String
Problem: Write a function that returns the number of vowels (a, e, i, o, u) used in a given string.
Examples:
- Input: “Coding Money” Output: 4 (o, i, o, e)
- Input: “How are you?” Output: 5 (o, a, e, o, u)
7.1 Regular Expression Approach
The most concise way to count vowels is using regular expressions.
function countVowelsRegex(str) {
const matches = str.toLowerCase().match(/[aeiou]/gi);
return matches ? matches.length : 0;
}
console.log(countVowelsRegex("Coding Money")); // Output: 4
console.log(countVowelsRegex("How are you?")); // Output: 5
Let’s understand the countVowelsRegex
function:
-
str.toLowerCase()
: Converts the input string to lowercase for case-insensitive vowel counting. -
.match(/[aeiou]/gi)
: Uses thematch()
method with a regular expression to find all vowels.Regular Expression:
/[aeiou]/gi
[...]
: Character set.aeiou
: Matches any of the vowels ‘a’, ‘e’, ‘i’, ‘o’, ‘u’./g
: The global flag (find all matches)./i
: The case-insensitive flag (match both uppercase and lowercase vowels).
-
matches ? matches.length : 0
: This is a ternary operator to handle cases where no vowels are found.matches
: Ifmatch()
finds vowels, it returns an array of matches (stored inmatches
). In a conditional context, an array is truthy.matches.length
: Ifmatches
is truthy (an array of matches), we return thelength
of the array, which is the count of vowels.0
: Ifmatch()
finds no vowels, it returnsnull
. In a conditional context,null
is falsy. Ifmatches
is falsy (null), we return 0 (no vowels found).
This regular expression approach is efficient and elegant for vowel counting.
7.2 Iterative Approach with includes()
An alternative approach without regular expressions is to iterate through the string and check if each character is a vowel using the includes()
method on a vowel string.
function countVowelsIterative(str) {
const vowelSet = "aeiou";
let count = 0;
for (const char of str.toLowerCase()) {
if (vowelSet.includes(char)) {
count++;
}
}
return count;
}
console.log(countVowelsIterative("Coding Money")); // Output: 4
console.log(countVowelsIterative("How are you?")); // Output: 5
Let’s break down the countVowelsIterative
function:
const vowelSet = "aeiou"
: Creates a stringvowelSet
containing all lowercase vowels.let count = 0
: Initializes acount
variable to 0 to track the number of vowels.for (const char of str.toLowerCase())
: Iterates through each character in the lowercase version of the input string.if (vowelSet.includes(char))
:-
vowelSet.includes(char)
: Checks if the current characterchar
is present in thevowelSet
string.includes()
: This JavaScript string method determines whether one string may be found within another string, returningtrue
orfalse
as appropriate. -
If
includes()
returnstrue
(the character is a vowel), we increment thecount
.
-
return count
: Returns the final vowelcount
.
This iterative approach, using includes()
, provides a clear and understandable way to count vowels without relying on regular expressions.
8. FizzBuzz
Problem: Write a program that console logs the numbers from 1 to n
. For multiples of 3, print “Fizz” instead of the number. For multiples of 5, print “Buzz”. For multiples of both 3 and 5, print “FizzBuzz”.
Example (n = 15):
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
FizzBuzz is a classic programming problem often used in coding interviews to assess basic conditional logic and modulo operator understanding.
8.1 Solution with Modulo Operator and Conditional Logic
function fizzBuzz(n) {
for (let i = 1; i <= n; i++) {
if (i % 3 === 0 && i % 5 === 0) {
console.log("FizzBuzz");
} else if (i % 3 === 0) {
console.log("Fizz");
} else if (i % 5 === 0) {
console.log("Buzz");
} else {
console.log(i);
}
}
}
fizzBuzz(15); // Runs FizzBuzz for n = 15
Let’s break down the fizzBuzz
function:
-
for
Loop for Numbers 1 ton
:for (let i = 1; i <= n; i++)
: Afor
loop iterates through numbers from 1 ton
(inclusive).
-
Conditional Logic (if/else if/else):
if (i % 3 === 0 && i % 5 === 0)
: Checks if the current numberi
is divisible by both 3 and 5.-
i % 3 === 0
: Checks ifi
is a multiple of 3 using the modulo operator.Modulo Operator (%): The modulo operator returns the remainder of a division. If
a % b === 0
, it meansa
is perfectly divisible byb
with no remainder. -
i % 5 === 0
: Checks ifi
is a multiple of 5. -
&&
: Logical AND operator. The condition is true only if bothi % 3 === 0
andi % 5 === 0
are true. -
If true, it
console.log("FizzBuzz")
.
-
else if (i % 3 === 0)
: If the previous condition is false, this checks ifi
is divisible by 3.- If true, it
console.log("Fizz")
.
- If true, it
else if (i % 5 === 0)
: If both previous conditions are false, this checks ifi
is divisible by 5.- If true, it
console.log("Buzz")
.
- If true, it
else
: If none of the above conditions are true (not divisible by 3 or 5), itconsole.log(i)
(prints the number itself).
The order of the if
and else if
conditions is crucial. We must check for divisibility by both 3 and 5 first because a number divisible by both is also divisible by 3 and divisible by 5 individually. If we checked for divisibility by 3 or 5 first, we would never reach the “FizzBuzz” condition for numbers like 15.
9. Step Shape Pattern
Problem: Write a function that accepts a positive number n
and console logs a step shape with n
levels using the pound character (#
). The step should have spaces on the right-hand side.
Examples (n = 3):
#
##
###
9.1 Nested Loop Approach
We can create the step shape using nested loops. The outer loop controls the rows (levels), and the inner loop handles the columns (characters in each row).
function stepShape(n) {
for (let row = 0; row < n; row++) {
let line = '';
for (let col = 0; col < n; col++) {
if (col <= row) {
line += '#';
} else {
line += ' ';
}
}
console.log(line);
}
}
stepShape(3); // Runs stepShape for n = 3
Let’s analyze the stepShape
function:
-
Outer Loop (Rows):
for (let row = 0; row < n; row++)
: The outerfor
loop iteratesn
times, representing each row (level) of the step shape.row
goes from 0 ton-1
.
-
Inner Loop (Columns) and Character Building:
let line = '';
: Inside the outer loop, we initialize an empty stringline
for each row. This string will store the characters for the current row.for (let col = 0; col < n; col++)
: The innerfor
loop also iteratesn
times, representing each column position within a row.col
also goes from 0 ton-1
.if (col <= row)
: This is the core logic to determine whether to place a#
or a space.col <= row
: For each row, we check if the current column indexcol
is less than or equal to the current row indexrow
.- If
true
, it means we are in the step portion of the row, so we append a#
to theline
. else
: Ifcol > row
, we are in the space portion of the row (to the right of the step), so we append a space" "
to theline
.
line += ...
: We append either#
or" "
to theline
string in each iteration of the inner loop.
-
Console Logging Each Row:
console.log(line)
: After the inner loop completes (we have built theline
string for the current row), weconsole.log(line)
to print the entire row to the console.
This nested loop structure combined with the col <= row
condition effectively generates the step shape pattern, placing #
characters to form the step and spaces to fill in the right side.
10. Pyramid Shape Pattern
Problem: Write a function that accepts a positive number n
and console logs a pyramid shape with n
levels using the pound character (#
). The pyramid should have spaces on both the left and right-hand sides.
Examples (n = 3):
#
###
#####
10.1 Nested Loop Approach with Midpoint Calculation
Creating a pyramid shape with spaces on both sides requires a slightly more complex nested loop logic. We’ll use a midpoint calculation to center the pyramid.
function pyramidShape(n) {
const midpoint = Math.floor((2 * n - 1) / 2);
for (let row = 0; row < n; row++) {
let line = '';
for (let col = 0; col < 2 * n - 1; col++) {
if (col >= midpoint - row && col <= midpoint + row) {
line += '#';
} else {
line += ' ';
}
}
console.log(line);
}
}
pyramidShape(3); // Runs pyramidShape for n = 3
Let’s break down the pyramidShape
function:
-
Midpoint Calculation:
const midpoint = Math.floor((2 * n - 1) / 2)
:(2 * n - 1)
: Calculates the total number of columns needed for the widest row of the pyramid (bottom row). For a pyramid of heightn
, the bottom row has2n - 1
characters./ 2
: Divides the total columns by 2 to find the middle column index (approximately).Math.floor(...)
: Rounds down to the nearest integer to get the integer index of the midpoint column.
-
Outer Loop (Rows):
for (let row = 0; row < n; row++)
: The outer loop iteratesn
times, representing each row (level) of the pyramid.
-
Inner Loop (Columns) and Character Building:
let line = '';
: Initializes an empty stringline
for each row.for (let col = 0; col < 2 * n - 1; col++)
: The inner loop iterates2n - 1
times, covering all column positions in each row.if (col >= midpoint - row && col <= midpoint + row)
: This is the key condition for placing#
characters to form the pyramid.midpoint - row
: Calculates the starting column index for#
characters in the currentrow
. Asrow
increases, this starting index moves to the left, widening the pyramid base.midpoint + row
: Calculates the ending column index for#
characters in the currentrow
. Asrow
increases, this ending index moves to the right, also widening the base.col >= midpoint - row && col <= midpoint + row
: Checks if the current column indexcol
falls within the range defined by the starting and ending column indices.- If
true
, it means we are within the pyramid shape for the current row, so we append a#
toline
. else
: Ifcol
is outside the pyramid range, we append a space" "
toline
.
-
Console Logging Each Row:
console.log(line)
: Prints the completedline
string for the current row, forming the pyramid level.
This nested loop structure, combined with the midpoint calculation and the col >= midpoint - row && col <= midpoint + row
condition, accurately generates the pyramid shape with spaces on both sides, centering the pyramid within the allocated column width.
11. Spiral Matrix
Problem: Write a function that accepts an integer n
and returns an n
x n
spiral matrix.
Examples (n = 3):
[[1, 2, 3],
[8, 9, 4],
[7, 6, 5]]
This is a more complex problem involving matrix manipulation and requires a systematic approach to filling the matrix in a spiral pattern.
11.1 Layer-by-Layer Filling Approach
We can fill the spiral matrix layer by layer, moving in a clockwise spiral direction. We’ll use variables to track the boundaries of the current layer and loops to fill each side of the layer.
function spiralMatrix(n) {
const result = [];
for (let i = 0; i < n; i++) {
result.push([]); // Initialize n x n matrix with empty arrays
}
let counter = 1;
let startRow = 0;
let endRow = n - 1;
let startCol = 0;
let endCol = n - 1;
while (startRow <= endRow && startCol <= endCol) {
// Top row (left to right)
for (let i = startCol; i <= endCol; i++) {
result[startRow][i] = counter;
counter++;
}
startRow++; // Move to the next row
// Right column (top to bottom)
for (let i = startRow; i <= endRow; i++) {
result[i][endCol] = counter;
counter++;
}
endCol--; // Move to the previous column
// Bottom row (right to left)
if (startRow <= endRow) { // Prevent duplicate row in odd n cases
for (let i = endCol; i >= startCol; i--) {
result[endRow][i] = counter;
counter++;
}
endRow--; // Move to the previous row
}
// Left column (bottom to top)
if (startCol <= endCol) { // Prevent duplicate column in odd n cases
for (let i = endRow; i >= startRow; i--) {
result[i][startCol] = counter;
counter++;
}
startCol++; // Move to the next column
}
}
return result;
}
console.log(spiralMatrix(3));
// Output: [[1, 2, 3], [8, 9, 4], [7, 6, 5]]
console.log(spiralMatrix(4));
// Output: [[1, 2, 3, 4], [12, 13, 14, 5], [11, 16, 15, 6], [10, 9, 8, 7]]
Let’s dissect the spiralMatrix
function:
-
Matrix Initialization:
const result = []
: Creates an empty arrayresult
to represent the matrix.for (let i = 0; i < n; i++) { result.push([]); }
: Initializesresult
as ann
xn
matrix by pushingn
empty arrays (rows) into it.
-
Boundary and Counter Variables:
counter = 1
: Initializes acounter
variable to 1. This will be used to fill the matrix with increasing numbers in a spiral.startRow = 0
,endRow = n - 1
,startCol = 0
,endCol = n - 1
: These variables define the boundaries of the current layer of the spiral we are filling. They initially encompass the entire matrix.
-
while
Loop for Layer Processing:while (startRow <= endRow && startCol <= endCol)
: Thewhile
loop continues as long as thestartRow
is less than or equal toendRow
andstartCol
is less than or equal toendCol
. This condition ensures we process all layers of the spiral, moving inwards.
-
Four Loops for Each Side of a Layer:
-
Top Row (Left to Right):
for (let i = startCol; i <= endCol; i++)
: Iterates through columns fromstartCol
toendCol
in the currentstartRow
(top row).result[startRow][i] = counter
: Fills the matrix elements in the top row with increasingcounter
values.counter++
: Increments thecounter
.startRow++
: After filling the top row, we incrementstartRow
to move the top boundary down for the next inner layer.
-
Right Column (Top to Bottom):
for (let i = startRow; i <= endRow; i++)
: Iterates through rows fromstartRow
toendRow
in the currentendCol
(right column).result[i][endCol] = counter
: Fills the matrix elements in the right column with increasingcounter
values.counter++
: Increments thecounter
.endCol--
: After filling the right column, we decrementendCol
to move the right boundary left for the next inner layer.
-
Bottom Row (Right to Left):
if (startRow <= endRow)
: This condition prevents processing a duplicate row in cases wheren
is odd. IfstartRow
becomes greater thanendRow
, it means we have reached the center row in an odd-sized matrix, and we should not process the bottom row again.for (let i = endCol; i >= startCol; i--)
: Iterates through columns fromendCol
down tostartCol
in the currentendRow
(bottom row).result[endRow][i] = counter
: Fills the matrix elements in the bottom row with increasingcounter
values.counter++
: Increments thecounter
.endRow--
: After filling the bottom row, we decrementendRow
to move the bottom boundary up for the next inner layer.
-
Left Column (Bottom to Top):
if (startCol <= endCol)
: Similar to the bottom row condition, this prevents processing a duplicate column in oddn
cases.for (let i = endRow; i >= startRow; i--)
: Iterates through rows fromendRow
down tostartRow
in the currentstartCol
(left column).result[i][startCol] = counter
: Fills the matrix elements in the left column with increasingcounter
values.counter++
: Increments thecounter
.startCol++
: After filling the left column, we incrementstartCol
to move the left boundary right for the next inner layer.
-
-
Returning the Spiral Matrix:
return result
: After thewhile
loop completes (all layers are filled), we return theresult
matrix, which now contains the spiral pattern.
This layer-by-layer filling approach, using boundary variables and four loops per layer, systematically constructs the spiral matrix. The while
loop ensures that we process all layers until we reach the center of the matrix.
This concludes our exploration of ten essential JavaScript algorithm problems for beginners. By mastering these concepts and practicing similar challenges, you will build a strong foundation in algorithms and be well-prepared for coding interviews and more advanced programming tasks. Remember to explore the linked LeetCode playlist for further practice and more challenging problems!