JavaScript Coding Test Course, Exploring a Maze

Hello! In this course, we will provide an in-depth explanation of an algorithm problem using JavaScript, specifically “Maze Exploration.” This is one of the frequently appearing problem types in coding tests, where the goal is to find a path from the starting point to the destination in a given maze. This problem can be solved using graph search algorithms such as BFS (Breadth-First Search) or DFS (Depth-First Search).

Problem Description

Problem: Check if there is a path from the starting point (start) to the destination (end) in a given 2D array representing a maze. The path in the maze can follow ‘0’ (a traversable space), while ‘1’ (an obstacle) cannot be passed.

For example, let’s assume the maze is as follows:

[
    [0, 1, 0, 0, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 1, 0],
    [0, 1, 0, 0, 0],
    [0, 0, 0, 1, 0]
]

The starting point is (0, 0) and the destination is (4, 4). In other words, we need to determine whether there exists a path from (0, 0) to (4, 4) while exploring the maze.

Input and Output

  • Input: 2D array (maze), starting point (start), destination (end)
  • Output: Whether a path exists (true/false)

Approach to Problem Solving

To solve this problem, we can use BFS (Breadth-First Search) or DFS (Depth-First Search) algorithms. BFS is suitable for finding the shortest path, but since we only need to check for the existence of a path in this problem, we can also solve it using DFS.

Algorithm Explanation

1. **Basic Setup**: The following process is needed to explore the maze.

  1. Add all nodes to be explored to the stack (for DFS).
  2. Mark explored nodes as visited to prevent duplicate exploration.
  3. Move in all possible directions (up, down, left, right) from the current position.
  4. If the target position is reached, return true.
  5. If all paths have been explored and the target is not reached, return false.

JavaScript Implementation

Now, let’s implement the above algorithm in JavaScript. Below is the specific code for the DFS algorithm for maze exploration:


function isPathExist(maze, start, end) {
    const rows = maze.length;
    const cols = maze[0].length;

    // Movement direction array (up, down, left, right)
    const directions = [
        [-1, 0], // up
        [1, 0],  // down
        [0, -1], // left
        [0, 1]   // right
    ];

    // Initialize stack and visited array
    const stack = [start];
    const visited = Array.from({ length: rows }, () => Array(cols).fill(false));
    visited[start[0]][start[1]] = true;

    // DFS exploration
    while (stack.length > 0) {
        const [x, y] = stack.pop();

        // If the destination is reached
        if (x === end[0] && y === end[1]) {
            return true;
        }

        // Move in each direction
        for (const [dx, dy] of directions) {
            const newX = x + dx;
            const newY = y + dy;

            // Check range and visit status
            if (newX >= 0 && newX < rows && newY >= 0 && newY < cols &&
                maze[newX][newY] === 0 && !visited[newX][newY]) {
                visited[newX][newY] = true;
                stack.push([newX, newY]);
            }
        }
    }

    // If unreachable
    return false;
}

// Test
const maze = [
    [0, 1, 0, 0, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 1, 0],
    [0, 1, 0, 0, 0],
    [0, 0, 0, 1, 0]
];
console.log(isPathExist(maze, [0, 0], [4, 4])); // true

Code Explanation

The above code implements the process of finding a path from the starting point to the destination in a given maze using DFS (Depth-First Search). The main steps are as follows:

  1. Initial Setup: First, initialize the number of rows and columns in the maze and set the movement directions in an array. The directions we can move are up, down, left, and right.
  2. Initialize Stack and Visited Array: Use a stack to explore paths for DFS, marking visited positions as 'true'.
  3. DFS Iteration: Pop a position from the stack to get the current position and check if it is the destination. If the destination is reached, return true.
  4. Check Move Possibility: Check all directions to confirm if the new position is within range, has no obstacles, and hasn’t been visited before adding it to the stack.

Performance Analysis

The time complexity of this algorithm is O(V+E), where V is the number of vertices (i.e., all positions in the maze) and E is the number of edges (i.e., the number of possible movements from each position). In the worst case, we need to explore all positions, which is why this complexity is necessary. The space complexity is O(V) as space is required for the visited array.

Conclusion

In this lecture, we discussed how to solve the maze exploration problem using JavaScript. We learned basic code and techniques to check the existence of a path through the maze using the DFS algorithm. These types of problems frequently appear in coding tests, so practice various modified problems to build your confidence.

In the next lecture, we will cover more complex maze exploration problems or other types of algorithm problems, so please look forward to it!

JavaScript Coding Test Course, Sort Digits in Descending Order

Problem Definition

Resolve the following problem. You need to implement a function that sorts the given integer in descending order and returns those digits as a single integer. For example, if the input is 42145, it should return 54421.

Input

  • A single integer n (0 ≤ n ≤ 1,000,000,000)

Output

  • An integer sorted in descending order

Approach

To solve the problem, follow the steps below:

  1. Convert the given integer to a string.
  2. Convert the string to an array, containing each digit in the array.
  3. Sort the array in descending order.
  4. Combine the sorted array back into a string, then convert it to an integer and return it.

Code Implementation

Below is an example of how the above approach is implemented in code:


function sortDigitsDescending(n) {
    // Step 1: Convert the integer to a string
    const strNum = n.toString();
    
    // Step 2: Convert the string to an array
    const digitsArray = strNum.split('');
    
    // Step 3: Sort the array in descending order
    digitsArray.sort((a, b) => b - a);
    
    // Step 4: Combine the sorted array into a string and convert to integer
    const sortedNumber = parseInt(digitsArray.join(''), 10);
    
    return sortedNumber;
}

Code Explanation

The code above works as follows:

  • The function sortDigitsDescending(n) takes an integer n as a parameter.
  • It uses the toString() method to convert the number to a string.
  • The split('') method separates each digit of the string into an array.
  • The sort() method sorts the elements of the array in descending order. It compares each digit by converting them to numbers.
  • The join('') method combines the sorted array back into a single string, then parseInt() converts it to an integer and returns it.

Test Cases

Now it is necessary to validate the function with various test cases:


console.log(sortDigitsDescending(42145)); // 54421
console.log(sortDigitsDescending(123456789)); // 987654321
console.log(sortDigitsDescending(0)); // 0
console.log(sortDigitsDescending(10000)); // 10000
console.log(sortDigitsDescending(9876543210)); // 9876543210

Performance Considerations

This algorithm has a time complexity of O(n log n) depending on the length of the input. Here, n is the number of digits. During the sorting of the digits, JavaScript’s internal sorting algorithm is utilized, which guarantees O(n log n) performance in the worst case.

Conclusion

We have successfully implemented an algorithm to sort the digits of a given integer in descending order. In this process, we utilized JavaScript’s string and array methods to solve the problem simply and efficiently. An important aspect of solving algorithmic problems is to break down the problem into smaller parts and make the code writing clear at each stage. As you tackle various algorithmic problems, try to apply this approach.

JavaScript Coding Test Course, Finding Prime Numbers

Hello! Today we will discuss the ‘Finding Prime Numbers’ problem, which frequently appears in JavaScript coding tests. A prime number is a natural number greater than 1 that cannot be formed by multiplying two smaller natural numbers. For example, 2, 3, 5, 7, and 11 are all prime numbers. In this article, we will implement an algorithm to find prime numbers and explain the process in detail.

1. Problem Definition

Given an integer N, write a program that outputs all prime numbers less than or equal to N.

Input: An integer N (1 ≤ N ≤ 10,000)

Output: Print all prime numbers less than or equal to N in a single line

2. Algorithm Selection

The representative algorithm to find prime numbers is the Sieve of Eratosthenes. This algorithm is very efficient and suitable for finding prime numbers less than or equal to N. The reasons for using this algorithm are as follows:

  • It has a time complexity of O(N log log N), making it very fast.
  • The memory usage is moderate, allowing it to handle up to N = 10,000 smoothly.

2.1 Explanation of the Sieve of Eratosthenes Algorithm

The Sieve of Eratosthenes algorithm proceeds through the following steps:

  1. Add all numbers from 2 to N to a list.
  2. Confirm the first number in the list, which is 2, as a prime. Remove all multiples of 2 from the list.
  3. Confirm the next number (3) as a prime and remove all multiples of 3. Repeat this process.
  4. Continue only while the current number is less than or equal to the square root of N.

3. Writing Code

Now, let’s write the code in JavaScript based on the above algorithm.

function sieveOfEratosthenes(N) {
    const primes = [];
    const isPrime = new Array(N + 1).fill(true);
    isPrime[0] = isPrime[1] = false; // 0 and 1 are not prime numbers.

    for (let i = 2; i <= N; i++) {
        if (isPrime[i]) {
            primes.push(i); // i is a prime number.
            for (let j = i * 2; j <= N; j += i) {
                isPrime[j] = false; // Multiples of i are not prime numbers.
            }
        }
    }
    return primes;
}

// Example of usage
const N = 100; // Input N.
const primeNumbers = sieveOfEratosthenes(N);
console.log(primeNumbers.join(' ')); // Output the prime numbers.

4. Code Analysis

Let’s take a look at the written code step by step:

  • const isPrime = new Array(N + 1).fill(true);: This creates an array of numbers up to N and initializes all values to true.
  • isPrime[0] = isPrime[1] = false;: Since 0 and 1 are not prime, they are set to false.
  • The for loop checks the numbers from 2 to N. If isPrime[i] is true, it means i is a prime number. Add this number to the primes array.
  • Also, iterate through all multiples of i and set them to false.
  • Through this process, the final array containing only prime numbers is returned.

5. Test Cases

Now, let’s run some test cases to verify that our implementation works well.

console.log(sieveOfEratosthenes(10)); // [2, 3, 5, 7]
console.log(sieveOfEratosthenes(50)); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
console.log(sieveOfEratosthenes(100)); // [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

6. Conclusion

Today we learned how to find prime numbers in JavaScript. We explored an efficient method of locating primes using the Sieve of Eratosthenes, and based on that, we wrote practical code. I hope this code helps enhance your algorithm skills further. Additionally, I hope it aids you in preparing for coding tests!

7. Additional Learning Resources

If you want to see more materials and solve problems, please refer to the following resources:

  • LeetCode – Various algorithm problems and solutions
  • HackerRank – Coding test problems and practices
  • Codewars – Coding practice by solving problems in various languages

JavaScript Coding Test Course, Finding Least Common Ancestor 2

Problem Description

This problem involves finding the Lowest Common Ancestor (LCA) of two nodes.
Given a binary tree, the goal is to find the common ancestor of two specified nodes.

Input Format

  • The root node of the binary tree is given.
  • Two node values are provided.

Output Format

  • The value of the Lowest Common Ancestor of the given two nodes, or -1 if it does not exist.

Example Problems

    Input:
        1
       / \
      2   3
     / \
    4   5

    The LCA of nodes 4 and 5 is 2.
  
    Input:
        1
       / \
      2   3
     / \
    4   5

    The LCA of nodes 4 and 3 is 1.

    Input:
        1
       / \
      2   3
     / \
    4   5

    The LCA of nodes 6 and 7 is -1.
    

Problem Solution

To solve this problem, we approach it with the following steps:

  1. Define the binary tree structure:
    First, we define a node class to represent the structure of the tree. This class has a value for the node,
    and properties that reference left and right child nodes.
  2. Recursive Approach:
    To find the lowest common ancestor, we recursively traverse the tree.
    If the current node corresponds to ${p} or ${q}, we return the current node.
  3. Check Left and Right Subtrees:
    We find the LCA in both the left and right subtrees, and if both results exist,
    the current node is the LCA.
  4. Return Result:
    If both nodes are found, return the current node; otherwise, return null.

JavaScript Code Implementation


// Define a binary tree node class
class TreeNode {
    constructor(value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

// Function to find the lowest common ancestor
function findLCA(root, p, q) {
    // Base case: when the current node is null
    if (root === null) {
        return null;
    }
    
    // If the current node is p or q, return the current node
    if (root.value === p || root.value === q) {
        return root;
    }
    
    // Find LCA in left and right subtrees
    const leftLCA = findLCA(root.left, p, q);
    const rightLCA = findLCA(root.right, p, q);
    
    // If results exist in both left and right, the current node is LCA
    if (leftLCA && rightLCA) {
        return root;
    }
    
    // Return the result found in one of the subtrees
    return leftLCA !== null ? leftLCA : rightLCA;
}

// 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);

const node1 = 4;
const node2 = 5;
const lca = findLCA(root, node1, node2);
console.log(lca ? lca.value : -1); // Output: 2
    

Conclusion

Finding the lowest common ancestor is an important search problem in binary trees.
It can be solved efficiently through a recursive approach for various tree structures and nodes.
This method is useful in many situations and will greatly aid in understanding recursive thinking and tree traversal.

Additional Tasks

Try to solve the following additional tasks!

  • Add logic to handle the case where the given nodes do not exist.
  • Research and implement an optimized method to find the LCA in a binary search tree.
  • Also, implement a function to visualize various tree structures.

References

Javascript Coding Test Course, Finding the Largest Square

Hello! In this article, we will take a detailed look at how to solve the “Maximum Square” problem using JavaScript. This problem can be efficiently solved using a Dynamic Programming approach. It is a type of problem frequently encountered in coding tests at actual companies, so by solving this problem, we will solidify our understanding of the concept of dynamic programming and explore its application in real-world scenarios.

Problem Description

Given a 2-dimensional array matrix, the problem is to find the length of the side of the largest square made up entirely of ‘1’s. For example, if the input matrix is as follows:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

In this matrix, the length of the side of the largest square is 2. In other words, the maximum size of ‘1’s is 2×2.

Approach to Solve the Problem

This problem can be approached in several ways, but using dynamic programming is the most efficient. By using dynamic programming, we can reuse previously calculated values to reduce unnecessary computations. Here is a step-by-step approach to solving this problem.

Step 1: Set Up the Dynamic Programming Table

First, create a 2-dimensional array dp with the same size as the original matrix. This array will store the maximum side length of the square made up of ‘1’s at each position.

Step 2: Handle Boundary Conditions

The first row and the first column of the matrix are set as boundary conditions. In this case, if the position is ‘1’, the maximum side length of the square is 1. If it is ‘0’, there can be no square, so it is set to 0.

Step 3: Fill the DP Array

Next, traverse the matrix and update the DP array based on the value at each position. If the current position matrix[i][j] is ‘1’, the length of the side of the square at that position is calculated as follows:

dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;

Here, the Math.min() function calculates the minimum value from above, left, and diagonal-left. This minimum value represents the minimal length needed to form a square.

Step 4: Process the Result

Finally, to find the side length of the square, look for the largest value in the DP array.

Algorithm Implementation

Now, let’s implement the actual algorithm using JavaScript based on the approach described above.

function maximalSquare(matrix) {
    if (matrix.length === 0) return 0;

    const rows = matrix.length;
    const cols = matrix[0].length;
    const dp = Array.from({ length: rows }, () => Array(cols).fill(0));
    let maxSide = 0;

    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (matrix[i][j] === '1') {
                if (i === 0 || j === 0) {
                    dp[i][j] = 1; // For the first column or row, it is 1
                } else {
                    dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
                }
                maxSide = Math.max(maxSide, dp[i][j]);
            }
        }
    }

    return maxSide * maxSide; // Return the area of the square
}

Performance and Complexity Analysis

The time complexity of this algorithm is O(m*n), and the space complexity is O(m*n). Here, m is the number of rows in the matrix, and n is the number of columns. Since a DP array is used, additional space is needed, but this can be optimized to O(n).

Optimization: Reducing Space Complexity

In some cases, the space complexity of the DP array can be reduced to O(n). Two arrays can be used to save the previous DP array, and only the previous state is needed when updating the current state.

function maximalSquare(matrix) {
    if (matrix.length === 0) return 0;

    const rows = matrix.length;
    const cols = matrix[0].length;
    let dp = Array(cols).fill(0);
    let maxSide = 0, prev = 0;

    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            let temp = dp[j]; // Store the previous value
            if (matrix[i][j] === '1') {
                if (i === 0 || j === 0) {
                    dp[j] = 1;
                } else {
                    dp[j] = Math.min(dp[j], dp[j-1], prev) + 1;
                }
                maxSide = Math.max(maxSide, dp[j]);
            } else {
                dp[j] = 0; // Set to 0 for '0'
            }
            prev = temp; // Update the previous value
        }
    }

    return maxSide * maxSide; // Return the area of the square
}

Conclusion

Today, we learned how to solve the “Maximum Square” problem, which frequently appears in JavaScript coding tests, using dynamic programming. This problem helps to understand the basic concepts of dynamic programming and will greatly enhance your coding skills in real-world scenarios. Don’t forget to improve your understanding of algorithms and data structures by solving various problems!

We will cover more algorithmic problems in the future, so please stay tuned. Thank you!