问题: 给定一个矩阵,其中每行和每列都被排序,编写一个方法来查找其中的元素。
这是一个经典的面试问题,这是我的解决方案
boolean F(int[][] matrix, int hs, int he, int ws, int we)
{
if (hs > he || ws > we)
return false;
int m = (hs + he) / 2;
int n = (ws + we) / 2;
if (matrix[m][n] == t)
{
return true;
}
else if (matrix[m][n] < t)
{
// find the ele in the same row, right to [m][n]
F(m, m, n + 1, we);
// find the ele in the same col, upper to [m][n]
F(m + 1, he, n, n);
// find the ele in the area, where i>m,j>n
F(m + 1, he, n + 1, we);
}
else if (matrix[m][n] > t)
{
// very similar to previous part
}
}
算法的运行时间是log(m)+ log(n)。我正在寻找一种更高效的算法,或者使用简洁的代码。
有更多评论,我想出了以下代码:
// return target recurrence in the matrix
int F(int[][] m, int rs, int re, int cs, int ce, int t){
int r1 = rs, r2 = re;
int c1 = cs, c2 = ce;
int r=0 , c = c1;
while( r1 < r2 && c1 < c2 ){
// find the last element that <= t in column c
r = FlastLess( r1, r2, c, t)
if( r == -1 ) break;
else{
// find the first ele in the row that is >=t
c = FfirstGreater( r, c1, c2, t);
if( c == -1) break;
else{
r2 = r;
c1 = c;
}// else
}// else
}// while
}// f
这是功能F1和F2的链接 Find the first element in a sorted array that is greater than the target
void FlastLess(int s, int e, int t){
int l = s, h = e;
while( l != h ){
int mid = (l+h)/2;
if( mid >= t) high = mid - 1;
else {
if( high < t) low= mid + 1;
else low = mid;
}
}
void FfirstGreater(int s, int e, int t){
while(l < h){
mid = (l+h)/2;
if ( mid <= t) low = mid+1;
else high = mid;
}
}
}
答案 0 :(得分:23)
从矩阵的左下角开始。然后向右走,直到找到确切的数字(完成),或直到找到更大的数字。
然后你在矩阵中向上移动,直到找到确切的数字(完成),或直到找到一个太小的数字。
然后再次向右移动,......依此类推,直到找到数字或直到你到达矩阵的右侧或顶部。
以下图片包含一些示例,使用Excel表格显示绿色的目标编号,以及黄色跟随的路径。
在最后一个例子中,我们查找207,它不在矩阵中:
这只是算法。编码留给你作为练习: - )
编辑:从底行开始,二进制搜索可能会提供更好的起点。对于算法的其余部分,它可能无关紧要。
答案 1 :(得分:6)
您的算法可能是O(log m + log n),但它也给出了错误的答案。
假设您在以下矩阵中搜索“4”(其中左上角是row = 0,col = 0):
0 1 4
1 2 5
2 3 6
您的算法首先查看中心的2
。由于4
大于2
,因此您继续搜索同一行(不在那里),同一列(不在那里)和右下角(不在那里)。糟糕。
每个行和列的排序约束实际上非常弱。特别是,沿对角线的元素可以是任何顺序。
我认为正确的方法是在第一个和最后一个列上进行二进制搜索,以缩小可能行的范围。然后对这些行的第一行和最后一行进行二进制搜索,以缩小可能的列。等等。
不确定如何分析这个的表现...
答案 2 :(得分:4)
这是我要尝试的。给定m
个n
矩阵A
,将值X
与条目A(m/2,n/2)
进行比较(如有必要,请使用楼层)。
如果A(m/2,n/2) == X
已完成。
如果A(m/2,n/2) < X
,则需要检查3个较小的矩阵:
A(1:m/2, 1:n/2)
A(1:m/2, n/2+1:n)
A(m/2+1:m, 1:n/2)
如果A(m/2,n/2) > X
,则需要检查3个较小的矩阵:
A(m/2:m, n/2:n)
A(1:m/2, n/2+1:n)
A(m/2+1:m, 1:n/2)
您可以通过将值与相应矩阵中的最小值(左上角值)进行比较来消除其中的两个(并非总是)。然后你递归地尝试在每个剩余的矩阵中找到值。
此复杂程度约为O((n*m)^0.8)
。
行和列排序矩阵是Young tableau的特例。我搜索了一个用于搜索Young画面的谷歌搜索,并找到this article which gives 3 nice approaches(其中第一个(也是最差的)是我上面描述的那个。)
答案 3 :(得分:2)
对于基于比较的算法,O(lg(m)+ lg(n))查询是最佳的。
<强>证明强>
对于基于比较的查询,每个查询只能有两个结果:true或false。一个明显的扩展是,对于N个查询,您最多可以得到2个 N 结果。因此,使用N个查询,您只能在矩阵中找到最多2个 N 元素的元素。
搜索m x n矩阵需要多少个查询?只需解决N。
2 N = mn
lg(2 N )= lg(mn)
N = 1g(m)+ 1g(n)
因此lg(m)+ lg(n)查询是最佳的。
基于非比较的查询
该证据是确凿的,但仅适用于基于比较的查询。如果以不涉及比较的方式查询矩阵,那么如果您知道值的分布,则可以获得接近恒定的时间。我不会给你一个算法,但我会建议查看Radix sort,因为它包含了击败lg(m)+ lg所需的非基于比较的技术(n)下限。
答案 4 :(得分:1)
阅读之前的评论我想出了这个算法。它基本上假设通过从右上角开始,矩阵可以用作带有一些“循环”的BST(我们不关心这个循环)。
1 4 9
5 6 10
9
/ \
4 10
/ \ /
1 6
\ /
5
此算法与在BST中搜索相同,并且非常容易理解。最坏的情况是运行时间为O(n + m)。
public static boolean search( int[][] matrix, int value )
{
int rows = matrix.length;
int columns = matrix[0].length;
int i = 0;
int j = columns - 1;
while( i < rows
&& j >= 0 )
{
if( matrix[i][j] == value )
{
System.out.println( "Found at " + i + " " + j );
return true;
}
if( matrix[i][j] < value )
{
j--;
}
else
{
i++;
}
}
return false;
}
答案 5 :(得分:1)
boolean FindElem(int[][] mat, int elem, int M, int N) {
int row = 0;
int col = N-1;
while (row < M && col >= 0) {
if (mat[row][col] == elem) {
return true;
} else if (mat[row][col] > elem) {
col--;
} else {
row++;
}
}
return false;
}
答案 6 :(得分:0)
我相信你的算法没有你认为它的时间范围。
为了看到这一点,为简单起见,我们假设您的网格是一个n x n平方(让我们称这个大小为m)。如果在这种情况下我可以得到与O(log n)不同的时间限制,我可以说你所拥有的不应该是正确的。
特别注意,在最坏的情况下,你会对大小(n / 2)x(n / 2)= m / 4的问题进行三次递归调用。这意味着我们有了重复
T(1) = 1
T(m) = 3T(m / 4) + O(1)
使用Master Theorem,此函数的运行时间为O(m log 4 3 )= O(n 2 log 4 3 )= O(n log 4 9 )≈O(n 1.5849625 )。这是ω(log n + log m);也就是说,它渐近地严格地更大。
正如许多其他人发布的那样,有几种众所周知的算法在O(m + n)中运行,这些算法的基础是每一步都朝着正确的方向走一步。因此,除了正确性之外,我不建议使用您发布的算法。
答案 7 :(得分:0)
给定2d数组中的元素(是[n] [m])水平和垂直增加。因此,对于给定的问题,我们需要首先找到元素的索引。因此,如果我们能够更快地找到元素,那么我们就可以优化解决方案。问题是我们如何以有效的方式找到它。一种方法是采用矩阵的中间元素并用它检查给定元素
如果给定元素小于中间元素,那么我们的解决方案位于矩阵a [0] [0]到[n / 2] [m / 2],因为右边和下面的所有元素都大于中间(因为给定元素小于中间元素)因此我们将搜索空间从[n] [m]减少到[n / 2] [m / 2],这是原始大小的四分之一。
如果给定元素大于中间元素,那么我们的解决方案不在于矩阵a [0] [0]到[n / 2] [m / 2],因为左边和上面的所有元素都小于中间(因为给定元素大于中间元素)因此我们的搜索空间是总数组减去[0] [0]到[n / 2] [m / 2],这是原始大小的四分之三。总数组减去[0] [0]到[n / 2] [m / 2]意味着,将有三个带数组索引的递归调用
--------->a[0][m/2](start index) to a[n/2][m](end index)
--------->a[n/2][0](start index) to a[n][m/2](end index)
--------->a[n/2][m/2](start index) to a[n][m](end index)
现在根据我们的搜索空间递归调用相同的函数。
我们的功能的时间复杂性如下。注意:在时间函数中,n表示元素的总数,但不表示所提及的行数.n =(no_of_rows)*(no_of_columns)
_________________T(n/4) if given element is less than middle of the array.
/
/
T(n)==========------------------- 1 if n=1 (if element found)
\
\_________________3T(n/4) if given element is greater than middle element of array
所以out time功能
T(n)= 3T(n / 4)或T(n)= T(n / 4)
In worst case T(n)=3T(n/4)
T(n)=3{3T(n/4)}
T(n)=3power(i)T(n/(4)poweri) equation------> (1)
但是T(1)= 1(猜测给出elemet在数组中找到)
so n/(4power(i))=1
====> n=2power(2*i)
====> n=2power(2*i)
Talking log to base 2 on both sides (log[n])/2=i ====> i=log(sqrt(n))
代入等式1我们得到
T(n)=3power(log[sqrt(n)])
T(n)∈ θ( nlog sqrt(3) )..
据我所知,这是一种算法,只需要进行最少量的比较。
答案 8 :(得分:0)
JavaScript解决方案:
//start from the top right corner
//if value = el, element is found
//if value < el, move to the next row, element can't be in that row since row is sorted
//if value > el, move to the previous column, element can't be in that column since column is sorted
function find(matrix, el) {
var row = 0; //first row
var col = matrix[0].length - 1; //last column
while (row < matrix.length && col >= 0) {
if (matrix[row][col] === el) { //element is found
return true;
} else if (matrix[row][col] < el) {
row++; //move to the next row
} else {
col--; //move to the previous column
}
}
return false;
}