JavaScript Coding Test Course, DNA Password

Coding tests are an important means of validating programming skills, and companies use them to assess the candidate’s algorithmic thinking and problem-solving abilities. In this course, we will closely examine the process of solving the DNA Password problem using JavaScript.

Problem Description

The DNA Password problem is as follows:

Problem: Given a DNA string of length N, find the number of all possible substrings from the DNA string that can be a password. The conditions for a password are that it must contain exactly 1 or 2 of the characters ‘A’, ‘C’, ‘G’, ‘T’, and must contain at least 1.

Example Input and Output


Input: "ACGTACGTA"
Output: 16

Problem Solving Process

To solve this problem, we will follow these steps:

Step 1: Problem Analysis

We need to find all substrings from the given DNA string that satisfy the password conditions. A password must contain either 1 or 2 of the characters ‘A’, ‘C’, ‘G’, ‘T’. Therefore, we need to consider cases for substrings that include each character.

Step 2: Generate Substrings

To generate all substrings of the DNA string, we can use two pointers to define the startIndex and endIndex. This process generates O(N^2) cases.

Step 3: Check Password Conditions

For each substring, we need to check the counts of ‘A’, ‘C’, ‘G’, ‘T’. We can use regular expressions or a counting array for this.

Step 4: Implement Code

Now, let’s implement the JavaScript code to solve the problem:


function countDNAPasswords(dna) {
    const n = dna.length;
    let count = 0;

    // Generate all substrings
    for (let start = 0; start < n; start++) {
        const charCount = { 'A': 0, 'C': 0, 'G': 0, 'T': 0 };
        
        for (let end = start; end < n; end++) {
            // Current character count
            const char = dna[end];
            if (charCount[char] !== undefined) {
                charCount[char]++;
            }

            // Check password conditions
            const uniqueCount = Object.values(charCount).filter(x => x > 0).length;
            if (uniqueCount >= 1 && uniqueCount <= 2) {
                count++;
            }
        }
    }

    return count;
}

// Example usage
const dnaString = "ACGTACGTA";
console.log(countDNAPasswords(dnaString)); // Output: 16

Code Explanation

The main functions of the code written above are as follows:

  • The countDNAPasswords function takes the DNA string as input and calculates the number of passwords.
  • Two nested for loops are used to generate all substrings.
  • For each substring, the charCount object is used to count the occurrences of ‘A’, ‘C’, ‘G’, ‘T’, and check the password conditions.
  • It counts the cases that meet the conditions.

Time Complexity Analysis

The time complexity of this algorithm is O(N^2). It generates all substrings using two nested loops. However, this method may lead to performance issues as the length of the string increases. Therefore, it is worth considering optimized methods or applying other algorithms.

Conclusion

In this course, we learned how to solve the DNA Password problem using JavaScript. We started from an algorithmic problem, implemented the code, and examined the process of deriving results in detail. Such problems frequently appear in coding tests, so consistent practice is necessary.

Through this practice, you can repeatedly improve your problem-solving skills, so it is recommended to tackle various problems. Thank you!

JavaScript Coding Test Course, Insertion Sort

Hello! In this post, we will learn how to solve algorithm problems using JavaScript. The topic is ‘Insertion Sort’. All algorithms are means to solve specific problems, and insertion sort is one of the most fundamental and important sorting algorithms. Through this article, we will understand the concept of insertion sort and explore in detail how to implement it in JavaScript.

1. What is Insertion Sort?

Insertion sort is a very efficient sorting algorithm when the data is nearly sorted. This algorithm basically divides the list into two parts and builds a sorted list by inserting new elements into their appropriate positions. It has a methodology of comparing each element one by one and inserting it into its rightful place.

1.1. How Insertion Sort Works

The basic process of insertion sort works as follows:

  1. Compare the first two elements.
  2. If the first element is greater than the second element, swap their positions.
  3. Select the next element and appropriately insert it into the sorted list. Repeat this process until all elements are sorted.

2. Time Complexity of Insertion Sort

The average time complexity of insertion sort is O(n²). Even in the worst case, it remains O(n²), and it only shows O(n) performance in the best case. However, the best case occurs when the data is already sorted. For this reason, insertion sort is very efficient for cases with a small number of elements or nearly sorted data.

3. Problem Definition

3.1. Problem Statement

Given an integer array like the following, write a function to sort the array in ascending order using insertion sort.


Input: [5, 2, 9, 1, 5, 6]
Output: [1, 2, 5, 5, 6, 9]
    

4. Algorithm Implementation

Now let’s actually implement insertion sort in JavaScript. The code is simple and is as follows:


function insertionSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let key = arr[i];
        let j = i - 1;

        // Compare the current key with the sorted part to find position
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];  // Move position
            j--;
        }
        arr[j + 1] = key;  // Insert position
    }
    return arr;
}

// Test
const input = [5, 2, 9, 1, 5, 6];
console.log(insertionSort(input));  // [1, 2, 5, 5, 6, 9]
    

4.1. Code Explanation

The above code is structured as follows:

  • for loop: Starts from the second element of the array (index 1) and iterates to the last element of the array.
  • key variable: This is the element that is currently used as a reference. This value will be inserted into the sorted array.
  • while loop: Compares the current element (key) with the sorted part (left) to find its position. Moves larger elements to the right.
  • Inserts each element into its appropriate position and ultimately returns the sorted array.

5. Performance Analysis

The performance of insertion sort depends on the composition of the input data, but it generally has an average speed of O(n²) for an array of length n. It is very simple, but it does not perform well on large datasets, so it is common to use it alongside other sorting algorithms in practical applications.

6. Advantages and Disadvantages of Insertion Sort

6.1. Advantages

  • It can be easily implemented.
  • It is very fast when the data is nearly sorted.
  • It uses little memory and does not require additional space.
  • It is a stable sorting algorithm.

6.2. Disadvantages

  • It is inefficient for large datasets.
  • With a time complexity of O(n²), it performs poorly in the worst case.

7. Conclusion

In this post, we learned about insertion sort. It is a simple sorting algorithm, but understanding and utilizing its structure and working mechanism is very useful. In particular, it is a fundamental algorithm you must know when writing advanced algorithms in JavaScript. In the next tutorial, we will compare it with other sorting algorithms to further expand your understanding of algorithms!

8. References

JavaScript Coding Test Course, Finding the Sum of Consecutive Natural Numbers

Problem Description

The problem of finding the sum of consecutive natural numbers is one of the representative types of algorithm problems. Given a number N, what we want to find out is the number of combinations of consecutive natural numbers that can form the number N. In other words, this problem is about determining whether N can be expressed as the sum of multiple consecutive natural numbers.

Problem Definition

The problem can be summarized in the following format:

        Input:
        - Integer N (1 ≤ N ≤ 10^6)

        Output:
        - The number of ways to express N as the sum of consecutive natural numbers
    

Examples

Example 1

        Input: 15
        Output: 4
        Explanation: 15 can be expressed in the following ways:
        - 7 + 8
        - 4 + 5 + 6
        - 1 + 2 + 3 + 4 + 5
        - 15 (as the integer itself)
    

Example 2

        Input: 10
        Output: 2
        Explanation: 10 can be expressed in the following ways:
        - 1 + 2 + 3 + 4
        - 4 + 6
    

Problem Solution

To find the sum of consecutive natural numbers, a specific methodology is needed. Basically, the sum of two consecutive natural numbers follows a mathematical formula:

The sum of several numbers a, a+1, a+2, …, a+k can be expressed as:

        S = a + (a + 1) + (a + 2) + ... + (a + k)
          = (k + 1) * a + (0 + 1 + 2 + ... + k)
          = (k + 1) * a + (k * (k + 1) / 2)
    

At this point, S must equal N. Based on this, we can design an algorithm.

Algorithm Design

This problem can be efficiently approached using a sliding window algorithm with two pointers. The proposed method is as follows:

  1. Set up a start pointer and an end pointer, both initialized to 1.
  2. Initialize the current sum.
  3. Move the end pointer to the right while adding the value of the end pointer to the sum.
  4. If the current sum is less than N, continue moving the end pointer.
  5. If the current sum equals N, increment the count of combinations and move the start pointer to the right to reduce the sum.
  6. If the current sum is greater than N, move the start pointer to the right to reduce the sum.
  7. Repeat until the end pointer is less than or equal to N.

Python Code Implementation

Now, let’s implement the algorithm described above in Python. Although the syntax differs from JavaScript, it will help in understanding the logic.

        
def count_consecutive_sum(N):
    count = 0
    start = 1
    end = 1
    current_sum = 0

    while end <= N:
        current_sum += end

        while current_sum > N:
            current_sum -= start
            start += 1

        if current_sum == N:
            count += 1

        end += 1

    return count
        
    

JavaScript Code Implementation

Now, let’s implement the same algorithm in JavaScript.

        
function countConsecutiveSum(N) {
    let count = 0;
    let start = 1;
    let end = 1;
    let currentSum = 0;

    while (end <= N) {
        currentSum += end;

        while (currentSum > N) {
            currentSum -= start;
            start++;
        }

        if (currentSum === N) {
            count++;
        }

        end++;
    }

    return count;
}
        
    

Conclusion

The problem of finding the sum of consecutive natural numbers is one of the basic algorithm problems, and can be effectively solved using mathematical approaches alongside the sliding window technique. This technique is a common topic in coding interviews, so being familiar with it will be beneficial.

Tip: The most important thing in the process of solving problems in coding tests is to accurately understand the requirements of the problem and to practice with various examples. Practice as if in real situations and prepare to explain your solution clearly during interviews!

References

Additional resources for studying algorithms include the following:

JavaScript Coding Test Course, Exploring Combinations

1. Introduction

Many developers prepare for coding tests to solve algorithm problems while preparing for employment. In particular, when using JavaScript, combination problems are one of the frequently encountered topics. Combinations deal with how to select a specific number of elements from a given set. In this article, we will clarify the concept of combinations and present algorithm problems utilizing this concept, detailing the solution process.

2. Concept of Combinations

A combination refers to the method of selecting a specific number of elements without regard to the order. For example, the combinations of selecting 2 elements from the set {A, B, C} are {A, B}, {A, C}, and {B, C}, totaling 3. Combinations can be calculated using the following mathematical formula.

  • nCk = n! / (k! * (n-k)!)

Here, n is the size of the set, k is the number of elements to be selected, and ! denotes factorial.

3. Algorithm Problem

Problem: Sum of Combinations

Given an integer array arr and an integer target. Find all combinations of elements from the array that sum up to target. Each combination should be considered the same even if the order of elements is different.

Input Example

  • arr = [2, 3, 6, 7]
  • target = 7

Output Example

  • Result: [[7], [2, 2, 3]]

4. Problem Solving Process

To solve this problem, we can use recursion and the backtracking technique. The considerations when designing the function are as follows.

  • If the sum of the currently selected elements equals the target, save that combination.
  • If the sum of the currently selected elements exceeds the target, terminate the function.
  • Iteratively select each element to create combinations.

4.1. JavaScript Code


function combinationSum(arr, target) {
    const results = [];
    
    function backtrack(start, path, sum) {
        if (sum === target) {
            results.push([...path]);
            return;
        }
        if (sum > target) {
            return;
        }
        
        for (let i = start; i < arr.length; i++) {
            path.push(arr[i]);
            backtrack(i, path, sum + arr[i]);
            path.pop();
        }
    }
    
    backtrack(0, [], 0);
    return results;
}

const arr = [2, 3, 6, 7];
const target = 7;
console.log(combinationSum(arr, target));

    

4.2. Code Analysis

The above code solves the problem through the following steps.

  1. Function Definition: Define the combinationSum function and declare the backtrack function internally to generate combinations.
  2. Recursive Call: After selecting each element, continue to explore combinations including that element recursively. Here, the variable start is used to ensure that already selected elements are not selected again.
  3. Sum Comparison: If the current sum sum equals the target, add the current combination path to the results array.
  4. Backtracking: After the recursive call, remove the selected element and move to the next element.

5. Time Complexity

The time complexity of this problem is O(2^n) in the worst case. This is because it involves deciding whether or not to include each element, leading to exploration of all possible combinations. Even though a worst-case scenario exists, if the number of combinations is relatively small, this method can still effectively solve the problem.

6. Conclusion

Today, we explored how to solve combination problems using JavaScript. We demonstrated that by understanding the concept of combinations and utilizing a recursive approach through backtracking, it is possible to effectively solve these problems. Since combination problems frequently appear in coding tests, understanding and practicing these problems is essential. I hope you enhance your skills by tackling various problems.

7. References

  • LeetCode - Algorithm problem-solving platform
  • GeeksforGeeks - Various data structures and algorithm courses

JavaScript Coding Test Course, Counting the Number of Leaf Nodes

1. Problem Definition

The problem is to count the number of leaf nodes in a binary tree. A leaf node is a node that has no child nodes. Counting the number of nodes with no children is a good exercise for understanding the structure of the tree and utilizing exploration algorithms. To solve this problem, we can approach it using either recursive or iterative methods.

2. Problem Example

Let’s assume we are given the following binary tree:

             1
            / \
           2   3
          / \
         4   5
        

The leaf nodes of the above tree are 4, 5, and 3, totaling 3. We need to receive a tree like this as input and return the number of leaf nodes.

3. Algorithm Approach

There are several ways to determine the number of leaf nodes. The most common method we will use is Depth-First Search (DFS) utilizing recursion. This method involves exploring the tree in a depth-first manner to find leaf nodes and count them.

4. Algorithm Explanation

The following is a basic outline of the algorithm to count leaf nodes:

  1. Check if the given node is null. If it is null, return 0.
  2. If the node is a leaf node (i.e., both left and right children are null), return 1.
  3. Recursively call the left and right subtrees to determine the count of leaf nodes in each.
  4. Add the counts of the left and right leaf nodes and return the result.

5. JavaScript Implementation

Below is the code implementing the above algorithm using JavaScript:

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

                function countLeafNodes(node) {
                    // Base case: null node
                    if (node === null) {
                        return 0;
                    }
                    // Leaf node condition
                    if (node.left === null && node.right === null) {
                        return 1;
                    }
                    // Count leaf nodes with a recursive call
                    return countLeafNodes(node.left) + countLeafNodes(node.right);
                }

                // Example tree construction
                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(countLeafNodes(root)); // Expected output: 3
            
        

6. Algorithm Analysis

The time complexity of this algorithm is O(n) because we need to visit every node in the tree once. Here, n refers to the number of nodes. The space complexity is O(h) in the worst case, where h represents the height of the tree. This is due to the depth of the recursive call stack.

If the tree is empty or if all nodes are skewed to one side, the stack depth may increase. If the tree is balanced, the height would be log(n).

7. Iterative Method

We can also implement DFS using an iterative approach. This involves using a stack to track the current node and count nodes with no children. Below is an example of an iterative implementation:

            
                function countLeafNodesIterative(root) {
                    if (root === null) {
                        return 0;
                    }

                    let stack = [root];
                    let leafCount = 0;

                    while (stack.length > 0) {
                        let node = stack.pop();

                        // Check if the node is a leaf node
                        if (node.left === null && node.right === null) {
                            leafCount++;
                        }

                        // Add child nodes to the stack
                        if (node.right !== null) {
                            stack.push(node.right);
                        }
                        if (node.left !== null) {
                            stack.push(node.left);
                        }
                    }

                    return leafCount;
                }

                console.log(countLeafNodesIterative(root)); // Expected output: 3
            
        

8. Conclusion

In this tutorial, we explored how to count leaf nodes in a binary tree. We confirmed that both recursive and iterative methods can be used to approach this problem. I hope this has helped deepen your understanding of tree data structures and DFS exploration algorithms. I want to emphasize that analyzing the performance of algorithms and considering efficiency is crucial in learning data structures and algorithms.