【剑指 offer】二维数组中的查找 -- c++ 实现总结

二维数组中的查找

http://www.nowcoder.com/questionTerminal/abc3fe2ce8e146608e868a70efebf62e

注:如果代码出现了段错误问题,可能是没有考虑到空数组(至少包括[][[]]两种空的二维数组),健壮性也是算法的一部分

一、暴力法

1.分析:遍历数组,如果找到就返回 true
2.代码

    // Solution 1: 运行时间:15ms 占用内存:1764k
    bool Find(int target, vector > array) {
        // for循环代码可以处理空数组,不需单独处理
        int rows = array.size();
        int columns = array[0].size();
        for (int i = 0; i < rows; ++i)
            for (int j = 0; j < columns; ++j)
            {
                if (array[i][j] == target)
                    return true;
                else
                    continue;
            }
        return false;
    }

3.复杂度
时间复杂度:O(n2)
空间复杂度:O(1)

二、N行折半查找法

1.分析:依次对每一行(列)都执行折半查找
2.代码

// Solution 2: 运行时间:15ms 占用内存:1764k
bool Find(int target, vector<vector<int> > array) {
    if (array.empty())    return false;
    if (array[0].empty()) return false; //处理空数组
    int length = array.size();

    for (int i = 0; i < length; ++i)
        // 对每一列二分查找
        for (int begin=0, end=length-1, j=length/2; begin<=end;)
        {
            if (array[i][j] == target)
                return true;
            else if (array[i][j] > target)
                end = j-1;
            else 
                begin =j+1;
            j = begin + (end-begin)/2;
        }
    return false;
}

3.复杂度
时间复杂度:O(n*lgn)
空间复杂度:O(1)

4.拓展:双折半查找

二维数组分为上下左右四个边界top,bottom,left,right:
对上边界top进行折半查找,假设终止点为E,则可以将二维数组位于终止点E右边的矩形Rr排除,因为终止点E小于其右边相邻的元素E+1,而E+1是右边矩形Rr的最小元素(左上元素)
同理,对下边界bottom折半,可以排除二维数组位于终止点E左边的矩形Rl排除,
对左边界left折半,可以排除二维数组位于终止点E下边的矩形Rb排除,
对右边界right折半,可以排除二维数组位于终止点E上边的矩形Rt排除,
一轮过去,边界范围缩小,对由新边界组成的矩形重复以上操作,直到范围缩小为只有一个元素。

三、十字分割法

我首先想到的是这种方法,不过要注意这种方法无论是从思维还是实现过程都比较麻烦,实战慎用

1.分析:在主对角线方向上进行查找操作,直到一个元素大于目标,该终止点的左上区域与右下区域都可以排除,再递归剩下的左下、右上两个区域。
2.代码

// Solution 2: 运行时间:12ms 占用内存:1624k
bool Find_2(int target, vector<vector<int> > array) {
    if (array.empty())return false;
    if (array[0].empty())return false; //处理空数组

    //初始化栈
    typedef struct {
        int xmin=0,ymin=0,xmax,ymax;
    } stack_elem;
    stack_elem temp, newTemp;
    stack<stack_elem> s;
    temp.xmax = temp.ymax = array.size()-1;
    s.push(temp); 

    while (!s.empty()){
        newTemp = temp = s.top();
        s.pop();
        int x=temp.xmin, y=temp.ymin;


        while (true){
            // 首先确定区域是否可能包含目标值
            if (array[temp.xmin][temp.ymin]>target || array[temp.xmax][temp.ymax]<target) break; 
            if (temp.xmin>temp.xmax || temp.ymin>temp.ymax) break; 

            // 遍历过程节点分为几类
            if (array[x][y] == target) return true; // 情况1: 找到目标值
            if (array[x][y] > target){ //情况2:找到大于目标值的节点,缩小大区域为两个小区域
                newTemp.xmax = x-1;
                newTemp.ymin = y;
                if (newTemp.xmin<=newTemp.xmax && newTemp.ymin<=newTemp.ymax)
                    s.push(newTemp);
                temp.ymax = y-1;
                temp.xmin = x; 
                y = temp.ymin;
            }
            else if (x == temp.xmax){ //情况3:处理非正方形区域某一边界先溢出的情况
                temp.ymin = y+1;
                x = temp.xmin;
                ++y;
            }
            else if (y == temp.ymax){
                temp.xmin = x+1;
                y = temp.ymin;
                ++x;
            }
            else {++x,++y;} // 情况4:继续查找
        } 
    }
    return 0;
}

3.复杂度
时间复杂度:采用二分查找时为O(lgn)、采用顺序查找时为O(nlgn)
空间复杂度:O(1)

四、左下/右上元素移动法

1.分析:依次对每一行(列)都执行折半查找
2.代码

// Solution 4: 运行时间:10ms 占用内存:1504k
bool Find_3(int target, vector<vector<int> > array) {
    if (array.empty())return false;
    if (array[0].empty())return false;

    int length = array.size();
    int row=0, col=length-1;
    while (row<length && col>-1){
        if (array[row][col]==target) return true;
        if (array[row][col]>target) --col;
        else ++row;
    }
    return false;
} 

3.复杂度
时间复杂度:O(n)
空间复杂度:O(1)

全部评论
你好, 我认为,按照你的解法 四、左下/右上元素移动法 这样写只适用n==m(矩阵长宽相等)的情况 应该把col = array[0].size()-1就好
点赞 回复 分享
发布于 2021-10-08 15:20

相关推荐

Tom哥981:让我来压力你!!!: 这份简历看着“技术词堆得满”,实则是“虚胖没干货”,槽点一抓一大把: 1. **项目描述是“技术名词报菜名”,没半分自己的实际价值** 不管是IntelliDoc还是人人探店,全是堆Redis、Elasticsearch、RAG这些时髦词,但你到底干了啥?“基于Redis Bitmap管理分片”是你写了核心逻辑还是只调用了API?“QPS提升至1500”是你独立压测优化的,还是团队成果你蹭着写?全程没“我负责XX模块”“解决了XX具体问题”,纯把技术文档里的术语扒下来凑字数,看着像“知道名词但没实际动手”的实习生抄的。 2. **短项目塞满超纲技术点,可信度直接***** IntelliDoc就干了5个月,又是RAG又是大模型流式响应又是RBAC权限,这堆活儿正经团队分工干都得小半年,你一个后端开发5个月能吃透这么多?明显是把能想到的技术全往里面塞,生怕别人知道你实际只做了个文件上传——这种“技术堆砌式造假”,面试官一眼就能看出水分。 3. **技能栏是“模糊词混子集合”,没半点硬核度** “熟悉HashMap底层”“了解JVM内存模型”——“熟悉”是能手写扩容逻辑?“了解”是能排查GC问题?全是模棱两可的词,既没对应项目里的实践,也没体现深度,等于白写;项目里用了Elasticsearch的KNN检索,技能栏里提都没提具体掌握程度,明显是“用过但不懂”的硬凑。 4. **教育背景和自我评价全是“无效信息垃圾”** GPA前10%这么好的牌,只列“Java程序设计”这种基础课,分布式、微服务这些后端核心课提都不提,白瞎了专业优势;自我评价那堆“积极认真、细心负责”,是从招聘网站抄的模板吧?没有任何和项目挂钩的具体事例,比如“解决过XX bug”“优化过XX性能”,纯废话,看完等于没看。 总结:这简历是“技术名词缝合怪+自我感动式凑数”,看着像“背了后端技术栈名词的应届生”,实则没干货、没重点、没可信度——面试官扫30秒就会丢一边,因为连“你能干嘛”都没说清楚。
点赞 评论 收藏
分享
12-15 11:27
门头沟学院 Java
哇哇的菜鸡oc:所有人不要理会,就好了,后面他就知道怎么回事了,只能说有的时候市场都是被宰的人搞坏的
点赞 评论 收藏
分享
评论
10
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务