我最近对一家公司进行了技术测试,我认为他们在识别二进制矩阵中的形状方面存在一个非常有趣的问题。
练习的目的是创建一个算法,该算法可以在二进制矩阵中找到最大的X形状,并返回其长度。 X以这种方式定义: -A X由两条长度相等的对角线组成,它们共享一个独特的点。 例如:
1001
0110
0110
1001
包含长度为3的有效X,因此算法将返回3.
{{1}}
不包含任何有效的X,因此算法将返回1,因为1长X是单个1.
我设法做了这个练习,但是我实现了一个非常混乱的算法,时间复杂度估计为O(n3),这很糟糕,不适合非常大的矩阵。这种复杂性的很大一部分是我用来遍历矩阵的双循环。
我可以做些什么来制作更干净的算法?我出于个人兴趣提出这个问题,需要提高我的技能和实践思维。
答案 0 :(得分:4)
如果你可以使用O(n ^ 2)额外的空间,那么一个选项是构建两个额外的数组:一个用于记录以每个单元为中心的最长\
形状的长度,一个用于记录最长/
的长度。 (您可以使用三重嵌套循环在O(n ^ 2)时间内构建每个循环 - 这可能听起来像O(n ^ 3)时间,但是memoization意味着您只需要迭代一次< / em>超过任何给定的\
或/
,因此最里面的循环可以是摊销 - 恒定时间。)然后迭代所有位置;对于任何位置,以该位置为中心的最大X
的大小等于该位置处两个矩阵值中的较小者。只需跟踪最大的这么小的价值,你就完成了。
具有O(n ^ 2)的最坏情况时间复杂度,这显然是渐近最优的。
答案 1 :(得分:1)
Althogh在问题中没有明确说明,我假设两个对角线的共同点必须在中间形成一个有效的&#34; X&#34;形状。这些图片似乎支持这种假设。
想象一下,阵列旋转了45度,这样我们现在就拥有了某种钻石形状。我们现在正在搜索与x和y轴对齐的十字架。
您可以逐行检查其中的跨度。只有具有奇数个1的跨度可能是交叉的一部分(否则没有中间元素)。
对于每个这样的跨度,检查是否确实存在交叉。
如果我们只对最大尺寸感兴趣,您可以省略交叉检查是否跨度短于或等于目前为止的最大值。
识别水平跨度所需的时间是O(n ^ 2)。 对于每个长度为m的水平跨度,您最多检查另一个方向上的m + 2个元素。所有跨度的总和显然是O(n ^ 2),因此交叉检查所需的时间也是O(n ^ 2)。
因此,该算法的总工作量为O(n ^ 2),并且不需要额外的空间。
答案 2 :(得分:1)
这是一个O(m*n)
程序,其中m
是行数,n
是矩阵中的列数:
从上到下逐行迭代。每个1
可以有0到2个父母。如果1
有父项,请为其父项指定父项。用两个父母保存细胞。现在对“孩子”做同样的事情,从下到上遍历行。之后,找到有两个父母和两个孩子的单元格,显示最大尺寸的X.
10001
01010
00100
01010
10001
从上到下:
p1 ... p2
p1 ... p2
[p1,p2] // one cell with two parents
...
...
自下而上:
...
...
[c1,c2]
c1 ... c2
c1 ... c2
答案 3 :(得分:0)
O(n)空间和O(n)时间(n为单元数):
在O(n)时间内,将矩阵的每个元素与沿着&#39; 1&#39;的最大距离相关联。直线NE,SE,SW和NW中的单元格(像地图一样读取矩阵)。
在O(n)时间内,找到同时为&#39; 1&#39;并且具有这四个值中最大的最小值。您可以在设置四个中的最后一个的同时执行此操作。
最大长度是上述最大分钟的两倍多。