粉碎糖果
难度:
标签:
题目描述
代码结果
运行时间: 69 ms, 内存: 16.1 MB
/*
题目思路:
1. 使用Stream API进行数组转换和处理。
2. 扫描整个糖果板,看是否有三个或三个以上相同的糖果在一行或一列。如果找到,标记这些糖果。
3. 把被标记的糖果消除,并让上面的糖果掉下来填补空缺。
4. 重复上述步骤,直到没有更多的糖果可以被消除。
*/
import java.util.Arrays;
public class CandyCrushStream {
public int[][] candyCrush(int[][] board) {
int rows = board.length;
int cols = board[0].length;
boolean[] toCrushRow = new boolean[rows * cols];
boolean[] toCrushCol = new boolean[rows * cols];
Arrays.stream(board).flatMapToInt(Arrays::stream).forEach(candy -> {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols - 2; j++) {
if (board[i][j] != 0 && board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2]) {
toCrushRow[i * cols + j] = toCrushRow[i * cols + j + 1] = toCrushRow[i * cols + j + 2] = true;
}
}
}
for (int j = 0; j < cols; j++) {
for (int i = 0; i < rows - 2; i++) {
if (board[i][j] != 0 && board[i][j] == board[i + 1][j] && board[i][j] == board[i + 2][j]) {
toCrushCol[i * cols + j] = toCrushCol[(i + 1) * cols + j] = toCrushCol[(i + 2) * cols + j] = true;
}
}
}
});
boolean toCrush = Arrays.stream(toCrushRow).anyMatch(c -> c) || Arrays.stream(toCrushCol).anyMatch(c -> c);
if (toCrush) {
for (int j = 0; j < cols; j++) {
int idx = rows - 1;
for (int i = rows - 1; i >= 0; i--) {
if (!toCrushRow[i * cols + j] && !toCrushCol[i * cols + j]) {
board[idx--][j] = board[i][j];
}
}
while (idx >= 0) {
board[idx--][j] = 0;
}
}
}
return toCrush ? candyCrush(board) : board;
}
}
解释
方法:
这个题解采用了两次遍历的方法来解决粉碎糖果问题。第一次遍历中,对每个非零元素,检查其水平和垂直方向上是否存在连续三个相同的数字。如果存在,则将这些数字标记为待消除状态(取相反数)。第二次遍历中,对每一列进行处理,将非零元素移动到当前列的底部,其余位置填充为0。重复这个过程,直到不存在可消除的相同数字为止。
时间复杂度:
O(m^2 * n^2)
空间复杂度:
O(1)
代码细节讲解
🦆
在算法中,如何确保在每一次循环中,标记待消除状态的数字不会影响正在检查的其他连续三个数字的判断?
▷🦆
为什么选择使用取相反数的方法来标记待消除的数字,而不是使用其他标记方式如特殊值或者布尔数组?
▷🦆
在处理列元素移动时,有没有可能出现在上移动过程中误将已标记为待消除的数字当作有效数字处理的情况?
▷🦆
这种方法中,如何处理边界条件,例如矩阵的最上行或最左列?
▷