[interview questions] width first search

[interview questions] width first search

1, The most concise general template of BFS algorithm

1. BFS templates for trees and graphs

  • new ArrayDeque is recommended for queues, and new LinkedList is not recommended (linked list is slower than array)
//Double ended queue
Queue<Node> queue = new ArrayDeque<>();
HashMap<Node,Integer> distance = new HashMap<>();

//step 1 initialization
//Put the initial node in deque. If there are multiple nodes, put them in
//Mark the distance of the initial node as 0 and record it in the hashmap of distance
//Distance has two functions: one is to judge whether it has been visited, and the other is to record the distance from the starting point
queue.offer(node);
distance.put(node,0);

//step 2: continuously accessing the queue
//while loop + one point in each pop queue
while (!queue.isEmpty()) {
    Node node = queue.poll();
    //step 3: expand adjacent nodes
    //pop out the adjacent nodes of the node, join the queue and store examples in distance
    for (Node neighbor : node.getNeighbors()) {
        continue;
    }
    distance.put(neighbor, distance.get(node) + 1);
    queue.offer(neighbor);
}

  • Complexity: BFS time complexity on the graph = O(N + M)

2,LeetCode-133. Clone graph

class Solution {
    public Node cloneGraph(Node node) {
        //Special case handling
        if (node == null) {
            return null;
        }

        //Step 1: find all points
        List<Node> nodes = findNodesByBFS(node);

        //Step 2: copy all points
        Map<Node, Node> mapping = copyNodes(nodes);
        
        //Part 3: copy all edges
        copyEdges(nodes, mapping);

        //Returns the node with the same val as the given node
        return mapping.get(node);
    }

    //Step 1: BFS find all points
    private List<Node> findNodesByBFS(Node node) {
        Queue<Node> queue = new LinkedList<>();
        //Used to save all points without repetition or omission
        Set<Node> visited = new HashSet<>();
        queue.offer(node);
        //Once you join the team, mark it immediately
        visited.add(node);

        while(!queue.isEmpty()) {
            Node curNode = queue.poll();
            for (Node neighbor : curNode.neighbors) {
                //If this point has been found before, there is no need to BFS again, otherwise the loop is dead
                if (visited.contains(neighbor)) {
                    continue;
                }
                //Once accessed, it should be marked immediately.
                visited.add(neighbor);
                queue.offer(neighbor);;
            }
        }
        return new LinkedList<>(visited);
    }

    //Step 2: copy all points
    private Map<Node,Node> copyNodes(List<Node> nodes) {
        //(old point - > new point) mapping
        Map<Node,Node> mapping = new HashMap<>();
        for (Node node : nodes) {
            mapping.put(node, new Node(node.val));
        }

        return mapping;
    }

    //Step 3: copy all edges
    private void copyEdges(List<Node> nodes, Map<Node,Node> mapping) {
        for (Node node : nodes) {
            Node newNode = mapping.get(node);
            //The old point has neighbors, and the new point has neighbors
            for (Node neighbor : node.neighbors) {
                Node newNeighbor = mapping.get(neighbor);
                newNode.neighbors.add(newNeighbor);
            }
        }
    }
}
  • As can be seen from the above code, the most important thing is to find all points through BFS.

3,LeetCode-127. Word Chain

① Generate transform list

② Transform list diagram

③ Code implementation

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        //Assume wordList is not empty
        //Suppose beginWord and endWord are non empty and different

        //You must join endWord. Can join beginword
        wordList.add(endWord);

        HashSet<String> visited = new HashSet<String>();
        Queue<String> queue = new LinkedList<String>();
        queue.offer(beginWord);
        visited.add(beginWord);

        //Record the shortest route length, starting at 1
        int length = 1;
        while(!queue.isEmpty()) {
            //The length to the next layer (not the current layer), which is equivalent to the depth of the tree
            length++;
            //The current layer has size elements
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                String word = queue.poll();
                //Get the next word
                for (String nextWord : getNextWords(word,wordList)) {
                    if (visited.contains(nextWord)) {
                        continue;
                    }

                    //If the word of the next layer is the last word, the current length to the next layer (not the current layer) is returned directly
                    if (nextWord.equals(endWord)) {
                        return length;
                    }

                    //Join the next layer to prepare for the later BFS
                    visited.add(nextWord);
                    queue.offer(nextWord);
                }
            }
        }
        //Can't connect head to tail
        return 0;
    }

    //Find all the words that can connect with word
    //For example, word = 'hot', dict = {hot ',' hit ',' hog '}, return ['hit', 'hog']
    private ArrayList<String> getNextWords(String word, List<String> wordList) {
        ArrayList<String> nextWords = new ArrayList<String>();
        //Enumerate each word in the dictionary
        for (String nextWord : wordList) {
            boolean hashOneDiff = false;
            for (int i = 0; i < word.length(); i++) {
                //Judge whether two words differ by only one letter. If yes, it can be connected
                if (nextWord.charAt(i) != word.charAt(i)) {
                    //If there is already a difference, eliminate it directly
                    if (hashOneDiff) {
                        hashOneDiff = false;
                        break;
                    }
                    hashOneDiff = true;
                }
            }
            if (hashOneDiff) {
                nextWords.add(nextWord);
            }
        }
        return nextWords;
    }

    //New string
    private String replace(String word, int index, char c) {
        char[] chars = word.toCharArray();
        chars[index] = c;
        return new String(chars);
    }
}

2, Width first search in matrix

1,LeetCode-200. Number of islands

Train of thought analysis

code implementation

//Define a class to represent a point in the coordinate system
class Coordinate {
    int x, y;
    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Solution {
    //Offset in four directions
    int[] deltaX = {0, 1, -1, 0};
    int[] deltaY = {1, 0, 0, -1};
    public int numIslands(char[][] grid) {
        //Special case handling
        if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) {
            return 0;
        }

        int islands = 0;
        int row = grid.length, col = grid[0].length;
        //Record whether a point has been BFS. If it has been BFS before, it should not be BFS again
        boolean[][] visited = new boolean[row][col];

        //Traverse every point in the matrix
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                //In case of ocean, BFS is not required
                //If the point has been visited, there is no need for redundant traversal and repeated reading
                if (grid[i][j]!='0' && !visited[i][j]) {
                    bfs(grid, i, j, visited);
                    islands++;
                }
            }
        }
        return islands;
    }

    //Start from a piece of land and traverse the whole island through BFS
    private void bfs(char[][] grid, int x, int y, boolean[][] visited) {
        Queue<Coordinate> queue = new ArrayDeque<>();
        queue.offer(new Coordinate(x, y));
        visited[x][y] = true;

        while (!queue.isEmpty()) {
            Coordinate coor = queue.poll();
            //Traverse up, down, left and right directions
            for (int direction = 0; direction < 4; direction++) {
                int newX = coor.x + deltaX[direction];
                int newY = coor.y + deltaY[direction];
                //If it is not a valid point, continue
                if (!isValid(grid, newX, newY, visited)) {
                    continue;
                }
                queue.offer(new Coordinate(newX, newY));
                //Once you join the team, mark that this store has participated in BFS
                visited[newX][newY] = true;
            }
        }
    }

    //Judge whether a point can be BFS
    private boolean isValid(char[][] grid, int x, int y, boolean[][] visited) {
        int n = grid.length, m = grid[0].length;
        //If out of bounds, return false
        if (x < 0 || x>=n || y < 0 || y >= m) {
            return false;
        }

        //If you have BFS, do not BFS again, avoid: 1 Dead cycle 2 Redundant BFS variables
        if (visited[x][y]) {
            return false;
        }

        //true if '1'; false if 0
        return grid[x][y] == '1' ? true : false;
    }
}

Reflection record

  • Deal with the problem of matrix, the simulation of up, down, left and right coordinate movement, through the offset.
  • Traverse the surrounding islands and use BFS.
  • The processing of details, the representation of points in the coordinate system, the transfer method of visited array (avoiding the use of global variables), and the detection and judgment of boundary values.
  • The ArrayDeque class is used in the queue when implementing BFS.

2,LintCode-611. The shortest route for a knight

/**
* Definition for a point.
* class Point {
*     int x;
*     int y;
*     Point() { x = 0; y = 0; }
*     Point(int a, int b) { x = a; y = b; }
* }
*/
public class Solution {
   //Offset in 8 directions, which can be moved by the knight
   public static final int[] X_OFFSET = {1, 1, -1, -1, 2, 2, -2, -2};
   public static final int[] Y_OFFSET = {2, -2, 2, -2, 1, -1, 1, -1};
   public int shortestPath(boolean[][] grid, Point source, Point destination) {
       //exception handling
       if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) {
           return -1;
       }

       Queue<Point> queue = new ArrayDeque<>();
       //Record the shortest distance from the starting point to (x,y), (x,y) represents the shortest distance to the starting point start
       Map<Integer,Integer> cellToDisMap = new HashMap();

       int col = grid[0].length;
       queue.offer(source);
       //Multiply by col to avoid repetition, and replace two-dimensional coordinates with one-dimensional coordinates
       cellToDisMap.put(source.x * col + source.y, 0);

       while (!queue.isEmpty()) {
           Point point = queue.poll();
           int curPointKey = point.x * col + point.y;
           if (point.x == destination.x && point.y == destination.y) {
               return cellToDisMap.get(curPointKey);
           }

           //Traverse 8 different directions
           for (int i = 0; i < 8; i++) {
               int adjX = point.x + X_OFFSET[i];
               int adjY = point.y + Y_OFFSET[i];
               if (!isValid(adjX, adjY, grid)) {
                   continue;
               }

               //Small routine: convert two-dimensional coordinates into one-dimensional coordinates
               int newPointKey = adjX * col + adjY;
               //If this point has been reached before, it is impossible to find the shortest path at this point of BFS again, which will also cause dead cycle
               if (cellToDisMap.containsKey(newPointKey)) {
                   continue;
               }

               queue.offer(new Point(adjX, adjY));
               cellToDisMap.put(newPointKey, cellToDisMap.get(curPointKey) + 1);
           }
       }

       return -1;
   }

   private boolean isValid(int x, int y, boolean[][] grid) {
       if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length) {
           return false;
       }

       //A value of 1 indicates an obstacle and returns false
       return !grid[x][y];
   }
}

3, Topological Sorting

1. What is topological sorting

In a directed graph, all nodes are sorted, and no node is required to point to the node in front of it.

First count the penetration of all nodes. The nodes with penetration of 0 can be separated, and then reduce the penetration of the node pointed to by this node by one.

Repeat the above operation until all nodes are separated.

If there is no node with a degree of 0 at last, it indicates that there is a ring and there is no topological sorting, that is, there is no solution.

Detailed description:

  • Count the penetration of each point
  • Put each point with a penetration of 0 into the queue as the starting node.
  • Constantly take out a point from the queue, remove all the connected edges of this point (the edges pointing to other points), and the corresponding penetration of other points is - 1
  • Once a new point with a penetration of 0 is found, it is thrown back into the queue.

2,LeetCode-207. Class Schedule Card

[title link]( 207. Curriculum - LeetCode (LeetCode CN. Com))

Problem solving ideas:

Graph + dependency + directed + (ring) = topological sorting

  • Count the penetration of each point
  • Put each point with 0 in degree into the queue as the starting node
  • Constantly take out a point from the queue, remove all the connected edges of this point (the edges pointing to other points), and the corresponding penetration of other points is - 1
  • Once a new point with a penetration of 0 is found, it is thrown back into the queue.

Code implementation:

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        //Construction diagram, representing the mapping of (pre courses - > multiple post courses)
        List[] graph = new ArrayList[numCourses];
        //Initialization of diagram, each course - > empty course List
        for (int i = 0; i < numCourses; i++) {
            graph[i] = new ArrayList<Integer>();
        }

        //1. Count the penetration of each point and build a graph
        int[] inDegree = new int[numCourses];
        for (int[] edge : prerequisites) {
            //For example [0,1]: if you want to take course 0, learn course 1 first, then the entry of 0 will be increased by 1.
            //That is, the arrow of the directed graph indicates the dependency. You can only take course 0 after taking course 1
            //Note that here is the mapping of courses to be taken first - > courses to be taken later, i.e. courses to be taken or courses to be taken later
            graph[edge[1]].add(edge[0]);
            inDegree[edge[0]]++;
        }

        Queue queue = new LinkedList();

        //2. Put each point with 0 in degree into the queue as the starting node
        for (int i = 0; i < inDegree.length; i++) {
            if (inDegree[i] == 0) {
                queue.add(i);
            }
        }

        //Record the number of courses taken
        int numChoose = 0;
        //Record topology order
        int[] topoOrder = new int[numCourses];

        //3. Constantly take out a point from the queue, remove all connected edges of this point (edges pointing to other points), and the corresponding penetration of other points is - 1
        while (!queue.isEmpty()) {
            int nowPos = (int) queue.poll();
            //Record courses taken
            topoOrder[numChoose] = nowPos;
            numChoose++;
            //From this course to other courses, traverse and reduce the entry by one
            for (int i = 0; i < graph[nowPos].size(); i++) {
                int nextPos = (int) graph[nowPos].get(i);
                //The neighbor's degree at the current point is reduced by one, which means that a prerequisite course for each subsequent course has been completed
                inDegree[nextPos]--;
                //4. Once a new point with a penetration of 0 is found, it will be thrown back into the queue
                //Indicates that all the prerequisite courses of a later course have been completed and can be taken
                if (inDegree[nextPos] == 0) {
                    queue.add(nextPos);
                }
            }
        }
        //If topological sorting is required, topoOrder can also be returned here
        return (numChoose == numCourses) ? true : false;
    }
}

Points worth learning:

  • When defining pre courses and post courses, they are represented by a List. The pre courses are placed at the front and the post courses are placed at the back.
  • In the definition of this graph, the List[] graph is ingeniously combined with the course subscript and array subscript.

4, BFS summary

  • Those who can use BFS should not use DFS
  • BFS usage scenarios
    • Connected block problem
    • Hierarchical traversal problem
    • Topological sorting problem
  • Whether level traversal is required
    • One more loop is required, queue size()
  • Matrix coordinate transformation array

Keywords: Algorithm

Added by lamajlooc on Mon, 31 Jan 2022 15:30:03 +0200