Complete leetcode intensive lecture of algorithm interview of large factory 23. And check the collection

Complete leetcode intensive lecture of algorithm interview of large factory 23. And check the collection

Video Explanation (efficient learning): Click to learn

catalog:

1. Introduction

2. Time and space complexity

3. Dynamic planning

4. Greed

5. Binary search

6. Depth first & breadth first

7. Double pointer

8. Sliding window

9. Bit operation

10. Recursion & divide and conquer

11 Pruning & backtracking

12. Reactor

13. Monotone stack

14. Sorting algorithm

15. Linked list

16.set&map

17. Stack

18. Queue

19. Array

20. String

21. Trees

22. Dictionary tree

23. Consolidation

24. Other types of questions

Union & find: used for merging and querying some elements

Find: determines which subset an element belongs to. It can be used to determine whether two elements belong to the same subset. Path compression is added, and the complexity is close to O(1)

Union: merge two subsets into the same set

//     0,1,2,3
//parent:  0,1,2,3
//size:   1,1,1,1
class UnionFind{
    constructor(n){ //Construct a set of size n
        this.count = n
        this.parent = new Array(n)   
        this.size = new Array(n)  // The size array records the size of each tree
        for (let i = 0; i < n; i++) {
            this.parent[i] = i; // You are your own parent
            this.size[i] = 1;
        }
    }

    union(p,q){ //Connected node p and node q, p and q are indexes
        let rootP = this.find(p);
        let rootQ = this.find(q);
        if(rootP === rootQ) return
        // A small number of elements are connected to a large number of elements, which is more balanced
        if (this.size[rootP] > this.size[rootQ]) {
            this.parent[rootQ] = rootP;
            this.size[rootP] += this.size[rootQ];
        } else {
            this.parent[rootP] = rootQ;
            this.size[rootQ] += this.size[rootP];
        }
        this.count--;
    }

    isConnected(p, q) { //Judge whether P and Q are connected
        return this.find(p)=== this.find(q) 
    }

    find(x) { //Find the root of node x
        while (this.parent[x] != x) {
            // Path compression
            this.parent[x] = this.parent[this.parent[x]];
            x = this.parent[x];
        }
        return x;
    }

    getCount() { //Returns the number of subsets
        return this.count;
    }
}

//     0,1,2,3
//parent:  0,1,2,3
//rank:   1,1,1,1
//Using rank optimization
class UnionFind {
    constructor(n) { //Construct a set with n nodes
        this.count = n //Total number of concurrent queries
        this.parent = new Array(n)
        this.rank = new Array(n)  // The rank array records the weight of each tree
        for (let i = 0; i < n; i++) {
            this.parent[i] = i; // You are your own parent
            this.rank[i] = 1; //Number of nodes on each collection
        }
    }

    union(p, q) { //Connected node p and node q, p and q are indexes
        let rootP = this.find(p);
        let rootQ = this.find(q);
        if (rootP === rootQ) return
        // The elements with small depth are connected under the elements with large depth
        if (this.rank[rootP] > this.rank[rootQ]) {
            this.parent[rootQ] = rootP;
        } else if (this.rank[rootP] < this.rank[rootQ]) {
            this.parent[rootP] = rootQ;
        } else {
            this.parent[rootP] = rootQ;
            this.rank[rootQ]++
        }
        this.count--;
    }

    isConnected(p, q) { //Judge whether P and Q are connected
        return this.find(p) === this.find(q)
    }

    find(x) { //Find the root of node x
        while (this.parent[x] != x) {
            // Path compression
            this.parent[x] = this.parent[this.parent[x]];
            x = this.parent[x];
        }
        return x;
    }

    getCount() { //Returns the number of subsets
        return this.count;
    }
}

200. Number of islands (medium)

The animation is too large. Click to view it

Method 1.dfs
  • Idea: cycle the grid and traverse the four sides of each coordinate in depth first. Pay attention not to cross the boundary. When encountering land, add 1 and sink the surrounding land, so that the calculation will not be repeated
  • Complexity: time complexity O(mn), m and N are the number of rows and columns. The space complexity is O(mn). In the worst case, all grids need recursion, and the recursion stack depth reaches m * n

js:

const numIslands = (grid) => {
    let count = 0
    for (let i = 0; i < grid.length; i++) {
        for (let j = 0; j < grid[0].length; j++) {//Cyclic grid
            if (grid[i][j] === '1') {//If it is land, count + +,
                count++
                turnZero(i, j, grid)
            }
        }
    }
    return count
}
function turnZero(i, j, grid) {//Sinking the surrounding land
    if (i < 0 || i >= grid.length || j < 0
        || j >= grid[0].length || grid[i][j] === '0') return //Check the validity of coordinates
    grid[i][j] = '0'//Turn the land around into sea water
    turnZero(i, j + 1, grid)
    turnZero(i, j - 1, grid)
    turnZero(i + 1, j, grid)
    turnZero(i - 1, j, grid)
}

java:

class Solution {
    void dfs(char[][] grid, int r, int c) {
        int nr = grid.length;
        int nc = grid[0].length;

        if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
            return;
        }

        grid[r][c] = '0';
        dfs(grid, r - 1, c);
        dfs(grid, r + 1, c);
        dfs(grid, r, c - 1);
        dfs(grid, r, c + 1);
    }

    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;
        int nc = grid[0].length;
        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    dfs(grid, r, c);
                }
            }
        }

        return num_islands;
    }
}
Method 2.bfs
  • Idea: cycle the grid, breadth first traverse the surrounding coordinates, add 1 when encountering land, sink the surrounding land, and do not repeat the calculation of the number of land
  • Complexity: time complexity O(mn),m and N are the number of rows and columns. The spatial complexity is O(min(m, n)), and the queue length needs to be able to accommodate the smaller of M and N in the worst case

js:

const numIslands = (grid) => {
    let count = 0
    let queue = []
    for (let i = 0; i < grid.length; i++) {
        for (let j = 0; j < grid[0].length; j++) {
            if (grid[i][j] === '1') {
                count++
                grid[i][j] = '0' // Mark to avoid repeated traversal
                queue.push([i, j]) //Join queue
                turnZero(queue, grid)
            }
        }
    }
    return count
}
function turnZero(queue, grid) {
    const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]]
    while (queue.length) {//When there are elements in the queue 
        const cur = queue.shift() //Take out the team head element
        for (const dir of dirs) {//Breadth first diffusion in four directions
            const x = cur[0] + dir[0]
            const y = cur[1] + dir[1]
            if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] !== '1') {
                continue
            }//Check coordinate validity
            grid[x][y] = '0' //Sunken land
            queue.push([x, y]) //Four nodes join the queue
        }
    }
}

java:

class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;
        int nc = grid[0].length;
        int num_islands = 0;

        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    grid[r][c] = '0';
                    Queue<Integer> neighbors = new LinkedList<>();
                    neighbors.add(r * nc + c);
                    while (!neighbors.isEmpty()) {
                        int id = neighbors.remove();
                        int row = id / nc;
                        int col = id % nc;
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.add((row-1) * nc + col);
                            grid[row-1][col] = '0';
                        }
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.add((row+1) * nc + col);
                            grid[row+1][col] = '0';
                        }
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.add(row * nc + col-1);
                            grid[row][col-1] = '0';
                        }
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.add(row * nc + col+1);
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }

        return num_islands;
    }
}
Method 3. Joint search
  • Idea:
  • Complexity: the time complexity is O(mn). The time complexity is actually O(mn * f(mn)). F is the complexity when using parallel search path compression, which is a constant, so it can be ignored. m and n are the number of rows and columns. The space complexity is O(mn), and the space of the search set

js:

class UnionFind {
    constructor(n) { //Construct a set with n nodes
        this.count = n //Total number of concurrent queries
        this.parent = new Array(n)
        this.size = new Array(n)  // The size array records the weight of each tree
        for (let i = 0; i < n; i++) {
            this.parent[i] = i; // You are your own parent
            this.size[i] = 1; //Number of nodes on each collection
        }
    }

    union(p, q) { //Connected node p and node q, p and q are indexes
        let rootP = this.find(p);
        let rootQ = this.find(q);
        if (rootP === rootQ) return
        // A small number of elements are connected to a large number of elements, which is more balanced
        if (this.size[rootP] > this.size[rootQ]) {
            this.parent[rootQ] = rootP;
            this.size[rootP] += this.size[rootQ];
        } else {
            this.parent[rootP] = rootQ;
            this.size[rootQ] += this.size[rootP];
        }
        this.count--;
    }

    isConnected(p, q) { //Judge whether P and Q are connected
        return this.find(p) === this.find(q)
    }

    find(x) { //Find the root of node x
        while (this.parent[x] != x) {
            // Path compression
            this.parent[x] = this.parent[this.parent[x]];
            x = this.parent[x];
        }
        return x;
    }

    getCount() { //Returns the number of subsets
        return this.count;
    }

}

var numIslands = function (grid) {
    let m = grid.length
    if (m === 0) return 0
    let n = grid[0].length
    const dummy = -1
    const dirs = [[1, 0], [0, 1]]//Direction array right down
    const uf = new UnionFind(m * n)
    for (let x = 0; x < m; x++) {
        for (let y = 0; y < n; y++)
            if (grid[x][y] === '0') {//If the mesh is 0, merge with dummy
                uf.union(n * x + y, dummy) 
            }
            else if (grid[x][y] === '1') {//If the grid is 1, try down to the right
                for (let d of dirs) {
                    let r = x + d[0]
                    let c = y + d[1]
                    if (r >= m || c >= n) continue //Coordinate legitimacy
                    if (grid[r][c] === '1') { //If 1 is below the right side of the current grid, it will be merged with the current grid
                        uf.union(n * x + y, n * r + c)
                    }
                }
            }
    }
    return uf.getCount()  //Return and query the number of sets minus one
};

Java:

class Solution {
    class UnionFind {
        int count;
        int[] parent;
        int[] rank;

        public UnionFind(char[][] grid) {
            count = 0;
            int m = grid.length;
            int n = grid[0].length;
            parent = new int[m * n];
            rank = new int[m * n];
            for (int i = 0; i < m; ++i) {
                for (int j = 0; j < n; ++j) {
                    if (grid[i][j] == '1') {
                        parent[i * n + j] = i * n + j;
                        ++count;
                    }
                    rank[i * n + j] = 0;
                }
            }
        }

        public int find(int i) {
            if (parent[i] != i) parent[i] = find(parent[i]);
            return parent[i];
        }

        public void union(int x, int y) {
            int rootx = find(x);
            int rooty = find(y);
            if (rootx != rooty) {
                if (rank[rootx] > rank[rooty]) {
                    parent[rooty] = rootx;
                } else if (rank[rootx] < rank[rooty]) {
                    parent[rootx] = rooty;
                } else {
                    parent[rooty] = rootx;
                    rank[rootx] += 1;
                }
                --count;
            }
        }

        public int getCount() {
            return count;
        }
    }

    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;
        int nc = grid[0].length;
        int num_islands = 0;
        UnionFind uf = new UnionFind(grid);
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    grid[r][c] = '0';
                    if (r - 1 >= 0 && grid[r-1][c] == '1') {
                        uf.union(r * nc + c, (r-1) * nc + c);
                    }
                    if (r + 1 < nr && grid[r+1][c] == '1') {
                        uf.union(r * nc + c, (r+1) * nc + c);
                    }
                    if (c - 1 >= 0 && grid[r][c-1] == '1') {
                        uf.union(r * nc + c, r * nc + c - 1);
                    }
                    if (c + 1 < nc && grid[r][c+1] == '1') {
                        uf.union(r * nc + c, r * nc + c + 1);
                    }
                }
            }
        }

        return uf.getCount();
    }
}

547. Number of provinces(medium)

Method 1.dfs
  • Idea: depth first traversal, whether visited records have been accessed, loop the province array, and recursively find the adjacent cities in the isConnected matrix.
  • Complexity: time complexity O(n^2), n is the number of cities, traversing each element in the matrix. Space complexity O(n), recursion depth no more than n

js

var findCircleNum = function(isConnected) {
  const rows = isConnected.length;
  const visited = new Set();//Whether the record has been accessed
  let count = 0;//Number of provinces
  for (let i = 0; i < rows; i++) {
      if (!visited.has(i)) {//If you haven't visited
          dfs(isConnected, visited, rows, i);//Depth first traversal
          count++;//Number of provinces + 1
      }
  }
  return count;
};

const dfs = (isConnected, visited, rows, i) => {
  for (let j = 0; j < rows; j++) {
      if (isConnected[i][j] == 1 && !visited.has(j)) {//If i, j are connected
          visited.add(j);
          dfs(isConnected, visited, rows, j);//Recursive traversal
      }
  }
};

java:

class Solution {
    public int findCircleNum(int[][] isConnected) {
        int rows = isConnected.length;
        boolean[] visited = new boolean[rows];
        int count = 0;
        for (int i = 0; i < rows; i++) {
            if (!visited[i]) {
                dfs(isConnected, visited, rows, i);
                count++;
            }
        }
        return count;
    }

    public void dfs(int[][] isConnected, boolean[] visited, int rows, int i) {
        for (int j = 0; j < rows; j++) {
            if (isConnected[i][j] == 1 && !visited[j]) {
                visited[j] = true;
                dfs(isConnected, visited, rows, j);
            }
        }
    }
}
Method 2.bfs
  • Idea: breadth first traversal, follow the matrix, and then find adjacent cities to join the queue. If the queue is not empty, keep out of the queue and continue traversal
  • Complexity: time complexity O(n^2), n is the number of cities, traversing each element in the matrix. The space complexity is O(n), and the longest queue and visited array is n

js:

var findCircleNum = function(isConnected) {
  const rows = isConnected.length;
  const visited = new Set();//Whether the record has been accessed
  let count = 0;
  const queue = new Array();
  for (let i = 0; i < rows; i++) {
      if (!visited.has(i)) {//Not visited
          queue.push(i); //Join queue
          while (queue.length) {//The queue is not empty to continue the cycle
              const j = queue.shift();//Out of the team
              visited.add(j);
              for (let k = 0; k < rows; k++) {//Loop neighboring cities to join the queue
                  if (isConnected[j][k] === 1 && !visited.has(k)) {
                      queue.push(k);
                  }
              }
          }
          count++;
      }
  }
  return count;
};

Java:

class Solution {
    public int findCircleNum(int[][] isConnected) {
        int rows = isConnected.length;
        boolean[] visited = new boolean[rows];
        int count = 0;
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < rows; i++) {
            if (!visited[i]) {
                queue.offer(i);
                while (!queue.isEmpty()) {
                    int j = queue.poll();
                    visited[j] = true;
                    for (int k = 0; k < rows; k++) {
                        if (isConnected[j][k] == 1 && !visited[k]) {
                            queue.offer(k);
                        }
                    }
                }
                count++;
            }
        }
        return count;
    }
}
Method 3. Joint search
  • Idea: circular matrix, merge when encountering adjacent cities, and finally return and check the number of sets in the set
  • Complexity: time complexity O(n^2). N is the number of cities. It needs to traverse the matrix. After path compression, it needs to find the parent node in the joint query set. The complexity is constant. The space complexity is O(n), that is, the space of the parent

js:

class UnionFind{
    constructor(n){ //Construct a set of size n
        this.count = n
        this.parent = new Array(n)   
        this.size = new Array(n)  // The size array records the size of each tree
        for (let i = 0; i < n; i++) {
            this.parent[i] = i; // You are your own parent
            this.size[i] = 1;
        }
    }

    union(p,q){ //Connected node p and node q, p and q are indexes
        let rootP = this.find(p);
        let rootQ = this.find(q);
        if(rootP === rootQ) return
        // A small number of elements are connected to a large number of elements, which is more balanced
        if (this.size[rootP] > this.size[rootQ]) {
            this.parent[rootQ] = rootP;
            this.size[rootP] += this.size[rootQ];
        } else {
            this.parent[rootP] = rootQ;
            this.size[rootQ] += this.size[rootP];
        }
        this.count--;
    }

    isConnected(p, q) { //Judge whether P and Q are connected
        return this.find(p)=== this.find(q) 
    }

    find(x) { //Find the root of node x
        while (this.parent[x] != x) {
            // Path compression
            this.parent[x] = this.parent[this.parent[x]];
            x = this.parent[x];
        }
        return x;
    }

    getCount() { //Returns the number of subsets
        return this.count;
    }
}


var findCircleNum = function(isConnected) {
    const rows = isConnected.length;
    const uf = new UnionFind(rows)

    for (let i = 0; i < rows; i++) {
        for (let j = i + 1; j < rows; j++) {
            if (isConnected[i][j] == 1) {//Merger of adjacent cities
                uf.union(i, j);
            }
        }
    }

    return uf.getCount();
};

Java:

class Solution {
    public int findCircleNum(int[][] isConnected) {
        int rows = isConnected.length;
        int[] parent = new int[rows];
        for (int i = 0; i < rows; i++) {
            parent[i] = i;
        }
        for (int i = 0; i < rows; i++) {
            for (int j = i + 1; j < rows; j++) {
                if (isConnected[i][j] == 1) {
                    union(parent, i, j);
                }
            }
        }
        int count = 0;
        for (int i = 0; i < rows; i++) {
            if (parent[i] == i) {
                count++;
            }
        }
        return count;
    }

    public void union(int[] parent, int index1, int index2) {
        parent[find(parent, index1)] = find(parent, index2);
    }

    public int find(int[] parent, int index) {
        if (parent[index] != index) {
            parent[index] = find(parent, parent[index]);
        }
        return parent[index];
    }
}

Posted by coily on Mon, 06 Dec 2021 19:00:24 -0800