leecode-130-被围绕的区域

这篇博客探讨了解决LeetCode第130题的两种方法:一是使用深度优先搜索(DFS)进行递归遍历,二是利用并查集(Union-Find)的数据结构来解决问题。这两种方法都来自于labuladong的算法小抄。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

方法一:dfs递归遍历

class Solution {
    public void solve(char[][] board) {
        if (board.length == 0 || board[0].length == 0)
		return;
        if (board.length < 2 || board[0].length < 2)
        return;
    // 表示行
	int m = board.length;
    // 表示列
    int n = board[0].length;

	// 第一列和最后一列, 把 'O' 变成 '*'
	for (int i = 0; i < m; i++) {
		if (board[i][0] == 'O')
			dfs(board, i, 0);
		if (board[i][n-1] == 'O')
			dfs(board, i, n-1);	
	}
	// 第一行和最后一行, 把 'O' 变成 '*'
	for (int j = 0; j < n; j++) {
		if (board[0][j] == 'O')
			dfs(board, 0, j);
		if (board[m-1][j] == 'O')
			dfs(board, m-1, j);	
	}
	// 循环遍历所有点,把 'O' 变成 'X', '*' 变成 'O'
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++) {
			if (board[i][j] == 'O')
				board[i][j] = 'X';
			else if (board[i][j] == '*')
				board[i][j] = 'O';
		}
	}
}
    // 递归遍历将边界 'O' 和内部与之相连的 'O' 变成 '*'
    public void dfs(char[][] board, int i, int j) {
        // board[i][j] == '*' 说明已经递归遍历过 
        if (i < 0 || j < 0 || i >= board.length  || j >= board[0].length || board[i][j] == 'X' || board[i][j] == '*') {
            return;
        }
        board[i][j] = '*';
        // 上
        dfs(board, i - 1, j); 
        // 下
        dfs(board, i + 1, j); 
        // 左
        dfs(board, i, j - 1); 
        // 右
        dfs(board, i, j + 1); 
    }
}

在这里插入图片描述
方法二:union-find 并查集 (转自labuladong的算法小抄)

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

    int m = board.length;
    int n = board[0].length;
    // 给 dummy 留一个额外位置
    UF uf = new UF(m * n + 1);
    int dummy = m * n;
    // 将首列和末列的 O 与 dummy 连通
    for (int i = 0; i < m; i++) {
        if (board[i][0] == 'O')
            uf.union(i * n, dummy);
        if (board[i][n - 1] == 'O')
            uf.union(i * n + n - 1, dummy);
    }
    // 将首行和末行的 O 与 dummy 连通
    for (int j = 0; j < n; j++) {
        if (board[0][j] == 'O')
            uf.union(j, dummy);
        if (board[m - 1][j] == 'O')
            uf.union(n * (m - 1) + j, dummy);
    }
    // 方向数组 d 是上下左右搜索的常用手法
    int[][] d = new int[][]{{1,0}, {0,1}, {0,-1}, {-1,0}};
    for (int i = 1; i < m - 1; i++) 
        for (int j = 1; j < n - 1; j++) 
            if (board[i][j] == 'O')
                // 将此 O 与上下左右的 O 连通
                for (int k = 0; k < 4; k++) {
                    int x = i + d[k][0];
                    int y = j + d[k][1];
                    if (board[x][y] == 'O')
                        uf.union(x * n + y, i * n + j);
                }
    // 所有不和 dummy 连通的 O,都要被替换
    for (int i = 1; i < m - 1; i++) 
        for (int j = 1; j < n - 1; j++) 
            if (!uf.connected(dummy, i * n + j))
                board[i][j] = 'X';
    }
}

class UF {
    // 记录连通分量个数
    private int count;
    // 存储若干棵树
    private int[] parent;
    // 记录树的“重量”
    private int[] size;

    public UF(int n) {
        this.count = n;
        parent = new int[n];
        size = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }

    /* 将 p 和 q 连通 */
    public void union(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        if (rootP == rootQ)
            return;

        // 小树接到大树下面,较平衡
        if (size[rootP] > size[rootQ]) {
            parent[rootQ] = rootP;
            size[rootP] += size[rootQ];
        } else {
            parent[rootP] = rootQ;
            size[rootQ] += size[rootP];
        }
        count--;
    }

    /* 判断 p 和 q 是否互相连通 */
    public boolean connected(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        // 处于同一棵树上的节点,相互连通
        return rootP == rootQ;
    }

    /* 返回节点 x 的根节点 */
    private int find(int x) {
        while (parent[x] != x) {
            // 进行路径压缩
            parent[x] = parent[parent[x]];
            x = parent[x];
        }
        return x;
    }

    public int count() {
        return count;
    }
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值