JavaScript Coding Test Course, Binary Tree

Introduction

Today, software developers need a deep understanding of algorithms and data structures. In particular, recursive structures like binary trees are useful for solving various problems. In this course, we will cover the basic concepts of binary trees and coding test problems that utilize them, explaining the approach and code step by step.

What is a Binary Tree?

A binary tree is a tree structure in which each node has at most two child nodes (left and right). Binary trees can take various forms, and the following are some major types of binary trees:

  • Complete Binary Tree: A tree where every node has child nodes, and all levels are completely filled except for the last level.
  • Balanced Binary Tree: A tree where, for every node, the height difference between the left and right subtrees is no more than 1.
  • Binary Search Tree: A tree that follows the rule where the left child node is smaller than the parent node, and the right child node is larger than the parent node.

Problem Description

In this problem, we will write a function to find the ‘maximum depth of a binary tree’. The maximum depth refers to the number of nodes from the root node to the deepest leaf node.

Problem: Find the Maximum Depth of a Binary Tree

function maxDepth(root) {
    // Given the root node of a binary tree, return the maximum depth.
}

Approach to the Problem

To solve this problem, we can use the following approach:

  1. Use recursion to calculate the depth of each node.
  2. Return the depth when reaching a leaf node.
  3. Compare the depths of each subtree and return the greater value to the parent node.

Step-by-Step Solution

Step 1: Set Up the Basic Structure

First, we need to define the node structure. Let’s define a binary tree in JavaScript using a node class.


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

Step 2: Define the Depth Calculation Function

Now, let’s define the recursive function for calculating depth. This function takes the current node as an argument and calculates the depth.


function maxDepth(root) {
    // Base case: if there is no node, the depth is 0
    if (root === null) {
        return 0;
    }
    // Calculate depth of left and right subtrees
    const leftDepth = maxDepth(root.left);
    const rightDepth = maxDepth(root.right);
    // Return the maximum depth
    return Math.max(leftDepth, rightDepth) + 1;
}

Step 3: Test the Function

Let’s test the function we wrote to confirm it works. We will construct a binary tree as follows:


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);
root.left.left.left = new TreeNode(6);

console.log(maxDepth(root)); // 4 - (1 -> 2 -> 4 -> 6)

Conclusion

We have explored a problem of calculating the maximum depth using binary trees and recursion. This structure is frequently used in many algorithm problems, so understanding binary trees can help solve various application problems. I hope you continue to build deeper algorithm skills through persistent practice and problem-solving!

Additional Learning Resources

If you want to practice more algorithm problems related to binary trees, I recommend the following resources:

  • LeetCode: Maximum Depth of Binary Tree problem
  • HackerRank: Tree: Height of a Binary Tree problem
  • GeeksforGeeks: Binary Tree Basics

References

You can deepen your knowledge of algorithms and data structures through the following references:

  • Introduction to Algorithms – Thomas H. Cormen et al.
  • Data Structures and Algorithms in JavaScript – Michael McMillan

JavaScript Coding Test Course, Finding the Kth Number in an Array

Coding tests are a very important element in modern software development. Companies issue various algorithm problems to evaluate developers’ problem-solving abilities. Today, we will address the problem of finding the Kth smallest number in an array. This problem is a good example to build basic algorithm skills in handling arrays.

Problem Description

Given an array arr and an integer k, find and return the Kth smallest number in arr. The array index starts from 0.

Input

  • Integer array arr, length between 1 and 100,000.
  • Integer k, between 1 and the length of the array.

Output

Return the Kth smallest number from arr.

Example

Input: arr = [3, 1, 2, 4, 5], k = 2
Output: 2
Explanation: When the array is sorted in ascending order, it becomes [1, 2, 3, 4, 5], and the 2nd number is 2.
Input: arr = [7, 10, 4, 3, 20, 15], k = 3
Output: 7
Explanation: When the array is sorted in ascending order, it becomes [3, 4, 7, 10, 15, 20], and the 3rd number is 7.

Solution Process

This problem can be easily solved by sorting the array first and then finding the Kth element, but the time complexity may vary depending on the sorting method. Here, we will explore two approaches.

Method 1: Sorting the array and finding the Kth number

  1. Sort the array in ascending order.
  2. Return the value at index k - 1 to find the Kth number.

JavaScript Code Example

function findKthNumber(arr, k) {
    arr.sort((a, b) => a - b); // Sort the array in ascending order
    return arr[k - 1]; // Return Kth number
}

// Example execution
console.log(findKthNumber([3, 1, 2, 4, 5], 2)); // 2
console.log(findKthNumber([7, 10, 4, 3, 20, 15], 3)); // 7

In the code above, we simply sorted the array and found the Kth element. The time complexity of this method is O(n log n). However, there is a way to find the Kth smallest number without necessarily sorting.

Method 2: Quickselect Algorithm

The Quickselect algorithm is a method to find the Kth smallest number in a way similar to Quicksort. This method has an average time complexity of O(n). This algorithm is performed by setting up a subarray and choosing a pivot.

  1. Select a pivot element from the array.
  2. Place values smaller than the pivot on the left and larger values on the right.
  3. If the pivot’s position is the same as the position of the Kth number, return the pivot.
  4. If not, perform Quickselect recursively in the appropriate subarray based on whether the Kth number is in the left or right subarray.

JavaScript Code Example

function quickSelect(arr, left, right, k) {
    if (left === right) {
        return arr[left]; // If there is only one element in the array
    }
    const pivotIndex = partition(arr, left, right);
    if (k === pivotIndex) {
        return arr[k]; // Kth number found
    } else if (k < pivotIndex) {
        return quickSelect(arr, left, pivotIndex - 1, k);
    } else {
        return quickSelect(arr, pivotIndex + 1, right, k);
    }
}

function partition(arr, left, right) {
    const pivot = arr[right]; // Select the last element as the pivot
    let i = left; 
    for (let j = left; j < right; j++) {
        if (arr[j] <= pivot) {
            [arr[i], arr[j]] = [arr[j], arr[i]]; // Swap
            i++;
        }
    }
    [arr[i], arr[right]] = [arr[right], arr[i]]; // Final position of the pivot
    return i; // Return the index of the pivot
}

function findKthNumber(arr, k) {
    return quickSelect(arr, 0, arr.length - 1, k - 1); // Pass K-1 as the argument
}

// Example execution
console.log(findKthNumber([3, 1, 2, 4, 5], 2)); // 2
console.log(findKthNumber([7, 10, 4, 3, 20, 15], 3)); // 7

With the above code, we can efficiently find the Kth number using the Quickselect algorithm. This method is particularly useful for large datasets due to its average time complexity of O(n).

Conclusion

In this lecture, we explored the problem of finding the Kth number in an array, which is frequently covered in JavaScript coding tests. Although the problem can be solved simply with sorting, we can maximize performance by using efficient methods like Quickselect. Such knowledge can be very useful in actual coding tests, so I highly recommend practicing it.

All algorithms require a basic understanding followed by developing applicability through various problems. In the next lecture, we will cover more diverse array problems and advanced algorithms. Thank you!

JavaScript Coding Test Course, Finding the Largest Palindrome

In this article, we will address the algorithm problem of finding the Catalan numbers using JavaScript. Catalan numbers are related to mathematical concepts such as structural concepts like nested parentheses, and this problem often appears in programming interviews. Therefore, we will explore a way to thoroughly understand and solve this problem.

What are Catalan numbers?

Catalan numbers are strings of length N composed of 0s and 1s that follow the following rules:

  • The first and last characters of the string must be 0.
  • The number of 1s in the string must always be at least 1, there cannot be consecutive 1s, and each 1 must be surrounded by 0s.

For example, the Catalan numbers of length 5 include 00000, 01000, 00100, 00010, 00001,
01010, 01100, 10000, etc. Catalan numbers are similar to the Fibonacci sequence, and
they can take different counts depending on the length N.

Problem Description

Given a natural number N, the problem is to output the count of Catalan numbers of length N.
For example, when N = 5, we need to find the number of Catalan numbers,
and the result should be 8.

Solution

To find the Catalan numbers, we can use recursive calls or dynamic programming (DP) methods.
Below is the formula for computing the Catalan numbers.

  • p(n) = p(n-1) + p(n-2) (n ≥ 2, p(0) = 1, p(1) = 1)

This formula recursively computes the Catalan numbers.
It is important to note that it should return 1 when n is 0.
Moreover, the relationship between p(n-1) and p(n-2) refers to the counts of previous Catalan numbers.

Algorithm Implementation

        
            // Function to calculate Catalan numbers in JavaScript.
            function countCatalanNumbers(n) {
                // Array to store Catalan numbers
                const dp = new Array(n + 1).fill(0);
                dp[0] = 1; // Case for length 0
                dp[1] = 1; // Case for length 1

                // Using dynamic programming to calculate the Catalan numbers.
                for (let i = 2; i <= n; i++) {
                    dp[i] = dp[i - 1] + dp[i - 2];
                }

                return dp[n]; // Return the count of Catalan numbers of length n.
            }

            // Example of function call
            const n = 5; // Value of N
            console.log(`The number of Catalan numbers of length ${n} is: ${countCatalanNumbers(n)}`);
        
        

Process Explanation

  1. Understanding the Problem: This is a problem to find the Catalan numbers for a natural number N.
  2. Dynamic Programming Approach: We define the Catalan numbers structurally.
  3. Array Initialization: Create the dp array and set the base values.
  4. Executing the Loop: Calculate and store the count of Catalan numbers in the array.
  5. Verifying the Result: Validate that the output of the function is correct.

Execution Result

        
            // Input: N = 5
            // Output: The number of Catalan numbers of length 5 is: 8
        
    

Conclusion

We have learned how to find the Catalan numbers and have been able to efficiently solve the problem through dynamic programming.
This problem frequently appears in programming interviews, so it is important to develop the ability to understand and implement the principles of the algorithm.

JavaScript Coding Test Course, Understanding Friend Relationships

Coding tests often include problems related to data structures and algorithms. In particular, problems related to identifying friendships are related to graph theory, and many companies use these problems to evaluate candidates’ logical thinking and coding skills.

Problem Description

The algorithm problem for identifying friendships is as follows.
Problem: Friend Relationship Exploration
Given the number of people N and M pairs representing friend relationships, write a function to determine the number of people who are not friends with a specific individual.

Input Format

  • First line: Number of people N (1 ≤ N ≤ 100)
  • Second line: Number of friend relationships M (1 ≤ M ≤ 1000)
  • Third line: M pairs of friend relationships provided in the form (a b), meaning a and b are friends.
  • Fourth line: Specific individual X (1 ≤ X ≤ N) – the individual to check friendships for

Output Format

Output the number of people who are not friends with the input individual X.

Example Input

    5
    4
    1 2
    1 3
    2 4
    4 5
    1
    

Example Output

    3
    

Problem Solving Process

Step 1: Understanding and Analyzing the Problem

To understand the given problem, we need to be able to represent the provided friend relationships as a graph. To do this, we will use an adjacency list. This method allows us to connect each individual with their friends and easily understand the relationships.

Step 2: Designing Data Structures

Each individual can store friend relationships through array indices. For example, to save individuals who are friends with individual 1, we will add the relative individual’s number to the adjacency list. This structure allows us to easily find friends of a specific individual.

Step 3: Designing the Algorithm

To calculate the number of people who are not friends, we follow these procedures:

  1. Check the friend relationships for all individuals (from 1 to N) to build a friend list.
  2. Refer to the friend list of the specific individual X to calculate the number of people who are not friends.
  3. Finally, declare a friendCount variable, and by subtracting the number of friends and X from the total number of individuals, we can obtain the number of people who are not friends.

Step 4: JavaScript Implementation


    function countNonFriends(N, M, relations, X) {
        // Create adjacency list
        const friends = Array.from({ length: N + 1 }, () => []);

        // Add relationships
        relations.forEach(([a, b]) => {
            friends[a].push(b);
            friends[b].push(a);
        });

        // Count the number of non-friends
        const friendSet = new Set(friends[X]);
        let count = 0;

        for (let i = 1; i <= N; i++) {
            if (i !== X && !friendSet.has(i)) {
                count++;
            }
        }

        return count;
    }
    
    const N = 5;
    const M = 4;
    const relations = [[1, 2], [1, 3], [2, 4], [4, 5]];
    const X = 1;
    console.log(countNonFriends(N, M, relations, X)); // 3
    

Step 5: Time Complexity Analysis

The time complexity of this algorithm is O(N + M) in the process of checking each friend relationship. This is efficient since each relationship is only explored once.

Conclusion

This problem provides an opportunity to learn the process of solving graph-related problems and understanding friend relationships. Additionally, it offers practice in managing data using arrays and objects in JavaScript, and in implementing algorithms. It is important to practice multiple problems of this type while preparing for coding tests.

JavaScript Coding Test Course, Find Cities at a Specific Distance

Hello, everyone! Today we will discuss an algorithm problem to find cities at a specific distance using JavaScript. This problem is one of the frequently featured topics in coding tests, where you will learn how to apply graph traversal and BFS (Breadth-First Search) techniques.

Problem Description

Write a function that returns a list of cities located at a specific distance starting from the given two vertices. The cities are connected by edges, and it is assumed that the distance calculation for each edge is equal to 1.

Input

  • n: the number of cities (1 ≤ n ≤ 300,000)
  • edges: the edges connecting each city, given as a 2D array. edges[i] = [a, b] means city a is directly connected to city b.
  • start: the city from which the distance calculation will start (1 ≤ start ≤ n)
  • distance: a specific distance k (0 ≤ distance ≤ n)

Output

Return an array of cities that are at a specific distance, sorted in ascending order. If there are no such cities, return an empty array.

Problem Solving Strategy

The key to this problem is exploring the graph using the BFS algorithm. BFS is a technique that can explore all vertices in a graph and is suitable for finding the shortest path.

The basic steps to solve the problem are as follows:

  1. Define the graph structure using the given edges array.
  2. Calculate the distance from the given start city to each city using BFS.
  3. Collect the cities whose distance matches the specific distance.
  4. Sort the resulting city list in ascending order and return it.

Implementation Steps

Step 1: Graph Structuring

We will use an adjacency list to hold the connection information between cities. This will be composed of an object where each city is the key and the list of connected cities is the value.

Step 2: Implementing BFS Algorithm

Using BFS, we will calculate the distance from the starting city to each city and find the cities that can be reached at a specific distance.

Step 3: Processing and Returning Results

Collect the cities that match the specific distance, sort them, and return the result.

JavaScript Code Implementation


function findCitiesAtDistance(n, edges, start, distance) {
    // Step 1: Create the graph
    const graph = Array.from({ length: n + 1 }, () => []);
    for (const [a, b] of edges) {
        graph[a].push(b);
        graph[b].push(a); // Add to both sides for a bidirectional graph
    }
    
    // Step 2: Set variables for BFS
    const queue = [];
    const distances = Array(n + 1).fill(-1); // Initialize distances
    distances[start] = 0;
    queue.push(start);
    
    // Perform BFS traversal
    while (queue.length > 0) {
        const currentCity = queue.shift();
        
        for (const neighbor of graph[currentCity]) {
            // If the city has not been visited
            if (distances[neighbor] === -1) {
                distances[neighbor] = distances[currentCity] + 1;
                queue.push(neighbor);
            }
        }
    }
    
    // Step 3: Find cities at a specific distance
    const result = [];
    for (let city = 1; city <= n; city++) {
        if (distances[city] === distance) {
            result.push(city);
        }
    }

    // Sort in ascending order
    result.sort((a, b) => a - b);
    return result;
}
    

Code Explanation

The code above is a function that finds cities at a specific distance according to the given requirements. I will explain each step:

Graph Creation

We create the graph structure by iterating through the edges array and storing the connection information between each city. An adjacency list is used to keep the list of cities connected to each city.

BFS Implementation

This is the process of calculating distance values using BFS from the starting city. We use a distances array to record the distances of each city, marking visited cities as -1 to prevent duplication.

Result Processing

After exploring all the cities, we add the cities that match the specific distance to the result list and finally sort them in ascending order before returning.

Test Cases

Now let’s check the operation of the implemented code through some test cases.


console.log(findCitiesAtDistance(6, [[3, 6], [4, 3], [3, 2], [1, 3], [1, 2], [2, 4]], 5, 2)); 
// Output: [4, 5, 6]
console.log(findCitiesAtDistance(4, [[1, 2], [1, 3], [2, 4]], 1, 2)); 
// Output: [4]
console.log(findCitiesAtDistance(5, [[1, 2], [1, 3], [1, 4], [2, 5]], 1, 1)); 
// Output: [2, 3, 4]
console.log(findCitiesAtDistance(7, [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7]], 1, 3)); 
// Output: [4]
console.log(findCitiesAtDistance(3, [], 1, 1)); 
// Output: []
    

Conclusion

In this post, we covered an algorithm problem to find cities at a specific distance and explained the basic principles of graph traversal using BFS. Through this problem, we learned various concepts such as graph structuring, BFS implementation, and result processing. Since such problems are frequently presented in coding tests, understanding and practicing them is important. Next time, we will tackle more complex graph problems. Thank you!