算法题001 剑指Offer 面试题三:二维数组中的查找

剑指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:用二维遍历

  这个题要是按普通的二维遍历来查找的话,也能完成功能,但是就体现不出二维数组原先有序的优势了。

  如果数组非常大的话,会特别浪费时间。

  但是我做的时候还是首先采取了这个二维遍历的方法,因为我开始想的是,我需要读入所有的数据,也只能一个一个读,这已经是一个二维遍历,那就只好在读的时候顺便进行查找了。

  程序如下:

程序尝试1:双层循环
#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,如果矩阵走完,则表明没有找到。

  这样是可以实现(实现代码附在后面),可以通过本文所列的测试用例,但是提交到九度上,还是超时了。

  暂时没有解决办法,先休息吧,很晚了。。

 

题目解答代码 

解答方法2
#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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值