LC每日一题|20240611 - 419. 甲板上的战舰
给你一个大小为
m x n的矩阵board表示甲板,其中,每个单元格可以是一艘战舰'X'或者是一个空位'.',返回在甲板board上放置的 战舰 的数量。战舰 只能水平或者垂直放置在
board上。换句话说,战舰只能按1 x k(1行,k列)或k x 1(k行,1列)的形状建造,其中k可以是任意大小。两艘战舰之间至少有一个水平或垂直的空位分隔 (即没有相邻的战舰)。
提示:
m == board.lengthn == board[i].length1 <= m, n <= 200board[i][j]是'.'或'X'
进阶: 你可以实现一次扫描算法,并只使用 O(1) 额外空间,并且不修改 board 的值来解决这个问题吗?
题目等级:Medium
解题思路
这个题目题干翻译的确实太差了。这里求所谓「战舰」的数量,其实应该是「舰队」的数量,一个「舰队」由「战舰」按横向或纵向排成。
这种连通集数量问题有很多的解决方案,比如最基础的BFS。实际上题目用例已经限制了不会出现L型的舰队,所以可以直接使用全向的搜索解决这一问题。
这里贴出一个BFS的写法,实际上是从200. 岛屿数量copy过来的。
AC代码
class Solution {
fun countBattleships(grid: Array<CharArray>): Int {
val direction = arrayOf(
intArrayOf(0, 1), intArrayOf(0, -1),
intArrayOf(1, 0), intArrayOf(-1, 0)
)
val array = IntArray(grid.size * grid[0].size)
for (i in grid.indices) for (j in grid[i].indices) {
if (grid[i][j] == 'X') array[i * grid[i].size + j] = -1
}
var index = 0
while (-1 in array) {
index ++
val point = array.indexOf(-1)
val queue = ArrayDeque<Int>()
queue.addLast(point)
while (queue.isNotEmpty()) {
repeat(queue.size) {
val node = queue.removeFirst()
array[node] = -2
val ox = node / grid[0].size
val oy = node % grid[0].size
for (d in direction) {
val x = ox + d[0]
val y = oy + d[1]
val i = x * grid[0].size + y
while (x in grid.indices && y in grid[0].indices && array[i] == -1) {
array[i] = 0
queue.addLast(i)
}
}
}
}
}
return index
}
}
进阶
很明显BFS的代码是不能满足进阶要求的。
我们可以转换一下思路。一艘战舰会与其左侧与上侧紧邻位置的另一艘战舰出现在一个舰队中,那么我们可以从左上角开始遍历整个board,只要其左侧与上侧紧邻位置都没有战舰,那么该战舰一定不存在于已经遍历过的舰队中,我们只要统计满足这个条件的战舰数量即可。
class Solution {
fun countBattleships(board: Array<CharArray>): Int {
var res = 0
for (i in board.indices) {
for (j in board[i].indices) {
if (board[i][j] != 'X') continue
if (i > 0 && board[i - 1][j] != '.') continue
if (j > 0 && board[i][j - 1] != '.') continue
res++
}
}
return res
}
}