4.29 每日一题--LeetCode:427. 建立四叉树

原题链接

给你一个 n * n 矩阵 grid ,矩阵由若干 0 和 1 组成。请你用四叉树表示该矩阵 grid 。
你需要返回能表示矩阵的 四叉树 的根结点。
注意,当 isLeaf 为 False 时,你可以把 True 或者 False 赋值给节点,两种值都会被判题机制 接受 。
四叉树数据结构中,每个内部节点只有四个子节点。此外,每个节点都有两个属性:
val:储存叶子结点所代表的区域的值。1 对应 True,0 对应 False;
isLeaf: 当这个节点是一个叶子结点时为 True,如果它有 4 个子节点则为 False 。
class Node {
public :
bool val;
bool isLeaf;
Node topLeft;
Node topRight;
Node bottomLeft;
Node bottomRight;
};
我们可以按以下步骤为二维区域构建四叉树:
如果当前网格的值相同(即,全为 0 或者全为 1),将 isLeaf 设为 True ,将 val 设为网格相应的值,并划
分四个子节点都设为 Null 然后停止。
如果当前网格的值不同,将 isLeaf 设为 False, 将 val 设为任意值,然后如下图所示,将当前网格划分为
4个子网格。
使用适当的子网格递归每个子节点。
四叉树格式:
输出为使用层序遍历后四叉树的序列化形式,其中 null 表示路径终止符,其下面不存在节点。
它与二叉树的序列化非常相似。唯一的区别是节点以列表形式表示 [isLeaf, val] 。
如果 isLeaf 或者 val 的值为 True ,则表示它在列表 [isLeaf, val] 中的值为 1 ;如果 isLeaf 或者 val 的值为 False ,则表示值为 0 。
示例 :
输入:grid = [[0,1],[1,0]]
在这里插入图片描述
输出:[[0,1],[1,0],[1,1],[1,1],[1,0]]
在这里插入图片描述
输入:grid = [[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0]]
输出:[[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]]
提示:
n == grid.length == grid[i].length
n == 2^x 其中 0 <= x <= 6

  其实这道题刚开始阅读的时候我是十分懵逼的,不过仔细分析了之后其实还是可以分析出来这道题就是一个十分简单的dfs.
  对于一个四叉树的结点来说,每个结点在矩阵中的位置都是连续的四个范围如下图所示:
在这里插入图片描述

  再读题可知,对于一个结点代表的区域[(leftrow,leftcol),(rightrow,rightcol],如果其内部所有元素都相等(都为0或者都为1)那他是一个叶子结点,我们新开辟并返回一个结点;但是如果不都相等,就需要再对这个结点代表的范围进行分割也就是四等分,直到所有的叶结点都被找到位置。当然对于判断的过程可以利用二维前缀和来优化使得查找某一区间的和的时间复杂度降到O(1).

class Solution {
        vector<vector<int>> prev;
        Node* dfs(vector<vector<int>>& grid,int rl,int rr,int cl,int cr){
            int sum=prev[rr+1][cr+1]-prev[rr+1][cl]-prev[rl][cr+1]+prev[rl][cl];
            //得到这个区间的前缀和
            //因为我们传参的时候仍然是按照grid的坐标来传的,那么在前缀和数组中的下标就对应着原数组的下标加一
            //因此我们要求从[rl,cl]->[rr,cr]的前缀和就从原本的:
            //prev[rr,cr]-prev[rr][cl-1]-prev[rl][cr-1]+prev[rl-1][cl-1]变成了代码中的样子,这样做的好处是防止了越界
            int cnt=(rr-rl+1)*(cr-cl+1);//这个区间内格子的个数
            //printf("%d %d\n",sum,cnt);
            if(sum==0||cnt==sum){
                return  new Node(sum,true);//是叶子结点直接返回
            }else {
                Node * root=new Node(1,false);//不是则开始切割为四份
                root->topLeft=dfs(grid,rl,rl+(rr-rl)/2,cl,cl+(cr-cl)/2);
                //topleft->[(rl,cl),((rr-rl)/2,(cr-cl)/2)]
                root->topRight=dfs(grid,rl,rl+(rr-rl)/2,cl+(cr-cl)/2+1,cr);
				//topright->[(rl,(cr-cl)/2+1),((rr-rl)/2,cr]
                root->bottomLeft=dfs(grid,rl+(rr-rl)/2+1,rr,cl,cl+(cr-cl)/2);
                //bottomleft->[(rr-rl)/2+1,cl),(rr,(cr-cl)/2]
                root->bottomRight=dfs(grid,rl+(rr-rl)/2+1,rr,cl+(cr-cl)/2+1,cr);
                //bottomright->[(rr-rl]/2+1,(cr-cl)/2+1),(rr,cr)]
                return root;
            }
        }
        int n;
public:
    Node* construct(vector<vector<int>>& grid) {
        n=grid.size();
        for(int i=0;i<=n;i++){
            vector<int> tmp(n+1);
            prev.push_back(tmp);//防止越界吧前缀和数组往外扩一圈
        }
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                prev[i][j]=prev[i-1][j]+prev[i][j-1]-prev[i-1][j-1]+grid[i-1][j-1];
            }
        }
        return dfs(grid,0,n-1,0,n-1);//搜索开始
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值