五月训练 Day17

0. Leetcode LCP 44. 开幕式焰火

「力扣挑战赛」开幕式开始了,空中绽放了一颗二叉树形的巨型焰火。
给定一棵二叉树 root 代表焰火,节点值表示巨型焰火这一位置的颜色种类。请帮小扣计算巨型焰火有多少种不同的颜色。

分析与解答

直接进行搜索即可,这里因为训练广度优先搜索,因此使用广搜方法:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    set<int> result;
    deque<TreeNode*> deq;
    
public:
    void bfs(TreeNode* root) {
        while (!deq.empty()) {
            // 获取当前节点
            TreeNode* cur = deq.front();
            // 有效进行下一步搜索
            if (cur) {
                // 根据节点更新结果
                result.insert(cur->val);
                // 放入待搜索节点
                deq.push_back(cur->left);
                deq.push_back(cur->right);
            }
            
            deq.pop_front();
        }
    }

    int numColor(TreeNode* root) {
        deq.push_back(root);
        // 节点数大于等于 1
        bfs(root);
        
        return result.size();
    }
};

1. Leetcode 102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

分析与解答

广度优先搜索,没层搜索完后将当前层结果放入结果集中:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    vector<vector<int>> result;
    queue<TreeNode*> q;
    
public:
    void bfs(TreeNode* root) {
        // 初始化
        q.push(root);
        int nodeNum(0);
        int validNode(0);
        int cellNum(1);
        vector<int> curResult;
        
        // 广度搜索
        while (!q.empty()) {
            TreeNode* curNode = q.front();
            q.pop();
            if (curNode) {
                curResult.push_back(curNode->val);
                q.push(curNode->left);
                q.push(curNode->right);
                validNode++;
            } 
            
            nodeNum++;
            if (cellNum == nodeNum) {
                if (curResult.size() > 0) {
                    result.push_back(curResult);
                }
                
                cellNum = validNode * 2; // 每个有效节点有两个子节点
                nodeNum = 0;
                validNode = 0;
                curResult.clear();
            }
        }
    }

    vector<vector<int>> levelOrder(TreeNode* root) {
        bfs(root);
        
        return result;
    }
};

2. Leetcode 1609. 奇偶树

如果一棵二叉树满足下述几个条件,则可以称为 奇偶树 :
二叉树根节点所在层下标为 0 ,根的子节点所在层下标为 1 ,根的孙节点所在层下标为 2 ,依此类推。
偶数下标 层上的所有节点的值都是 奇 整数,从左到右按顺序 严格递增
奇数下标 层上的所有节点的值都是 偶 整数,从左到右按顺序 严格递减
给你二叉树的根节点,如果二叉树为 奇偶树 ,则返回 true ,否则返回 false 。

分析与解答

与上一题类似,使用哈希表记录每个节点对应的深度,若当前节点对应的深度为奇数,则该层应严格递减,反之应严格递增:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    queue<TreeNode*> q; // 广搜使用的队列
    unordered_map<TreeNode*, int> depMap; // 存储节点对应的深度
    
public:
    bool bfs(TreeNode* root) {
        q.push(root);
        depMap[root] = 0;
        int preDep = 0;
        int curNum(0);
        
        while (!q.empty()) {
            TreeNode* curNode = q.front();
            q.pop();
            if (depMap[curNode] != preDep) {
                // 上层结束
                if (depMap[curNode] % 2 == 1) {
                    curNum = INT_MAX;
                } else {
                    curNum = 0;
                }
                preDep = depMap[curNode];
            }
            
            if (curNode->left) { // 放入左子节点
                q.push(curNode->left);
                depMap[curNode->left] = depMap[curNode] + 1;
            }
            if (curNode->right) { // 放入右子节点
                q.push(curNode->right);
                depMap[curNode->right] = depMap[curNode] + 1;
            }
                
            if (depMap[curNode] % 2 == 1) {
                // 奇数层,应为偶数
                if (curNode->val % 2 == 1) {
                    return false;
                }
                if (curNode->val >= curNum) { // 严格递减
                    return false;
                } else {
                    curNum = curNode->val;
                }
            } else {
                // 偶数层,应为奇数
                if (curNode->val % 2 == 0) {
                    return false;
                }
                if (curNode->val <= curNum) { // 严格递增
                    return false;
                } else {
                    curNum = curNode->val;
                }
            }
        }
        
        return true;
    }

    bool isEvenOddTree(TreeNode* root) {
        return bfs(root);
    }
};

3. Leetcode 1263. 推箱子

「推箱子」是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。
游戏地图用大小为 m x n 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。
现在你将作为玩家参与游戏,按规则将箱子 ‘B’ 移动到目标位置 ‘T’ :
玩家用字符 ‘S’ 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
地板用字符 ‘.’ 表示,意味着可以自由行走。
墙用字符 ‘#’ 表示,意味着障碍物,不能通行。
箱子仅有一个,用字符 ‘B’ 表示。相应地,网格上有一个目标位置 ‘T’。
玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
玩家无法越过箱子。
返回将箱子推到目标位置的最小 推动 次数,如果无法做到,请返回 -1。

分析与解答

将箱子与人的位置压缩为一维状态,使用压缩后的一维状态进行判断。**注意:**相同状态下,步长不同时应将该状态放入待搜索集中:

vector<vector<int>> pos2State;
vector<vector<int>> gridMap;
int rows;
int cols;

class Solution {
    struct Pos {
        int x;
        int y;

        Pos() {
        }

        Pos(int inx, int iny) {
            x = inx;
            y = iny;
        }
    };

    struct BFSState {
        Pos person;
        Pos box;
        Pos target;
        int step;

        bool ifFinish() {
            return box.x == target.x && box.y == target.y;
        }

        int getState() { // 状态压缩
            return pos2State[box.x][box.y] * 11250 + pos2State[person.x][person.y];
        }
    };

    BFSState iniPos;
    queue<BFSState> q; // 广搜队列
    unordered_map<int, int> stateMap; // 状态 -> 出现次数
    unordered_map<int, int> stepMap; // 状态 -> 步长
    int dir[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; // 移动状态

public:
    void initialStateMat(vector<vector<char>>& grid) { // 位置初始化
        rows = grid.size();
        cols = grid[0].size();
        int stateId(0);
        Pos boxPos;
        Pos targetPos;
        Pos personSPos;
        
        for (int i = 0; i < grid.size(); ++i) {
            vector<int> curResult;
            vector<int> curMap;
            for (int j = 0; j < grid[i].size(); ++j) {
                if (grid[i][j] == 'B') {
                    boxPos.x = i;
                    boxPos.y = j;
                }
                if (grid[i][j] == 'S') {
                    personSPos.x = i;
                    personSPos.y = j;
                }
                if (grid[i][j] == 'T') {
                    targetPos.x = i;
                    targetPos.y = j;
                }
                if (grid[i][j] == '#') {
                    curMap.push_back(INT_MAX);
                } else {
                    curMap.push_back(1);
                }
                curResult.push_back(stateId++);
            }
            pos2State.push_back(curResult);
            gridMap.push_back(curMap);
        }

        iniPos.person = personSPos;
        iniPos.target = targetPos;
        iniPos.box = boxPos;
    }

    int bfs(vector<vector<char>>& grid) {
        // 状态初始化
        q.push(iniPos);
        stepMap.insert(std::make_pair(iniPos.getState(), 0));
        int minResult(-1);

        // 广度优先搜索
        while (!q.empty()) {
            BFSState curState = q.front();
            q.pop();
            // 判断是否终止
            if (curState.ifFinish()) {
                if (minResult < 0) {
                    minResult = curState.step;
                } else {
                    minResult = minResult > curState.step? curState.step: minResult;
                }
                continue;
            }

            // 尝试向 4 个方向移动
            for (int i = 0; i < 4; ++i) {
                BFSState newState = curState;
                newState.person.x += dir[i][0];
                newState.person.y += dir[i][1];
                // 判断当前位置人是否可达
                if (newState.person.x < 0 || newState.person.x >= rows || 
                    newState.person.y < 0 || newState.person.y >= cols || 
                    gridMap[newState.person.x][newState.person.y] == INT_MAX) {
                    continue;
                }
                // 判断人与箱子位置是否重合
                if (newState.person.x == newState.box.x && newState.person.y == newState.box.y) {
                    newState.box.x += dir[i][0];
                    newState.box.y += dir[i][1];
                    // 判断箱子是否能推动
                    if (newState.box.x < 0 || newState.box.x >= rows || 
                        newState.box.y < 0 || newState.box.y >= cols || 
                        gridMap[newState.box.x][newState.box.y] == INT_MAX) {
                        continue;
                    }
                    newState.step++;
                    // !注意这里要将步长更小的状态作为备选搜索状态!
                    if (newState.step < stepMap[newState.getState()] || stateMap[newState.getState()] == 0) {
                        q.push(newState);
                        stepMap[newState.getState()] = newState.step;
                        stateMap[newState.getState()]++;
                    } else {
                        continue;
                    }
                } else {
                    if (newState.step < stepMap[newState.getState()] || stateMap[newState.getState()] == 0) {
                        q.push(newState);
                        stepMap[newState.getState()] = newState.step;
                        stateMap[newState.getState()]++;
                    } else {
                        continue;
                    }
                }
            }
        }

        return minResult;
    }

    int minPushBox(vector<vector<char>>& grid) {
        pos2State.clear();
        gridMap.clear();
        initialStateMat(grid);
        return bfs(grid);
    }
};

总结

广度优先搜索并不难理解,但对于如何判断为重复状态(例如推箱子中步长不同的相同状态应该判断为不同状态),需要加强理解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值