547. 省份数量

547. 省份数量

547. 省份数量

难度中等

n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:

img

输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2

示例 2:

img

输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

提示:

  • 1 <= n <= 200
  • n == isConnected.length
  • n == isConnected[i].length
  • isConnected[i][j]10
  • isConnected[i][i] == 1
  • isConnected[i][j] == isConnected[j][i]

函数签名:

func findCircleNum(isConnected [][]int) int

分析

可以把 n 个城市和它们之间的相连关系看成一张图,城市就是各个顶点,相连关系就是图中的边,isConnected 就是其邻接矩阵,最终求省份数,就是求联通分量的个数。

DFS

func findCircleNum(isConnected [][]int) int {
	n := len(isConnected)
	marked := make([]bool, n)
	res := 0
	for i, v := range marked {
		if v {
			continue
		}
		res++
		mark(i, marked, isConnected)
	}
	return res
}

// 将和城市 from 联通的城市都标记, dfs
func mark(from int, marked []bool, isConnected [][]int) {
	marked[from] = true
	for to, conn := range isConnected[from] {
		if conn == 1 && !marked[to] {
			mark(to, marked, isConnected)
		}
	}
}

时间复杂度 O(n^2), 空间复杂度 O(n)

BFS

框架同 dfs, 只是 mark 方法用 bfs:

// 将和城市 from 联通的城市都标记, bfs
func mark(from int, marked []bool, isConnected [][]int) {
	queue := list.New()
	queue.PushBack(from)
	marked[from] = true
	for queue.Len() > 0 {
		cur := queue.Remove(queue.Front()).(int)
		for next, conn := range isConnected[cur] {
			if conn == 1 && !marked[next] {
				queue.PushBack(next)
				marked[next] = true
			}
		}
	}
}

复杂度同上。

并查集

func findCircleNum(isConnected [][]int) int {
    n := len(isConnected)
    uf := NewUnionFind(n)
    for r := 0; r < n; r++ {
        for c := 0; c < n; c++ {
            if isConnected[r][c] == 1 {
                union(uf, r, c)
            }
        }
    }
    return cal(uf)
}

func NewUnionFind(n int) []int {
    uf := make([]int, n)
    for i := range uf {
        uf[i] = i
    }
    return uf
}

func union(uf []int, x, y int) {
    rootX, rootY := find(uf, x), find(uf, y)
    uf[rootX] = rootY
}

func find(uf []int, x int) int {
    for x != uf[x] {
        x, uf[x] = uf[x], uf[uf[x]]
    }
    return x
}

func cal(uf []int) int {
    res := 0
    for i, v := range uf {
        if i == v {
            res++
        }
    }
    return res
}

复杂度同上。并查集的 union 和 find 方法复杂度可以看作常数级。实际上这里做了路径压缩,但没有按秩合并,最坏情况下并查集复杂度是 log n, 但是平均复杂度还是常数级。