面试题3
题目描述:
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
输入:
输入可能包含多个测试样例,对于每个测试案例,
输入的第一行为两个整数m和n(1<=m,n<=1000):代表将要输入的矩阵的行数和列数。
输入的第二行包括一个整数t(1<=t<=1000000):代表要查找的数字。
接下来的m行,每行有n个数,代表题目所给出的m行n列的矩阵(矩阵如题目描述所示,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
输出:
对应每个测试案例,
输出”Yes”代表在二维数组中找到了数字t。
输出”No”代表在二维数组中没有找到数字t。
样例输入:
3 3
5
1 2 3
4 5 6
7 8 9
3 3
1
2 3 4
5 6 7
8 9 10
3 3
12
2 3 4
5 6?7
8 9 10
样例输出:
Yes
No
No
空间复杂度都是O(n*m)
第一种思路:直接遍历一遍,时间复杂度O(n*m);
提交后耗时900ms
#include<iostream>
#include<cstring>
#include<cstdio>
using std::cin;
using std::endl;
using std::cout;
const int MAX = 1000005;
int num[ MAX ];
int main(){
int n, m, t, temp;
while( scanf( "%d %d", &n, &m ) != EOF ){
scanf( "%d", &temp );
memset( num, 0, sizeof( num ) );
for( int i = 0; i < n; ++i ){
for( int j = 0; j < m; ++j ){
//cin >> t;
scanf( "%d", &t );
num[ t ]++;
}
}
if( num[ temp ] )
puts( "Yes" );
else
puts( "No" );
}
return 0;
}
第二种思路:采用二分查找,适用于数据无规律情况。
时间复杂度为O(NlogN, N= n*m),
提交耗时670ms
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<algorithm>
#include<functional>
#include<stack>
using namespace std;
const int MAX = 1005;
int num[MAX * MAX ];
bool FindTheNumber(int *num, int Rows, int Columns, int number){
int l = 0;
int r = Rows * Columns;
bool found = false;
if (num != NULL && Rows >= 0 && Columns >= 0){
while (l <= r ){
int mid = (l + r) / 2;
if (num[l] == number ?? num[r] == number ?? num[mid] == number){
found = true;
break;
}
else if (number < num[mid])
r = mid - 1;
else
l = mid + 1;
}
}
return found;
}
int main(){
int n, m, number;
//while ( cin >> n >> m ){
while (scanf( "%d%d", &n, &m ) != EOF ){
//cin >> number;
scanf("%d", &number);
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j){
//cin >> num[ i * n + j];
scanf("%d", &num[ i *m + j ] );
}
}
bool flag = FindTheNumber(num, n, m, number);
if (flag)
puts("Yes");
else
puts("No");
}
}
第三种思路:适用于本题所给的条件。根据题意。在一个二维数组中,数据自左向右增大,自上而下增大。由此第一反应就是左上角数据最小,右下角数据最大。这样的话一开始很容易联想到,是不是可以从对角线出发,自左上角向右下角去查找。或许可行,没有尝试,因为感觉处理起来会比较麻烦。(未尝试)
但是下面这样处理就简单很多了。我们分别从左上角和右上角分别自上而下、自右向左查找。假设右上角的值大于当前查找值,则我们可以略过对最后一列的查找(自上而下增大,目前最小值大于查找值)。由此我们一直向左找到第一行小于查找值,这时所查找值可能位于当前列。同理,查询行,假设已经找到可能存在的列的第一行的值小于查找值,那么其左边的值都可以略过(自左向右增大,该行最大值小于查找值)。这样可以略过该行。直到查到查找值或者遍历完毕。未尝试的那种思路也可以这样理解,只不过那样处理起来是矩形的范围。
时间复杂度为O(n+m)。这种思路的提交耗时为700ms
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<algorithm>
#include<functional>
#include<stack>
using namespace std;
const int MAX = 1005;
int num[MAX * MAX ];
bool FindTheNumber(int *num, int Rows, int Columns, int number){
bool found = false;
if (num != NULL && Rows > 0 && Columns > 0){
int row = 0;
int column = Columns - 1;
while (row < Rows && column >= 0){
if (num[row * Columns + column] == number){
found = true;
break;
}
else if (num[row * Columns + column] > number){
column--;
}
else
row++;
}
}
return found;
}
int main(){
int n, m, number;
while (scanf( "%d%d", &n, &m ) != EOF ){
//cin >> number;
scanf("%d", &number);
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j){
//cin >> num[ i * n + j];
scanf("%d", &num[i * m + j]);
}
}
bool flag = FindTheNumber(num, n, m, number);
if (flag)
puts("Yes");
else
puts("No");
}
}