剑指Offer题目1:二维数组中的查找
题目描述:
http://ac.jobdu.com/problem.php?cid=1039&pid=0
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
输入:
输入可能包含多个测试样例,对于每个测试案例,
输入的第一行为两个整数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
题目解答分析
失败解答1:用二维遍历
这个题要是按普通的二维遍历来查找的话,也能完成功能,但是就体现不出二维数组原先有序的优势了。
如果数组非常大的话,会特别浪费时间。
但是我做的时候还是首先采取了这个二维遍历的方法,因为我开始想的是,我需要读入所有的数据,也只能一个一个读,这已经是一个二维遍历,那就只好在读的时候顺便进行查找了。
程序如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> using namespace std; int main(int argc, char* argv[]) { int row = 0, col = 0, t = 0; int testNum[100][100]; bool isFound = false; while(cin >> row >> col >> t) { isFound = false; for(int i = 0; i < row ; ++i) { for(int j = 0; j < col; ++j) { //输入每个数 cin>>testNum[i][j]; //边输入边验证 if(false == isFound && t == testNum[i][j]) { //已经找到后就没必要再找了 isFound = true; } } } if(true == isFound) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0; }
果然提交以后就显示Time Limit Exceed了。
想必,未显示的测试用例中必然有很大的二维数组。
进一步思考:利用数组有序特点
所以题目给的有序的条件是必须要用的,但是注意到这个有序也不是单纯的有序,行与行之间也只是上面的元素比下面的元素小的关系,并不代表第二行的数一定大于第一行,比如第一行可以是1,2,3,4,而第二行可以是2,3,4,5。
先定位行再定位列的想法也不行,因为不论是第一列还是最后一列,都不足以作为本行的键值,因为各行之间的元素还是可能重叠的。而且,比如,目标值t有可能比第一列的所有元素都大,所以想根据第一个元素来查找是不行的。
书中的思路:
从数组中选取数字,和目标数字的关系有三种情况:=,<或>。
如果是等于则查找成功;
如果是数组中元素小于要查找的数字,说明要查找的数字应该在当前位置的右边或下边。
如果是数组中元素大于要查找的数字,说明要查找的数字应该在当前位置的左边或上边。
但是这两个区域还有可能有重叠,比如右边或下边会在右下角有重叠。
解决方法:
如果查找从右上角开始,如果要查找的数字不在右上角,则每次可以剔除一列或一行。
也可以从左下角开始,但是不能从左上角或者右下角开始。
别人的解答
http://www.cnblogs.com/remlostime/archive/2012/11/21/2780352.html
于是搜到了如上的解答。
可见这个查找是从矩阵的右上角开始进行的(本行最大,本列最小);
如果查找成功,则返回;
如果查找失败,矩阵元素值比t小则下移一行,矩阵元素值比t大则左移一列。
这样把大小关系和调整的两个方向就分开了,经过一些移动,如果查找成功则返回Yes,如果矩阵走完,则表明没有找到。
这样是可以实现(实现代码附在后面),可以通过本文所列的测试用例,但是提交到九度上,还是超时了。
暂时没有解决办法,先休息吧,很晚了。。
题目解答代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> using namespace std; int main(int argc, char* argv[]) { int row = 0, col = 0, t = 0; int testNum[1000][1000]; bool isFound = false; while(cin >> row >> col >> t) { //先将数组全部读入 for(int i = 0; i < row ; ++i) { for(int j = 0; j < col; ++j) { //输入每个数 cin>>testNum[i][j]; } } //标志变量,记录查找是否成功 isFound = false; //然后进行查找 for(int i = 0, j = col -1; false == isFound && i < row && j >= 0;) { //找到之后循环就不必再进行 if(testNum[i][j] == t) { isFound = true; } else if(testNum[i][j] < t) { ++i; //换到下一行 } else { --j; //换到前一列 } } //最后输出结果 if(true == isFound) { cout << "Yes" << endl; } else { cout << "No" << endl; } } return 0; }
工具推荐
一个测试程序很方便的工具,使用如图:
首先设置好测试用例和标准输出(即正确情况下的输出),然后加入要运行的程序的exe文件(注意每次修改代码都需要重新加一次),最后点击运行即可查看结果。如果正确,会有对话框显示AC。
附上网盘分享链接:
http://pan.baidu.com/share/link?shareid=518071&uk=2701745266
在测试用例比较长的时候不用每次都重复输入耽误时间了。
参考资料:
《剑指Offer:名企面试官精讲典型编程题》九度Online Judge收录:
http://ac.jobdu.com/contest.php?cid=1039
何海涛的日志:
http://zhedahht.blog.163.com/blog/#m=0
解法:
http://www.cnblogs.com/remlostime/archive/2012/11/21/2780352.html