js(85)~200. 岛屿数量

138 阅读2分钟

本题力扣传送门 image.png

这个题,我看助教写的难理解,之前记得写过大圣说的一个找单词的跟着差不错,然后我看了题解 找到了一个根助教思路一样的,但是觉得人家的代码更容易理解,重要的是执行争取了,代码如下

方法一 并查集

并查集能够看懂,但是解体思路看不懂

class UnionFind {
    constructor(n) { //构造一个节点数为n的集合
        this.count = n //并查集总数
        this.parent = new Array(n)
        this.size = new Array(n)  // size数组记录着每棵树的重量
        for (let i = 0; i < n; i++) {
            this.parent[i] = i; // 自己是自己的parent
            this.size[i] = 1;	//每个集合上节点的数量
        }
    }

    union(p, q) { //连通结点p和结点q, p和q都是索引
        let rootP = this.find(p);
        let rootQ = this.find(q);
        if (rootP === rootQ) return
        // 元素数量小的接到数量多的下面,这样比较平衡
        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) { //判断p,q是否连通
        return this.find(p) === this.find(q)
    }

    find(x) { //找到x结点的root
        while (this.parent[x] != x) {
            // 进行路径压缩
            this.parent[x] = this.parent[this.parent[x]];
            x = this.parent[x];
        }
        return x;
    }

    getCount() { //返回子集个数
        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]]//方向数组 向右 向下
    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') {//如果网格是0,则和dummy合并
                uf.union(n * x + y, dummy) 
            }
            else if (grid[x][y] === '1') {//如果网格是1,则向右 向下尝试
                for (let d of dirs) {
                    let r = x + d[0]
                    let c = y + d[1]
                    if (r >= m || c >= n) continue //坐标合法性
                    if (grid[r][c] === '1') { //当前网格的右边 下面如果是1,则和当前网格合并
                        uf.union(n * x + y, n * r + c)
                    }
                }
            }
    }
    return uf.getCount()  //返回并查集的个数减一就行
};

方法二 dfs 思路:循环网格,深度优先遍历每个坐标的四周,注意坐标不要越界,遇到陆地加1,并沉没四周的陆地,这样就不会重复计算

复杂度:时间复杂度O(mn), m和n是行数和列数。空间复杂度是O(mn),最坏的情况下所有网格都需要递归,递归栈深度达到m * n

/**
 * @param {character[][]} grid
 * @return {number}
 */
const numIslands = (grid) => {
    let count = 0
    for (let i = 0; i < grid.length; i++) {
        for (let j = 0; j < grid[0].length; j++) {//循环网格
            if (grid[i][j] === '1') {//如果为陆地,count++,
                count++
                turnZero(i, j, grid)
            }
        }
    }
    return count
}
function turnZero(i, j, grid) {//沉没四周的陆地
    if (i < 0 || i >= grid.length || j < 0
        || j >= grid[0].length || grid[i][j] === '0') return //检查坐标的合法性
    grid[i][j] = '0'//让四周的陆地变为海水
    turnZero(i, j + 1, grid)
    turnZero(i, j - 1, grid)
    turnZero(i + 1, j, grid)
    turnZero(i - 1, j, grid)
}



题外话,这道题我刚开始按照助教讲解敲的,也是对的,就是第一次的时候findSet里面,this.findSet()小括号写成大括号了 一直不对

findSet(index) {
		if (this.parent[index] !== index) {
                // 
			this.parent[index] = this.findSet(this.parent[index])
		}
		return this.parent[index];
	}
/*
 * @lc app=leetcode.cn id=200 lang=javascript
 *
 * [200] 岛屿数量
 */

// @lc code=start
/**
 * @param {character[][]} grid
 * @return {number}
 */
let count;
var numIslands = function (grid) {
	let m = grid.length;
	let n = grid[0].length;
	let uf = new UnionFind(m * n)
	// 对二维数组进行搜索
	count = 0;
	for (let i = 0; i < m; i++) {
		for (let j = 0; j < n; j++) {
			if (grid[i][j] === '1') {
				count++;
			}
		}
	}

	// 搜索 上下左右四方向
	for (let i = 0; i < m; i++) {
		for (let j = 0; j < n; j++) {
			if (grid[i][j] === '1') {
				if (i - 1 >= 0 && grid[i - 1][j] === '1') {
					uf.unite(i * n + j, (i - 1) * n + j)
				}
				if (i + 1 < m && grid[i + 1][j] === '1') {
					uf.unite(i * n + j, (i + 1) * n + j)
				}
				if (j - 1 >= 0 && grid[i][j - 1] === '1') {
					uf.unite(i * n + j, i * n + j - 1)
				}
				if (j + 1 < n && grid[i][j + 1] === '1') {
					uf.unite(i * n + j, i * n + j + 1)
				}
			}
		}
	}

	return count

};
class UnionFind {
	constructor(n) {
		this.parent = new Array(n).fill(0).map((v, i) => i);
		this.rank = new Array(n).fill(1);
		this.setCount = n;
	}

	findSet(index) {
		if (this.parent[index] !== index) {
                // 
			this.parent[index] = this.findSet(this.parent[index])
		}
		return this.parent[index];
	}
	unite(index1, index2) {
		let root1 = this.findSet(index1), root2 = this.findSet(index2);
		if (root1 !== root2) {
			// ---------- start-------
			/* 	// 这一步不离基
				// 就是把节点少的往节点多的合并
				// 方法一
				if (root1 < root2) {
					[root1, root2] = [root2, root1]
				}
				// 合并 这一步明白
				this.parent[root2] = root1;
				// 计算合并的疏朗
				this.rank[root1] += this.rank[root2]; */
				//方法二 下面这种方法也是对的-----
			// 元素数量小的接到数量多的下面,这样比较平衡
			if (this.rank[root1] > this.rank[root2]) {
				this.parent[root2] = root1;
				this.rank[root1] += this.rank[root2];
			} else {
				this.parent[root1] = root2;
				this.rank[root2] += this.rank[root1];
			}
			// ---------- end------
			//合并以后减少
			this.setCount--;
		}
	}
	getCount() {
		return this.setCount;
	}
	connected() {
		let root1 = this.findSet(index1), root2 = this.findSet(index2);
		return root1 === root2;
	}
}
// @lc code=end