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
forloop iterates from the first character to the last character of the input stringstr. - In each iteration, we prepend the current character
str[i]to thereversedstring. - Finally, we return the
reversedstring.
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 integerninto 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 ifnis positive, -1 ifnis negative, and 0 ifnis 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
strusingsplit('').reverse().join('')and store it inreversedStr. - We then directly compare the original string
strwith 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 (
charMapObject):- We initialize an empty object
charMapto store character counts. - We iterate through each character
charin the input stringstrusing afor...ofloop. - For each character:
charMap[char] = charMap[char] + 1 || 1;This line efficiently updates the count in thecharMap.charMap[char] + 1: If the charactercharalready 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
maxCountto 0 andmaxCharto an empty string to track the highest count and corresponding character. -
We iterate through the keys (characters) of the
charMapobject using afor...inloop.for...inloop: This JavaScript loop iterates over all enumerable string properties of an object. -
For each character
charincharMap:if (charMap[char] > maxCount): We check if the count of the current charactercharMap[char]is greater than the currentmaxCount.- If it is, we update
maxCountto the new higher count andmaxCharto 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: TheMapobject 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:2Output:[[1, 2], [3, 4], [5]] - Array:
[1, 2, 3, 4, 5, 6, 7, 8], Chunk Size:3Output:[[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 arraychunkedArrayto store the resulting subarrays (chunks).index = 0: We initialize anindexvariable to 0. Thisindexwill track our current position in the originalarray.
-
whileLoop for Chunking:-
while (index < array.length): Thewhileloop continues as long as ourindexis 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 thearraystarting fromindexup 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 theindexbysize. This moves our starting position in the originalarrayforward by the chunk size, preparing for the extraction of the next chunk.
-
-
Returning the Chunked Array:
return chunkedArray: After thewhileloop completes (we’ve processed the entirearray), we return thechunkedArraycontaining 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 stringstrinto an array of words, using a space (” ”) as the delimiter.
-
Capitalizing Each Word:
-
capitalizedWords = []: We initialize an empty arraycapitalizedWordsto store the capitalized words. -
for (const word of words): We iterate through eachwordin thewordsarray. -
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 thecapitalizedWordsarray.
-
-
-
Joining the Capitalized Words Back into a String:
return capitalizedWords.join(' '): We use thejoin(' ')method to join the words in thecapitalizedWordsarray 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 eachwordin the array.word => ...: This is an arrow function that takes eachwordas input.word.charAt(0).toUpperCase() + word.slice(1): This is the same logic we used in thefor...ofloop 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 objectcharMapto 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 forstringAusingbuildCharMap. -
const charMapB = buildCharMap(stringB): Creates a character map forstringBusingbuildCharMap. -
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 incharMapAis 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 callscleanStringon bothstringAandstringBto 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: Ifmatchesis truthy (an array of matches), we return thelengthof the array, which is the count of vowels.0: Ifmatch()finds no vowels, it returnsnull. In a conditional context,nullis falsy. Ifmatchesis 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 stringvowelSetcontaining all lowercase vowels.let count = 0: Initializes acountvariable 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 charactercharis present in thevowelSetstring.includes(): This JavaScript string method determines whether one string may be found within another string, returningtrueorfalseas 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:
-
forLoop for Numbers 1 ton:for (let i = 1; i <= n; i++): Aforloop iterates through numbers from 1 ton(inclusive).
-
Conditional Logic (if/else if/else):
if (i % 3 === 0 && i % 5 === 0): Checks if the current numberiis divisible by both 3 and 5.-
i % 3 === 0: Checks ifiis a multiple of 3 using the modulo operator.Modulo Operator (%): The modulo operator returns the remainder of a division. If
a % b === 0, it meansais perfectly divisible bybwith no remainder. -
i % 5 === 0: Checks ifiis a multiple of 5. -
&&: Logical AND operator. The condition is true only if bothi % 3 === 0andi % 5 === 0are true. -
If true, it
console.log("FizzBuzz").
-
else if (i % 3 === 0): If the previous condition is false, this checks ifiis 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 ifiis 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 outerforloop iteratesntimes, representing each row (level) of the step shape.rowgoes from 0 ton-1.
-
Inner Loop (Columns) and Character Building:
let line = '';: Inside the outer loop, we initialize an empty stringlinefor each row. This string will store the characters for the current row.for (let col = 0; col < n; col++): The innerforloop also iteratesntimes, representing each column position within a row.colalso 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 indexcolis 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 thelinestring in each iteration of the inner loop.
-
Console Logging Each Row:
console.log(line): After the inner loop completes (we have built thelinestring 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 - 1characters./ 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 iteratesntimes, representing each row (level) of the pyramid.
-
Inner Loop (Columns) and Character Building:
let line = '';: Initializes an empty stringlinefor each row.for (let col = 0; col < 2 * n - 1; col++): The inner loop iterates2n - 1times, 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. Asrowincreases, this starting index moves to the left, widening the pyramid base.midpoint + row: Calculates the ending column index for#characters in the currentrow. Asrowincreases, this ending index moves to the right, also widening the base.col >= midpoint - row && col <= midpoint + row: Checks if the current column indexcolfalls 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: Ifcolis outside the pyramid range, we append a space" "toline.
-
Console Logging Each Row:
console.log(line): Prints the completedlinestring 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 arrayresultto represent the matrix.for (let i = 0; i < n; i++) { result.push([]); }: Initializesresultas annxnmatrix by pushingnempty arrays (rows) into it.
-
Boundary and Counter Variables:
counter = 1: Initializes acountervariable 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.
-
whileLoop for Layer Processing:while (startRow <= endRow && startCol <= endCol): Thewhileloop continues as long as thestartRowis less than or equal toendRowandstartColis 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 fromstartColtoendColin the currentstartRow(top row).result[startRow][i] = counter: Fills the matrix elements in the top row with increasingcountervalues.counter++: Increments thecounter.startRow++: After filling the top row, we incrementstartRowto 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 fromstartRowtoendRowin the currentendCol(right column).result[i][endCol] = counter: Fills the matrix elements in the right column with increasingcountervalues.counter++: Increments thecounter.endCol--: After filling the right column, we decrementendColto 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 wherenis odd. IfstartRowbecomes 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 fromendColdown tostartColin the currentendRow(bottom row).result[endRow][i] = counter: Fills the matrix elements in the bottom row with increasingcountervalues.counter++: Increments thecounter.endRow--: After filling the bottom row, we decrementendRowto 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 oddncases.for (let i = endRow; i >= startRow; i--): Iterates through rows fromendRowdown tostartRowin the currentstartCol(left column).result[i][startCol] = counter: Fills the matrix elements in the left column with increasingcountervalues.counter++: Increments thecounter.startCol++: After filling the left column, we incrementstartColto move the left boundary right for the next inner layer.
-
-
Returning the Spiral Matrix:
return result: After thewhileloop completes (all layers are filled), we return theresultmatrix, 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!