Interview

15 JavaScript Algorithm Interview Questions and Answers

Prepare for your next tech interview with our guide on JavaScript algorithms, featuring curated questions to enhance your problem-solving skills.

JavaScript is a cornerstone of modern web development, enabling dynamic and interactive user experiences. Its versatility extends beyond the browser, finding applications in server-side development with Node.js, mobile app development, and even desktop applications. Mastery of JavaScript algorithms is crucial for optimizing performance and solving complex problems efficiently, making it a highly sought-after skill in the tech industry.

This article offers a curated selection of JavaScript algorithm questions designed to challenge and refine your problem-solving abilities. By working through these examples, you’ll gain a deeper understanding of algorithmic concepts and be better prepared to tackle technical interviews with confidence.

JavaScript Algorithm Interview Questions and Answers

1. Write a function to find the second largest number in an array.

To find the second largest number in an array, iterate through the array while keeping track of the largest and second largest numbers. This ensures efficient traversal.

Example:

function findSecondLargest(arr) {
    let first = -Infinity, second = -Infinity;

    for (let num of arr) {
        if (num > first) {
            second = first;
            first = num;
        } else if (num > second && num < first) {
            second = num;
        }
    }

    return second;
}

console.log(findSecondLargest([10, 5, 8, 12, 7])); // Output: 10
console.log(findSecondLargest([4, 4, 4, 4, 4])); // Output: -Infinity (or handle as needed)

2. Determine if a given string is a palindrome.

A palindrome reads the same forward and backward, ignoring spaces, punctuation, and capitalization. To check if a string is a palindrome:

  • Normalize the string by removing non-alphanumeric characters and converting it to lowercase.
  • Compare the normalized string to its reverse.

Example:

function isPalindrome(str) {
    const normalizedStr = str.replace(/[^A-Za-z0-9]/g, '').toLowerCase();
    return normalizedStr === normalizedStr.split('').reverse().join('');
}

// Example usage
console.log(isPalindrome("A man, a plan, a canal, Panama")); // true
console.log(isPalindrome("race a car")); // false

3. Implement a recursive function to calculate the factorial of a number.

Recursion involves a function calling itself to solve a problem. Calculating the factorial of a number is a classic example. The factorial of n (n!) is the product of all positive integers up to n. Define a base case for n = 0 or 1, where the factorial is 1, and a recursive case for n * factorial(n-1).

Example:

function factorial(n) {
    if (n === 0 || n === 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

console.log(factorial(5)); // Output: 120

4. Write a function to perform bubble sort on an array.

Bubble sort repeatedly steps through the list, comparing and swapping adjacent items if needed, until the list is sorted.

Example:

function bubbleSort(arr) {
    let n = arr.length;
    for (let i = 0; i < n - 1; i++) {
        for (let j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                let temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}

console.log(bubbleSort([64, 34, 25, 12, 22, 11, 90]));
// Output: [11, 12, 22, 25, 34, 64, 90]

5. Implement a function to find the index of a target value in a sorted array using binary search.

Binary search efficiently finds the index of a target value in a sorted array by repeatedly dividing the search interval in half.

Example:

function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;

    while (left <= right) {
        const mid = Math.floor((left + right) / 2);

        if (arr[mid] === target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    return -1; // Target not found
}

const sortedArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const targetValue = 5;
console.log(binarySearch(sortedArray, targetValue)); // Output: 4

6. Implement a function to find the longest increasing subsequence in an array.

The longest increasing subsequence (LIS) in an array is a subsequence of elements in increasing order. Use dynamic programming to find the LIS length by maintaining an array dp where dp[i] represents the LIS length ending at index i.

function lengthOfLIS(nums) {
    if (nums.length === 0) return 0;

    let dp = new Array(nums.length).fill(1);

    for (let i = 1; i < nums.length; i++) {
        for (let j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
    }

    return Math.max(...dp);
}

// Example usage:
console.log(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])); // Output: 4

7. Implement a function to traverse a binary tree in in-order.

In-order traversal of a binary tree visits nodes in the order: left subtree, root node, right subtree. This retrieves binary search tree values in ascending order.

Example:

class TreeNode {
    constructor(value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

function inOrderTraversal(node, result = []) {
    if (node !== null) {
        inOrderTraversal(node.left, result);
        result.push(node.value);
        inOrderTraversal(node.right, result);
    }
    return result;
}

// Example usage:
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);

console.log(inOrderTraversal(root)); // Output: [4, 2, 5, 1, 3]

8. Write a function to calculate the nth Fibonacci number using memoization.

Memoization optimizes recursive algorithms by storing and reusing results of expensive function calls. This is useful for calculating the nth Fibonacci number, as the naive recursive approach has exponential time complexity.

Example:

function fibonacci(n, memo = {}) {
    if (n <= 1) return n;
    if (memo[n]) return memo[n];
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
    return memo[n];
}

console.log(fibonacci(10)); // Output: 55
console.log(fibonacci(50)); // Output: 12586269025

9. Implement a function to solve the N-Queens problem.

The N-Queens problem can be solved using backtracking. Place queens one by one in different columns, checking for conflicts with already placed queens. If a conflict is found, backtrack and try the next position.

function solveNQueens(n) {
    const solutions = [];
    const board = Array.from({ length: n }, () => Array(n).fill('.'));

    function isSafe(row, col) {
        for (let i = 0; i < col; i++) {
            if (board[row][i] === 'Q') return false;
        }
        for (let i = row, j = col; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] === 'Q') return false;
        }
        for (let i = row, j = col; i < n && j >= 0; i++, j--) {
            if (board[i][j] === 'Q') return false;
        }
        return true;
    }

    function solve(col) {
        if (col === n) {
            solutions.push(board.map(row => row.join('')));
            return;
        }
        for (let i = 0; i < n; i++) {
            if (isSafe(i, col)) {
                board[i][col] = 'Q';
                solve(col + 1);
                board[i][col] = '.';
            }
        }
    }

    solve(0);
    return solutions;
}

console.log(solveNQueens(4));

10. Implement the Knuth-Morris-Pratt (KMP) string matching algorithm.

The KMP algorithm consists of preprocessing the pattern to create the longest prefix suffix (LPS) array and using this LPS array to perform the search.

function computeLPSArray(pattern) {
    const lps = new Array(pattern.length).fill(0);
    let length = 0;
    let i = 1;

    while (i < pattern.length) {
        if (pattern[i] === pattern[length]) {
            length++;
            lps[i] = length;
            i++;
        } else {
            if (length !== 0) {
                length = lps[length - 1];
            } else {
                lps[i] = 0;
                i++;
            }
        }
    }
    return lps;
}

function KMPSearch(text, pattern) {
    const lps = computeLPSArray(pattern);
    let i = 0; // index for text
    let j = 0; // index for pattern

    while (i < text.length) {
        if (pattern[j] === text[i]) {
            i++;
            j++;
        }

        if (j === pattern.length) {
            console.log("Found pattern at index " + (i - j));
            j = lps[j - 1];
        } else if (i < text.length && pattern[j] !== text[i]) {
            if (j !== 0) {
                j = lps[j - 1];
            } else {
                i++;
            }
        }
    }
}

// Example usage
KMPSearch("abxabcabcaby", "abcaby");

11. Write a function to optimize the performance of finding the maximum subarray sum using Kadane’s Algorithm.

Kadane’s Algorithm finds the maximum sum of a contiguous subarray within a numeric array. It iterates through the array, tracking the maximum sum encountered and the current subarray sum, ensuring a solution in O(n) time complexity.

Example:

function maxSubArraySum(arr) {
    let maxSoFar = arr[0];
    let maxEndingHere = arr[0];

    for (let i = 1; i < arr.length; i++) {
        maxEndingHere = Math.max(arr[i], maxEndingHere + arr[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }

    return maxSoFar;
}

// Example usage:
const array = [-2, 1, -3, 4, -1, 2, 1, -5, 4];
console.log(maxSubArraySum(array)); // Output: 6

12. Implement a function to detect cycles in a directed graph using Depth-First Search (DFS).

To detect cycles in a directed graph using Depth-First Search (DFS), traverse the graph and track nodes in the recursion stack. If a node is already in the stack, a cycle is present.

Example:

function hasCycle(graph) {
    const visited = new Set();
    const recStack = new Set();

    function dfs(node) {
        if (recStack.has(node)) return true;
        if (visited.has(node)) return false;

        visited.add(node);
        recStack.add(node);

        for (let neighbor of graph[node]) {
            if (dfs(neighbor)) return true;
        }

        recStack.delete(node);
        return false;
    }

    for (let node in graph) {
        if (dfs(node)) return true;
    }

    return false;
}

// Example usage:
const graph = {
    0: [1],
    1: [2],
    2: [0],
    3: [4],
    4: []
};

console.log(hasCycle(graph)); // Output: true

13. Write a function to perform breadth-first search (BFS) on a graph.

Breadth-first search (BFS) traverses or searches tree or graph data structures, exploring neighbor nodes at the present depth before moving to the next depth level. BFS uses a queue to track nodes to be explored.

Example:

function bfs(graph, startNode) {
    let visited = new Set();
    let queue = [startNode];

    while (queue.length > 0) {
        let node = queue.shift();

        if (!visited.has(node)) {
            console.log(node);
            visited.add(node);

            let neighbors = graph[node];
            for (let neighbor of neighbors) {
                if (!visited.has(neighbor)) {
                    queue.push(neighbor);
                }
            }
        }
    }
}

const graph = {
    0: [1, 2],
    1: [0, 3, 4],
    2: [0, 5, 6],
    3: [1],
    4: [1],
    5: [2],
    6: [2]
};

bfs(graph, 0);

14. Implement a dynamic programming solution to the coin change problem.

Dynamic programming solves complex problems by breaking them into simpler subproblems. The coin change problem is a classic example where dynamic programming finds the minimum number of coins needed to make a given amount.

Example:

function coinChange(coins, amount) {
    const dp = Array(amount + 1).fill(Infinity);
    dp[0] = 0;

    for (let coin of coins) {
        for (let i = coin; i <= amount; i++) {
            dp[i] = Math.min(dp[i], dp[i - coin] + 1);
        }
    }

    return dp[amount] === Infinity ? -1 : dp[amount];
}

console.log(coinChange([1, 2, 5], 11)); // Output: 3
console.log(coinChange([2], 3)); // Output: -1

15. Write a function to merge two sorted arrays.

To merge two sorted arrays, use a two-pointer technique. Iterate through both arrays, comparing elements, and append the smaller element to the result array to maintain sorted order.

function mergeSortedArrays(arr1, arr2) {
    let mergedArray = [];
    let i = 0, j = 0;

    while (i < arr1.length && j < arr2.length) {
        if (arr1[i] < arr2[j]) {
            mergedArray.push(arr1[i]);
            i++;
        } else {
            mergedArray.push(arr2[j]);
            j++;
        }
    }

    while (i < arr1.length) {
        mergedArray.push(arr1[i]);
        i++;
    }

    while (j < arr2.length) {
        mergedArray.push(arr2[j]);
        j++;
    }

    return mergedArray;
}

// Example usage:
let array1 = [1, 3, 5];
let array2 = [2, 4, 6];
console.log(mergeSortedArrays(array1, array2)); // Output: [1, 2, 3, 4, 5, 6]
Previous

30 TypeScript Interview Questions and Answers

Back to Interview
Next

20 DataStage Interview Questions and Answers