LC-130被围绕的区域 | LC-547省份数量

LC-130 被围绕的区域

在这里插入图片描述

问题转化:如何寻找和边界联通的 ‘O’。

1.先遍历最外圈(左右,上下),找到边界连通的 ‘O’。

  • 找到后可以进行dfs递归或者用队列,栈进行坐标位置存储。

2.在递归中,或者在队列,栈中,对其四个方向进行遍历。

  • 一直递归下去,或者类似二叉树层序遍历,一边遍历,一边往队列或栈中添加元素。

解题方法:

  • dfs,递归。

  • dfs,非递归,用栈。

  • bfs,非递归,用队列。

  • 并查集,

// dfs, 递归
class Solution {

    // 上下左右的偏移。{x,y}={row, col}。分别代表行和列。
    private int[][] directions = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
    private int row, col;

    public void solve(char[][] board) {
        if (board.length == 0) return;

        row = board.length; // 行
        col = board[0].length; // 列
        
        // 左右边缘
        for (int i = 0; i < row; i++){
            dfs(board, i, 0);
            dfs(board, i, col - 1);
        }
        // 上下边缘
        for (int j = 0; j < col; j++) {
            dfs(board, 0, j);
            dfs(board, row - 1, j);
        }
        // 最后进行一次遍历
        for (int i = 0; i < row; i++){
            for (int j = 0; j < col; j++) {
                if (board[i][j] == 'T'){
                    board[i][j] = 'O';
                // else 不能少
                } else  if (board[i][j] == 'O'){
                    board[i][j] = 'X';
                }
            }
        }
    }

    private void dfs(char[][] board, int r, int c) {
        if (r < 0 || r >= row || c < 0 || c >= col || board[r][c] != 'O'){
            return;
        }
        board[r][c] = 'T';
        for (int[] d : directions){
            dfs(board, r + d[0], c + d[1]);
        }
    }
}
// 深度优先, 非递归, (栈)
class Solution {
    // row, col
    private int[][] directions = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
    private int row, col;
    public void solve(char[][] board) {
        if (board.length == 0) return;
        row = board.length;
        col = board[0].length;
        // 遍历边缘
        for (int i = 0; i < row; i++){
            for (int j =0; j < col; j++){
                boolean isEdge = i == 0 || j == 0 || i == row - 1 || j == col - 1;
                if (isEdge && board[i][j] == 'O') {
                    dfs(board, i, j);
                }
            }
        }
        // 最后进行一次遍历
        for (int i = 0; i < row; i++){
            for (int j = 0; j < col; j++) {
                if (board[i][j] == 'O'){
                    board[i][j] = 'X';
                } else if (board[i][j] == 'T'){
                    board[i][j] = 'O';
                }
            }
        }
        
    }

    private void dfs(char[][] board, int r, int c){
        Stack<int[]> stack = new Stack<>();
        stack.push(new int[]{r, c});
        board[r][c] = 'T';
        while (! stack.isEmpty()) {
            // 栈顶元素,不弹出
            int[] pos = stack.peek();
            boolean flag = true;
            // 上下左右四个元素进行遍历
            for (int[] d : directions) {
                int mr = pos[0] + d[0], mc = pos[1] + d[1];
                if (mr < 0 || mr >= row || mc < 0 || mc >= col || board[mr][mc] != 'O'){
                    continue;
                }
                // if (){
                    stack.push(new int[]{mr, mc});
                    board[mr][mc] = 'T';
                    flag = false;
                // }
            }
            // 当上一步找到了元素,则该元素不会被弹出。
            if (flag){
                stack.pop();
            }
        }
    }
}
// bfs,非递归,(队列)
class Solution {

    // row,col
    private int[][] directions = {{-1, 0},{0, 1},{1, 0},{0, -1}};
    private int row, col;

    public void solve(char[][] board) {
        if (board.length == 0) return;
        row = board.length;
        col = board[0].length;
        // 存储边缘上的'O'的坐标
        Queue<int[]> queue = new LinkedList<>();
        // 左右边缘
        for (int i = 0; i < row; i++){
            if (board[i][0] == 'O'){
                queue.offer(new int[]{i, 0});
                board[i][0] = 'T';
            }
            if (board[i][col - 1] == 'O'){
                queue.offer(new int[]{i, col - 1});
                board[i][col - 1] = 'T';
            }
        }
        // 上下边缘
        for (int j = 1; j < col - 1; j++){
            if (board[0][j] == 'O'){
                queue.offer(new int[]{0, j});
                board[0][j] = 'T';
            }
            if (board[row - 1][j] == 'O') {
                queue.offer(new int[]{row - 1, j});
                board[row - 1][j] = 'T';
            }
        }
        // 对队列中存储的位置,进行上下左右四个方向的遍历
        while (!queue.isEmpty()){
            int[] pos = queue.poll();
            int r = pos[0], c = pos[1];
            for (int[] d : directions){
                int mr = r + d[0], mc = c + d[1];
                if (mr < 0 || mr >= row || mc < 0 || mc >= col || board[mr][mc] != 'O'){
                    continue;
                }
                queue.offer(new int[]{mr, mc});
                board[mr][mc] = 'T';
            }
        }
        // 最后进行一次遍历
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (board[i][j] == 'O'){
                    board[i][j] = 'X';
                } else if (board[i][j] == 'T') {
                    board[i][j] = 'O';
                }
            }
        }
        
    }
}
// 并查集,求连通区域
class UnionFind{
    // 用一维数组表示并查集
    int[] parents;
    
    public UnionFind(int totalNodes) {
        parents = new int[totalNodes];
        for (int i = 0; i < totalNodes; i++) {
            // 初始化父节点,下一个节点都指向自身
            parents[i] = i;
        }
    }

    // 合并两个节点所在的连通区域
    public void union(int node1, int node2){
        int root1 = find(node1);
        int root2 = find(node2);
        if (root1 != root2) {
            parents[root2] = root1;
        }
    }

    // 查找该节点的根节点
    public int find(int node) {
        // 父节点的特征,指向自己
        while (parents[node] != node) {
            // 父节点 指向 父节点的父节点
            parents[node] = parents[parents[node]];
            node = parents[node];
        }
        return node;
    }

    // 判断两个节点是否在一个连通区域中
    boolean isConnected(int node1, int node2) {
        return find(node1) == find(node2);
    }
}

class Solution{
    private int rows, cols;
    public void solve(char[][] board) {
        if (board == null || board.length == 0)
            return;

        rows = board.length;
        cols = board[0].length;

        // 用一个虚拟节点, 边界上的O 的父节点都是这个虚拟节点
        UnionFind uf = new UnionFind(rows * cols + 1);
        int dummyNode = rows * cols;

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == 'O') {
                    // 遇到O进行并查集操作合并
                    if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) {
                        // 边界上的O,把它和dummyNode 合并成一个连通区域.
                        uf.union(node(i, j), dummyNode);
                    } else {
                        // 和上下左右合并成一个连通区域.
                        if (i > 0 && board[i - 1][j] == 'O')
                            uf.union(node(i, j), node(i - 1, j));
                        if (i < rows - 1 && board[i + 1][j] == 'O')
                            uf.union(node(i, j), node(i + 1, j));
                        if (j > 0 && board[i][j - 1] == 'O')
                            uf.union(node(i, j), node(i, j - 1));
                        if (j < cols - 1 && board[i][j + 1] == 'O')
                            uf.union(node(i, j), node(i, j + 1));
                    }
                }
            }
        }

        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (uf.isConnected(node(i, j), dummyNode)) {
                    // 和dummyNode 在一个连通区域的,那么就是O;
                    board[i][j] = 'O';
                } else {
                    board[i][j] = 'X';
                }
            }
        }
    }

    // 将二维坐标用表示成一维坐标
    private int node(int i, int j) {
        return i * cols + j;
    }
}

代码小技巧:

// 定义一个二维数组,不需要显示的new一个对象。
// {row,col}
private int[][] directions = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
for (int[] d : directions) {
    int mr = r + d[0], mc = c + d[1];
}

LC-547 省份数量

在这里插入图片描述

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

    private void dfs(int[][] isConnected, boolean[] visited, int n, int i) {
        for (int j = 0; j < n; j++) {
            // 从0开始,包含了[i][i]坐标,可以将i设为true。
            if (isConnected[i][j] == 1 && !visited[j]) {
                visited[j] = true;
                dfs(isConnected, visited, n, j);
            }
        }
    }
}
// bfs
class Solution {
    public int findCircleNum(int[][] isConnected) {
        int n = isConnected.length;
        boolean[] visited = new boolean[n];
        int circle = 0;
        Queue<Integer> queue = new LinkedList<>();
        // 每次广度优先遍历下去,连通的置为true且circle加1
        // 第二次遍历时,已经标记过的不在进行遍历。
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                queue.offer(i);    
                while (!queue.isEmpty()) {
                    int j = queue.poll();
                    visited[j] = true;
                    for (int k = 0; k < n; k++) {
                        if (isConnected[j][k] == 1 && !visited[k]) {
                            queue.offer(k);
                        }
                    }
                }
                circle++;
            }
        }
        return circle;
    }
}

123

// 并查集
class Solution {
    public int findCircleNum(int[][] isConnected) {
        int provinces = isConnected.length;
        int[] parent = new int[provinces];
        for (int i = 0; i < provinces; i++) {
            parent[i] = i;
        }
        for (int i = 0; i < provinces; i++) {
            for (int j = i + 1; j < provinces; j++) {
                if (isConnected[i][j] == 1) {
                    union(parent, i, j);
                }
            }
        }
        int circles = 0;
        // 省份的数量就是,并查集中根节点的数量
        for (int i = 0; i < provinces; i++) {
            // 根节点
            if (parent[i] == i) {
                circles++;
            }
        }
        return circles;
    }

    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];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值