Google 面试题 | 矩形个数

专栏 | 九章算法

网址 | http://www.jiuzhang.com

题目描述

给一个二维数组构成的网格,其中的每一个格点非0即1。找出由4个不同格点上的1作为4个角,撑起的矩形个数。

注意:

1. 该矩形边与横轴或纵轴平行。

2. 只需要4个角有1即可。

3. 行数和列数范围为[1, 200]

4. 一块网格中1的总数不超过6000。

样例:

输入:

[[1, 0, 0, 1, 0],

[0, 0, 1, 0, 1],

[0, 0, 0, 1, 0],

[1, 0, 1, 0, 1]]
复制代码

输出:

1
复制代码

解释:

由于只有[1][2], [1][4], [3][2], [3][4],这四个点能组成矩形。故输出1。

复制代码

输入:

[[1, 1, 1],

[1, 1, 1],

[1, 1, 1]]
复制代码

输出:

9
复制代码

解释:

有4个2x2的矩形,4个2x3的矩形,1个3x3的矩形。
复制代码

输入:

[[1, 1, 1, 1]]
复制代码

输出:

0
复制代码

解释:

注意,四个角任意两个不能共点
复制代码

解题思路分析

最直观的想法是穷举所有的矩形,看其是否满足要求。由于R行C列的网格,矩形一共有R * (R - 1) * C * (C - 1)个,所以穷举的时间复杂度是O((R * C) ^ 2),相当费时。有没有什么好的方法呢?

如果换一种思路考虑:每加入新的一行,会新加多少个矩形?这种想法有一些像动态规划,复杂度会比穷举法要低不少。由此就引出以下两种方法:

方法一:

对于新行的每对1(记为cur_row[i]和cur_row[j]),新加矩形个数,是之前行row[i]= row[j] = 1出现的次数!所以逐行遍历网格,并维护一个统计表count[i,j](可用map实现,python中亦可用Counter或defaultdict实现),count记录了之前行的每一对row[i] = row[j] = 1的次数。对于新行的每对1,例如cur_row[i]和cur_row[j],最终结果加上count[i, j]然后count[i, j]自增。

方法一复杂度分析:

时间复杂度: O(R ∗ C^2),其中R是行数,C是列数。由于遍历每行当中都要寻找每对1,故每行遍历复杂度为C ^ 2。

空间复杂度:O(C ^ 2)。

方法二(难度较大):

在方法一中每一行都要O(C ^ 2)时间去遍历,这个过程十分麻烦。很容易想到,可以先将每一行的所有1的位置记录到列表当中,一共有R个这样的列表。然后行遍历过程中,直接对列表里的每一对元素进行统计表count的查询和自增即可。这是一种很好的办法,但是如果这一行1很多,也即这一行很密集,那么这个列表会很长遍历每一对元素复杂度仍然会逼近O(C ^ 2)。

针对这种密集的行(记该行为r),一种改进措施是不再遍历其列表中每一对元素,而是直接寻找表中每一行(如行a)有多少个1,它们在r行对应列上也是1。假设有f个,那么行a与行f所组成的矩形就有(f - 1) * f / 2个。我们可以用把行r的列表转换为集合Set,然后只需线性遍历每一行,计算出f及其产生的矩形数。要注意如果如果行r和行a都是密集行,那么行a只有在行r的下方有效。否则会重复计算,也即遍历到行r计算了一次,遍历到行a又计算了一次。

那么到底一行超过多少个点算密集行,这里取的是N ^ 0.5,也即根号N,其中N为网格中1的个数。这样能保证最多只有N ^ 0.5个密集行,每一个密集行的复杂度为O(N),故复杂度不超过N ^ 1.5。而对于稀疏行,因总点数不超过N,所以可证所有的稀疏行总共复杂度不超过N ^ 1.5(证明复杂,需用数学推导,这里略去)。

方法二复杂度分析

时间复杂度:由以上分析可得O(N ^ 1.5)。

空间复杂度:O(N + R + C ^ 2)。N是由于记录1的位置的列表,R是由于由R个列表,C ^ 2是由于统计表count。

参考程序

2hwjvRr0HvRVJN-LcoUPIw-QQ截图20180113011448.png

www.jiuzhang.com/solution/nu…

面试官角度分析

本题是一道稍难的统计类题目,很考验面试者的观察能力,改变统计的思路从而减小时间复杂度。同时这道题考察了对于Set、Map等数据结构的使用。如果能想到逐行添加的思路,进而给出方法一,可以给出Hire;如果能在方法一的基础上进行优化,对于密集边进行单独处理并能分析出其复杂度,可以给出Strong Hire。

lintcode相关问题

QQ截图20180131162917.png

www.lintcode.com/zh-cn/probl…

QQ截图20180131163108.png

www.lintcode.com/zh-cn/probl…

分割线

欢迎关注我的微信公众号:九章算法(ninechapter)。
精英程序员交流社区,定期发布面试题、面试技巧、求职信息等

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值