【LeetCode - 723】粉碎糖果

1、题目描述

在这里插入图片描述

2、解题思路

  本题主要有两个步骤:找出需要粉碎的糖果所在的格子、粉碎糖果。

  定义一个变量 todo 作为是否需要粉碎的标记。

  一行一行遍历:当遇到三个及以上连续相同的值,则标记为原值的负数形式,并 todo = true;

  一列一列遍历,当遇到三个及以上连续相同的值,则标记为原值的负数形式,并 todo = true;

  粉碎糖果的逻辑是:

  1、从左到右遍历每一列,定义一个值 wr 初始值为最后一行的索引 rows - 1;
在这里插入图片描述

  2、wr 行的含义是 “接收上面掉下来的糖果” 的所在行,因此先从最低行往上遍历,直到遇到第一个待粉碎的糖果,wr 的初始值就是这一行;
在这里插入图片描述

  3、wr 确定后,r 继续往上走,直到遇到不需要粉碎的糖果,就把它掉下去,落在 wr 所在的格子里。然后 wr 往上走一格,r 继续往上找需要掉下来的糖果。
在这里插入图片描述
在这里插入图片描述

  4、当 r 走到最上面了,此时 wr 还在中间某个为止,说明掉落的糖果已经全部处理完毕,wr 往上全都是粉碎后的空白区。
在这里插入图片描述
  我们粉碎结束后,其实不确定新的状态是否有新的要粉碎的糖果,因此,只要本次进行过粉碎,就得再执行一次上面两个流程,如果再执行一次却从头到尾 todo 都是 false,则说明算法结束,已经到了稳定的状态。

3、解题代码

class Solution {

    public int[][] candyCrush(int[][] board) {
        int rows = board.length, cols = board[0].length;
        boolean todo = false;   // 是否存在要粉碎的糖果
        // 一行一行扫描
        for (int r = 0; r < rows; ++r) {
            for (int c = 0; c + 2 < cols; ++c) {
                // 取出这个点的绝对值(可能被取反了,所以要绝对值)
                int v = Math.abs(board[r][c]);
                // 如果连续三个格子是相同的糖果
                if (v != 0 && v == Math.abs(board[r][c + 1]) && v == Math.abs(board[r][c + 2])) {
                    // 把这三个连续格子的糖果数值取反,表明待粉碎状态
                    board[r][c] = board[r][c + 1] = board[r][c + 2] = -v;
                    todo = true;
                }
            }
        }
        // 一列一列扫描
        for (int r = 0; r + 2 < rows; ++r) {
            for (int c = 0; c < cols; ++c) {
                // 取出这个点的绝对值(可能被取反了,所以要绝对值)
                int v = Math.abs(board[r][c]);
                // 如果连续三个格子是相同的糖果
                if (v != 0 && v == Math.abs(board[r + 1][c]) && v == Math.abs(board[r + 2][c])) {
                    // 把这三个连续格子的糖果数值取反,表明待粉碎状态
                    board[r][c] = board[r + 1][c] = board[r + 2][c] = -v;
                    todo = true;
                }
            }
        }
        // 经过上面两个 for 循环后,需要粉碎糖果的格子已经变为负数

        // 遍历所有格子进行粉碎糖果
        if (todo) {
            for (int c = 0; c < cols; ++c) {   // 从左到右每一列
                int wr = rows - 1;  // 接收掉落糖果所在行
                for (int r = rows - 1; r >= 0; r--) {  // 从下往上遍历每一行
                    if (board[r][c] > 0) {
                        // 把 (r,c) 的糖果掉落到 (wr,c) 上
                        // r 和 wr 要么在同一行,要么 r 在上面,因此不用特地找到 wr 的初始行。
                        board[wr][c] = board[r][c]; 
                        wr--;   // 往上走一行
                    }
                }
                // 如果 wr 没有走到最顶行,说明上面应该全是要粉碎的
                while (wr >= 0) {
                    board[wr--][c] = 0;
                }
            }
        }
        // 如果还有需要粉碎的糖果,则再调用一次 candyCrush(board)
        // 注意,本次 candyCrush 后是不确定存不存在新的要粉碎的糖果,只能再调用一次 candyCrush
        // 如果多调用的 candyCrush 中两个 for 循环都没有把 tod0 标记为 true,则表示结束了
        // 因此,本方法都会多调用一次 candyCrush 但不进行粉碎的操作。
        return todo ? candyCrush(board) : board;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值