您将获得一个MxN的2D数组,该数组按行和列顺序排序。搜索元素的有效方法是什么?
答案 0 :(得分:5)
从矩阵的右上角位置v
开始。如果它是您正在寻找的项目x
,那么您已经完成了。如果v
小于您要查找的项目,请向下移动。如果v
大于您要查找的项目,请向左移动。重复,直到你到达矩阵的末端。
正确性证明:
如果右上角的项目等于x
,则无需证明。考虑两种情况
v < x
在这种情况下,我们知道顶行中所有元素都小于x
。因此,我们可以忽略整个顶行并将向下移动。
因此,我们可以从
开始 1 2 3 4 5 6
1 * * * * * v
2 * * * * * *
3 * * * * * *
4 * * * * * *
5 * * * * * *
到
1 2 3 4 5 6
1 . . . . . .
2 * * * * * v
3 * * * * * *
4 * * * * * *
5 * * * * * *
也就是说,我们最终遇到了一个小问题。
另一种情况是
v > x
在这种情况下,我们知道右列中所有元素都大于x
。因此,我们可以忽略整个右列并移动 left 。
1 2 3 4 5 6
1 * * * * * v
2 * * * * * *
3 * * * * * *
4 * * * * * *
5 * * * * * *
到
1 2 3 4 5 6
1 * * * * v .
2 * * * * * .
3 * * * * * .
4 * * * * * .
5 * * * * * .
同样,我们最终遇到了一个小问题。通过归纳,我们完成了。该算法具有时间复杂度O(m + n)
。
Ted Hopp链接到这个想法的绝对美妙的扩展,提供更好的性能。
这是个主意。在我上面给出的算法中,我们的想法是我们可以一次性排除整个行或列。他链接的想法是消除整个象限。这个想法很简单
* * * * * *
* * * * * *
* * * * * * <- binary search
* * * * * *
* * * * * *
二进制搜索中间行。这将为您提供项目,或包含您正在寻找的项目的位置
* * * * * *
* * * * * *
* * * a|b * <- x between a, b
* * * * * *
* * * * * *
现在,这是关键的洞察力。整个左上象限和整个右下象限可以立即消除;左上角的所有元素都小于a
,右下角的所有元素都大于b
。
. . . . * *
. . . . * *
. . . a|b .
* * * * . .
* * * * . .
现在递归剩下的两件作品。此外,您可以在中间行或左上角到右下角度执行相同的操作,具体取决于哪个将产生最大增益。
答案 1 :(得分:2)
有一个非常好的写入here算法来解决这个问题。正如文章所描述的那样,对每一行(或每个列同样)按行进行简单的二进制搜索,得到O(n log n)解。但是,从右上角开始然后线性向左或向下行进的简单算法会产生O(n)算法。 (这是正确的:线性搜索胜过二分搜索!)然而,更好的结果来自于使用矩阵的二进制分区(基于线性搜索)并产生一种算法,在某些情况下具有O((log n) 2 )(次线性)表现。
最好的算法似乎是一种分而治之的方法:对于 m × n 矩阵 M n (列数)&lt; m (行数) * 和目标值 v ,搜索中间行(称为行 r )对于索引 c ,使 M r , c ≤ v &lt;目标值 v 是 M r , c +1 。如果 v = M r , c ,那么您就完成了。否则,递归地将算法应用于子矩阵 M r + 1,0 ... M m -1, c 和 M 0, c +1 ... M n -1, r 。 (这些是由单元格界定的左下角矩阵( r +1, c )和由单元格界定的右上角矩阵( r -1, c +1)。)
有关性能和代码本身的详细信息,请参阅链接。
* 如果 n &gt; m ,搜索中间列。如果 n = m ,请搜索对角线。在每种情况下,子矩阵的确切边界需要从上面的描述中稍微调整一下;看文章。
答案 2 :(得分:0)
通常第一个索引是“row”,第二个索引是“column”,列索引应该是连续的内存,即使行是以不同的块分配的,所以从这个角度看,搜索所有内容的速度应该更快一行的列,然后移动到下一行并在那里迭代列。
显然,假设您搜索的所有项目均匀分布,并且“每行中的第一项更可能是您正在寻找的候选项,并且每列最后一项最不可能”。
同样非常明显的是,如果每一行都包含已排序的值,那么您可以对列进行二进制搜索,如果最小值和最大值未覆盖您正在搜索的范围,则跳过整行。
与“更快”的所有内容一样,您确实需要对解决方案进行基准测试,以确定在您的特定情况下哪种方法最佳。