Bea.AI coding blog
  • Tree
    • Tree Traverse
    • Iteration based traverse
    • Typical problems and solutions
      • Max width of binary tree
      • Binary Tree & BST Serialize & Deserialize
      • Lowest common ancestor (LCA) for Binary Tree and BST
      • Subproblem-based Approach for Resolving Max Value on Trees
      • Path sum III
  • Graph
  • Data structure
    • Java data structure
  • Algorithm general templates
    • Tree
    • Trie
    • Graph
  • Java Algorithm Tips and Tricks
    • Java concurrent list
  • Coding patterns & Strategies
  • Coding patterns & template
    • 1. Binary Search
    • 2. Two Pointer
    • 3. Sliding Window
    • 4. Backtracking
    • 5. DFS
    • 6. BFS
    • 7. Stack
    • 8. Heap
    • 9. Prefix Sum
    • 10. Linked List
    • 11. BST
    • 12. Line sweep
    • 14. Tree
    • 15. Graph
    • 16. Bit manipulation
    • 17. Matrix
    • 18. Monotonic Stack
    • 19. Sorting
    • 20. Union Find
    • 21. Trie
    • 22. Dynamic programming
    • 23. Customized Data Structure
  • Code interview steps
  • Leetcode
    • Tree
      • Traversal
      • Construction and Conversion
      • Search and Validation
      • Manipulation and Modification
      • Special Trees
      • Miscellaneous
    • Dynamic programing
      • Classic DP Problems
      • Sequence/Array DP
      • Matrix DP
      • Knapsack Problems
      • String DP
      • Tree/Graph DP
      • Optimization Problems
      • Combinatorial DP
    • DFS
      • Graph Traversal
      • Path Finding
      • Tree Traversal
      • Backtracking
      • Topological Sorting
      • Traversal Order
      • Dynamic Programming with DFS
      • Connected Components
    • BFS
      • Graph
      • Tree
      • Matrix
      • String
      • Others
    • Two points
      • Array
      • String
      • Linked List
      • Sorting
      • Others
    • LinkedList
      • Basic Operations
      • Cycle Detection and Handling
      • Intersection and Union
      • Partitioning and Merging
      • Palindrome
      • Miscellaneous
    • Backtracking
    • Binary Search
    • Union Find
    • Trie
    • Sort
    • Heap
    • Randomness
    • Topological sort
    • Stack
    • HashTable
    • Sliding Window
  • Blind 75
    • Array
    • Binary
    • Dynamic Programming
    • Graph
    • Interval
    • LinkedList
    • Matrix
    • String
    • Tree
    • Heap
  • Companies
    • Apple
Powered by GitBook
On this page
  • 18. Perfect Squares
  • 19. Minimum Genetic Mutation
  • 20. Open the Lock
  • 21. Web Crawler
  1. Leetcode
  2. BFS

Others

18. Perfect Squares

  1. Problem Description: The problem is called "Perfect Squares" and the link to the problem on LeetCode is https://leetcode.com/problems/perfect-squares/.

Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n.

  1. Optimized Java Code:

public class PerfectSquares {
    public int numSquares(int n) {
        // Create an array to store the minimum number of perfect squares needed for each number from 1 to n
        int[] dp = new int[n + 1];
        
        // For each number from 1 to n
        for (int i = 1; i <= n; i++) {
            // Set the default minimum to maximum possible value i.e., n (assuming all numbers are sum of 1's)
            dp[i] = i;
            
            // For each perfect square number j (j*j) that is less than or equal to i
            for (int j = 1; j * j <= i; j++) {
                // Update the minimum by considering the current perfect square
                dp[i] = Math.min(dp[i], 1 + dp[i - j * j]);
            }
        }
        
        // Return the minimum number of perfect squares needed for n
        return dp[n];
    }
}
  1. Algorithm Explanation:

The problem can be solved using dynamic programming.

We start by creating an array dp of size n+1 to store the minimum number of perfect squares needed for each number from 1 to n. The array is initialized with the maximum possible value i.e., n.

For each number 1 to n, we iterate through every perfect square number j*j that is less than or equal to the current number. We update the minimum number of perfect squares needed for the current number by considering the current perfect square number and the minimum number required for the difference between the current number and the perfect square (dp[i - j * j]).

Finally, we return the minimum number of perfect squares needed for n.

During an interview, it is important to understand the problem thoroughly and come up with a brute force solution first. Once the brute force solution is understood, you can optimize it using techniques like dynamic programming. The thought process involves breaking down the problem into smaller subproblems and reusing their solutions to solve the larger problem more efficiently. It is crucial to explain the algorithm step-by-step, including the reasoning behind each step, to demonstrate clear understanding to the interviewer.

19. Minimum Genetic Mutation

  1. Problem Description: The problem "Minimum Genetic Mutation" involves finding the minimum number of mutations needed to transform one string into another, given a list of possible gene mutations. A gene mutation occurs when one character in the string is replaced with another character. Only one mutation can occur at a time, and the resulting string must be a valid gene in the given list.

LeetCode Problem Link: https://leetcode.com/problems/minimum-genetic-mutation/

  1. Optimized Java Code:

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

public class MinGeneticMutation {
    public int minMutation(String start, String end, String[] bank) {
        if (start.equals(end)) // If start and end are the same, no mutations required
            return 0;
        
        Set<String> bankSet = new HashSet<>();
        for (String gene : bank) {
            bankSet.add(gene);
        }
        
        char[] nucleotides = {'A', 'C', 'G', 'T'}; // Possible gene mutations
        
        Queue<String> queue = new LinkedList<>();
        Set<String> visited = new HashSet<>();
        queue.offer(start);
        visited.add(start);
        
        int mutations = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String curr = queue.poll();
                
                if (curr.equals(end)) // Reached the target end gene
                    return mutations;
                
                char[] currGene = curr.toCharArray();
                for (int j = 0; j < currGene.length; j++) {
                    char originalChar = currGene[j];
                    for (char nucleotide : nucleotides) {
                        currGene[j] = nucleotide;
                        String newGene = String.valueOf(currGene);
                        
                        if (bankSet.contains(newGene) && !visited.contains(newGene)) {
                            queue.offer(newGene);
                            visited.add(newGene);
                        }
                    }
                    currGene[j] = originalChar; // Revert back the gene mutation for the next iteration
                }
            }
            mutations++;
        }
        
        return -1; // No possible mutation path found
    }
}
  1. Algorithm Explanation: The problem can be solved using a breadth-first search (BFS) approach.

  • Create a set (bankSet) to store all valid mutations from the given list.

  • Create a queue (queue) to store the current gene being processed.

  • Create a set (visited) to keep track of visited genes.

  • Enqueue the start gene into the queue and mark it as visited.

  • Start a loop until the queue is empty.

    • Process all genes at the current level by iterating over the queue size.

    • For each gene in the queue:

      • If the gene is equal to the end gene, return the number of mutations.

      • Convert the gene to a character array (currGene).

      • Iterate over each character in currGene.

      • For each character:

        • Store the original character.

        • Replace the character with each possible gene mutation.

        • If the mutated gene exists in the bankSet and has not been visited before:

          • Enqueue the mutated gene.

          • Mark the mutated gene as visited.

        • Revert the gene mutation (restore original character) for the next iteration.

    • Increment the number of mutations.

  • If no valid mutation path is found, return -1.

During an interview, it is important to communicate the steps of the algorithm clearly and provide an intuitive explanation. Emphasize the use of BFS to explore all possible mutations in the search space. Discuss the time and space complexity of the solution (which is O(N^2 * M), where N is the length of each gene and M is the size of the bank array) and potential optimizations (such as using a bidirectional BFS approach for better performance).

20. Open the Lock

  1. Problem Description:

Problem:

You have a lock in front of you with 4 circular wheels. Each wheel has 10 slots labeled '0' through '9'. The lock initially starts at '0000', a string representing the state of the 4 wheels.

You are given a list of deadends dead ends, indicating the combinations of the wheels that are not allowed. You goal is to reach the target state 'target' by changing the lock's wheels.

Write a function to find the minimum total number of turns required to open the lock, or return -1 if it is impossible.

  1. Optimized Java Solution:

class Solution {
    public int openLock(String[] deadends, String target) {
        Set<String> deadSet = new HashSet<>(Arrays.asList(deadends));
        Set<String> visited = new HashSet<>();
        Queue<String> queue = new LinkedList<>();
        queue.add("0000");
        visited.add("0000");
        int turns = 0;
        
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String current = queue.poll();
                if (deadSet.contains(current)) continue;
                if (current.equals(target)) return turns;
                for (int j = 0; j < 4; j++) {
                    String up = increment(current, j);
                    if (!visited.contains(up)) {
                        visited.add(up);
                        queue.offer(up);
                    }
                    String down = decrement(current, j);
                    if (!visited.contains(down)) {
                        visited.add(down);
                        queue.offer(down);
                    }
                }
            }
            turns++;
        }
        
        return -1;
    }
    
    private String increment(String s, int index) {
        char[] chars = s.toCharArray();
        if (chars[index] == '9') {
            chars[index] = '0';
        } else {
            chars[index]++;
        }
        return new String(chars);
    }
    
    private String decrement(String s, int index) {
        char[] chars = s.toCharArray();
        if (chars[index] == '0') {
            chars[index] = '9';
        } else {
            chars[index]--;
        }
        return new String(chars);
    }
}
  1. Algorithm Description:

To solve this problem, we can use a Breadth-First Search (BFS) approach.

We can represent each state of the lock as a string with 4 characters, where each character represents the current digit at that wheel position. For example, '0000' represents the initial state of the lock.

We start by initializing a set of deadends and a set to keep track of visited states. We also initialize a queue to store the states to be visited.

We add the initial state ('0000') to the queue and mark it as visited. Then, while the queue is not empty, we process each state in the queue. For each state, we check if it is a deadend. If it is, we skip to the next state. If it is the target state, we return the number of turns taken to reach that state.

For each non-deadend state, we generate the next possible states by incrementing and decrementing the digits at each wheel position. If a generated state has not been visited before, we add it to the queue and mark it as visited.

We repeat this process until either the queue is empty or we reach the target state. If we exhaust all possible states without reaching the target, we return -1 to indicate that it is impossible to open the lock.

During an interview, it is important to explain the algorithm step by step and highlight the key components such as using BFS to explore all possible paths and the use of sets to keep track of visited states and deadends. Additionally, you can discuss the time and space complexity of the solution, which is O(10^4) in this case.

21. Web Crawler

  1. The problem "Web Crawler" on LeetCode simulates a web crawler that starts from the given url and visits all the webpages within a maximum depth. The task is to implement a function that returns a list of all the visited webpages in any order.

Problem Link: https://leetcode.com/problems/web-crawler

  1. Here is the optimized solution in Java for the "Web Crawler" problem:

// Definition for a web page.
class HtmlParser {
    public List<String> getUrls(String url) {
        // Sample implementation to get URLs from a given webpage.
        // It can be replaced with actual implementation for testing.
        return new ArrayList<>();
    }
}

class Solution {
    public List<String> crawl(String startUrl, HtmlParser htmlParser) {
        String hostname = getHostname(startUrl);
        
        Set<String> visited = new HashSet<>();
        visited.add(startUrl);
        
        Queue<String> queue = new LinkedList<>();
        queue.add(startUrl);
        
        while (!queue.isEmpty()) {
            String currUrl = queue.poll();
            
            for (String nextUrl : htmlParser.getUrls(currUrl)) {
                if (!visited.contains(nextUrl) && getHostname(nextUrl).equals(hostname)) {
                    visited.add(nextUrl);
                    queue.add(nextUrl);
                }
            }
        }
        
        return new ArrayList<>(visited);
    }
    
    private String getHostname(String url) {
        String[] parts = url.split("/");
        return parts[2];
    }
}
  1. The algorithm used in this solution is a Breadth-First Search (BFS) traversal of the webpages.

During an interview, you can describe the algorithm in the following steps:

  • Start by initializing a set named visited to keep track of the visited webpages.

  • Add the startUrl to the visited set and initialize a queue with the startUrl.

  • While the queue is not empty, perform the following steps:

    • Dequeue the first URL from the queue and name it currUrl.

    • Get all the URLs present on the currUrl webpage using the getUrls() method of the htmlParser.

    • For each nextUrl in the obtained URLs, perform the following checks:

      • If it is not in the visited set and belongs to the same hostname as the startUrl, then add it to the visited set and enqueue it in the queue.

  • After the traversal is complete, return a list containing all the webpages visited, which is the visited set converted to an ArrayList.

During an interview, it is important to discuss the time complexity of the solution. In this case, the time complexity is primarily determined by the number of webpages (n) and the average number of URLs on a webpage (m). The worst-case time complexity would be O(n + m), as we visit each webpage once and process each URL once.

This solution demonstrates proficiency in graph traversal and data structure manipulation, which are common concepts in coding interviews.

PreviousStringNextTwo points

Last updated 1 year ago

LeetCode Link:

Open the Lock