【九章斩题录】C/C++:二维数组中的查找(JZ4)

news2024/11/28 20:55:44

  精品题解 👉 《九章刷题录》

📜 目录:

「 法一 」暴力美学

「 法二 」十字分割法

「 法三 」逐行二分


JZ4 - 二维数组中的查找

📚 题目描述:在一个二维数组 array 中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

比如下列二维数组 \color{}A给定 target = 3,则返回 true给定 target = 7,则返回 false

\color{} A=\begin{bmatrix} 1 & 2& 3& 4\\ 2 & 3& 4& 5\\ 3 & 4 & 5 & 6 \end{bmatrix} \in \mathbb{R}^{3\times 4},\, \left \langle\textrm{int} \right \rangle

int a[3][4] = {{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}};  // C
  • 数据范围:矩阵的长宽满足 \color{}0\leq n,m\leq500 , 矩阵中的值满足 \color{}0\leq val\leq 10^9
  • 进阶:时间复杂度 \color{}O(m+n),空间复杂度 \color{}O(1)

💭 示例:I/O

输入:7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回:true
说明:存在7,返回true  

输入:3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回:false
说明:不存在3,返回false   

输入:3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回:false
说明:不存在3,返回false   

✅ 模板:C语言

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param target int整型 
 * @param array int整型二维数组 
 * @param arrayRowLen int array数组行数
 * @param arrayColLen int* array数组列数
 * @return bool布尔型
 */
bool Find(int target, int** array, int arrayRowLen, int* arrayColLen ) {
    // write code here
}

✅ 模板:C++

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
    }
};


「 法一 」暴力美学

" 别和我说什么二分线性算法,老夫敲代码就是一把梭,直接 for 暴力! "

💡 思路:既然是要找数组中是否存在某个数字,直接逐行逐列遍历搜索即可。对于二维数组的遍历,需要用两层循环,因此时间复杂度为 \color{}O(M*N),空间复杂度为 \color{}O(1)

💬 代码演示:C语言

#include <stdbool.h>
bool Find(int target, int** array, int arrayRowLen, int* arrayColLen) {
    for (int i = 0; i < arrayRowLen; i++) {        // 遍历行
        for (int j = 0; j < *arrayColLen; j++) {   // 遍历列
            if (array[i][j] == target) {
                return true;   // 找到了
            }
        }
    }
    return false;    // 没找到
}

 我们定义 \color{}i,j 搜索二维数组,如果找到了目标值则返回 true,如果搜索完仍未找到我们根据题意返回 -1 即可。


「 法二 」十字分割法

" 既有规律可循,一次排除一批,岂不美哉?"

💡 思路:题中描述的数组是存在规律的:"在一个二维数组 array 中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。" 这就意味着矩阵内部的行列都是有序,数组右上角的值在这一行中是最大的,而在这一列中是最小的。,每次判断都能剔除一整行或一整列。

我们思考一个问题 —— "一次排除一个和一次排除一批",谁的效率更高?当然是后者效率更高!如果按照这个本质,我们在套上我们想到的 「法1」,其本质就是一次排除一个,效率自然也就不能再高了。我们观察这样的数组,我们可以通过比较目标值,和数组中右上角的值(或左下角的值),因为右上角的值是这一行中最大的,是这一列中最小的。

对我们而言,我们可以 直接拿着目标值,和当前矩阵的右上角的值进行比较。 如果当前值比右上角的值小,说明当前你要查找的值是绝对不会出现在这一列的,就可以把这一列整体排除。

现在我们就做到一行排除一行或者一列了。此时我们就需要考虑临界条件的问题,我们要关注什么时候结束,找到了,如果没找到,一定是一个行或者列出现了越界的条件,导致程序退出。排除时,行不断加,列不断减,因此临界条件为:行 (row) 不能增到 arrayRowLen,列 (col) 不能缩到比 0 小。

\color{}(row < arrayRowLen)\, \,\bigwedge \, \, (col \geq 0)

📚 介绍:上面我们介绍的这种方法,称之为 十字分割法 (Cross-Sectional Search),是在有序二维数组中查找目标元素的高效算法,当然前提是有序!它利用了有序数组的特性,通过逐步缩小搜索范围来快速确定目标元素的位置。"十字" 也很形象的表现出排除 "每次判断都能剔除一整行或一整列" 的特点。通过不断缩小搜索范围,十字分割法可以快速确定目标元素的位置,从而提高查找的效率。十字分割法的基本步骤如下:

Step1:选择一个起始点,通常是数组的右上角或左下角(个人习惯于右上角)

Step2:将起始点的值与目标元素进行比较。

Step3:如果起始点的值等于目标元素,找到目标元素,搜索结束。

Step4:如果起始点的值大于目标元素,目标元素可能在当前元素的左边或上方,将搜索范围缩小到当前元素的左边区域或上方区域。

Step5:如果起始点的值小于目标元素,目标元素可能在当前元素的右边或下方,将搜索范围缩小到当前元素的右边区域或下方区域。

* 重复步骤 2~5,直到找到目标元素,或达到临界条件(数组不能越界)!

该方法的时间复杂度为 \color{}O(R+C) ,其中 R 为二维数组的行数,C 为二维数组的列数。

💬 代码演示:C语言

bool Find(int target, int** array, int arrayRowLen, int* arrayColLen ) {
    int row = 0;                // 当前行
    int col = arrayRowLen - 1;  // 当前列

    while (row < arrayRowLen && col >= 0) {
        // array[row][col] 必定是当前行最大的,当前列最小的
        if (target < array[row][col]) {   // 如果目标值小于右上角
            col--;   // 不可能出现在该列,排除
        }
        else if (target > array[row][col]) {  // 如果目标值大于右上角
            row++;  // 不可能出现在该行,排除
        }
        else {
            return true;   // 找到了
        }
    }
    return false;    // 没找到
}

我们定义 row 和 col,初始化使 array[row][col] 能指向右上角,此时 array[row][col] 必然是当前行最大的,当前列最小的。主要判断目标值和 array[row][col] 的大小,如果比它小,那肯定不会出现在该列,因为垂直往下只会有更大的,所以肯定不在该列,直接 col-- 排除该列。如果比它大。那肯定不会出现在该行,因为横向只有比它还要小的,所以肯定不在该行,直接 row++ 排除该行,走到下一行。如此一来我们搜索的范围越来越小,最后如果有满足 target == array[row][col] 条件的情况就可以返回 true 了,数组都缩没了还没找到那自然是根本不存在目标值,循环外返回 false 即可。至于这里的循环边界的控制,是决定循环什么时候结束的关键!row 作为行,是肯定要比行长度 arrayRowLen 小的,如果 row++ 到 arrayRowLen 这个边界了,就会越界。同样,col-- 到 0 下标时如果在继续减也会越界,所以这里循环控制把控好就行。

时间复杂度为 \color{}O(R+C)(R 表示行 C 表示列),空间复杂度为 O(1)


「 法三 」逐行二分

" 逐行二分搜之…… "

💡 思路:我们可以对数组的每行进行二分查找!手写一个 BinarySearch 函数,然后只需要逐行传递给该函数即可。对数组地每一行使用二分,其时间复杂度为 \color{}O(R\, logN),空间复杂度为 \color{} O(1)

💬 代码演示:C语言

#include <stdbool.h>
int binary_search(int* arr, int sz, int k) {
    int left = 0;
    int right = sz - 1;
    int mid = 0;

    while (left <= right) {
        mid = (left + right) / 2;
        if (arr[mid] < k) {
            left = mid + 1;
        } 
        else if (arr[mid] > k) {
            right = mid - 1;
        } 
        else {
            return mid;  // 找到了
        }
    }

    return -1;  // 没找到
}

bool Find(int target, int** array, int arrayRowLen, int* arrayColLen ) {
    int ret = 0;   // 用于接收返回值

    // 遍历行,将行依次传给 binary_search 函数
    for (int i = 0; i < arrayRowLen; i++) {
        ret = binary_search(array[i], *arrayColLen, target);
        if (ret != -1) {
            return true;   // 找到了
        }
    }
    return false;
}

我们手动实现好 BinrarySearch 函数后,我们只需要写一个 for 循环,把行依次传入该数组。会先把第一行数组传给该函数,如果找到了就结束了,没找到就继续把下一行传给该函数以此类推……最后如果没有找到根据题意返回 -1 即可。

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2023.5.24
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

牛客网. 剑指offer 题解 [EB/OL]. []. https://www.nowcoder.com/exam/oj/ta?tpId=13.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/572466.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[时间同步] vscode chatGPT提供的程序打包封装成api解决方案怎么样

背景 在完成gnss时间同步程序大部分需求串口配置、串口数据中找出推荐定位信息RMC解析UTC时间以及UTC时间更新系统时间等功能后&#xff0c;有个需求比较特别&#xff0c;需要客户来操作。当车辆在地库场景待时间过久后重新回到地面&#xff0c;一直在自走的系统时间与又定位好…

Three.js--》探索Three.js:学习和就业的完整指南

目录 three.js的学习建议 WebGL前端工程师工作待遇相关问题 本篇文章主要给大家介绍一下如何学习Web3D可视化&#xff0c;具体说就是怎么学习WebGL、Three.js、3D建模等。 three.js的学习建议 在过去互联网是人联网的时代&#xff0c;开发人和人之间的联系的Web应用&#x…

MoveIt2中使用trac_ik

文章目录 1.下载trac_ik的源码2.安装 NLopt library3.编译源码4.使用 在ros1moveit1中&#xff0c;使用trac_ik是很简单的一件事情&#xff1a;【TRAC-IK Kinematics Solver】 但是在Ros2中&#xff0c;无论MoveIt2也好&#xff0c;还是trac_ik也好&#xff0c;都没有提供标准的…

如何编写快速高效的SQL查询(三)——高性能索引策略与样例

是时候开始讨论使用索引了&#xff01;正确地创建和使用索引是实现高性能查询的基础&#xff0c;现在我们一起来看看如何真正地发挥这些索引的优势。 高效地选择和使用索引有很多种方式&#xff0c;其中有些是针对特殊案例的优化方法&#xff0c;有些则是针对特定行为的优化。…

Redis7实战加面试题-高阶篇(Redis线程与IO多路复用,BigKey,缓存双写)

Redis线程 面试题&#xff1a;Redis为什么选择单线程? 这种问法其实并不严谨&#xff0c;为啥这么说呢?Redis的版本很多3.x、4.x、6.x&#xff0c;版本不同架构也是不同的&#xff0c;不限定版本问是否单线程也不太严谨。 1 版本3.x &#xff0c;最早版本&#xff0c;也就是…

高压放大器在3D打印中的应用

随着3D打印技术的快速发展&#xff0c;高压放大器在3D打印中的应用越来越受到人们的关注。高压放大器在3D打印中扮演着非常重要的角色&#xff0c;可以提高3D打印的效率和精度&#xff0c;从而实现更高的打印质量。本文将详细介绍高压放大器在3D打印中的应用及其原理。 高压放…

SAP 从入门到放弃系列之安全库存

概念 安全库存的主要目的是以一定数量的库存或时间的作为缓冲区间&#xff0c;以应对供需之间波动的影响。SAP ERP 系统提供两种类型的安全库存&#xff1a;静态安全库存和动态安全库存&#xff08;即安全天数供应&#xff09;。 静态安全库…

《程序员面试金典(第6版)》面试题 02.08. 环路检测(哈希法,双指针,检测链表是否有环)

题目描述 给定一个链表&#xff0c;如果它是有环链表&#xff0c;实现一个算法返回环路的开头节点。若环不存在&#xff0c;请返回 null。 题目传送门&#xff1a;面试题 02.08. 环路检测 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链…

操作系统第一章练习题

目录 问答题 选择题 填空题 判断题 问答题 1、设计现代OS的主要目标是什么&#xff1f; 答&#xff1a;设计现代OS的主要目标是&#xff1a;方便性、有效性、可扩充性、开放性 2、OS的作用可表现在哪几个方面&#xff1f; 答&#xff1a;&#xff08;1&#xff09;从一…

每个软件测试人员必须具备的12大技能

作者 | Kiran Beladiya 赛希翻译组 译 作为一名软件测试员&#xff0c;掌握一些技术技能是非常必要的&#xff0c;这可以使应用程序变得更好。让我们来看看对任何软件测试员来说必不可少的技能。 没有人能成为这样的软件测试员。要获得这个职业&#xff0c;每个测试人员都必…

公文写作技巧:作风建设类排比句40例

1.面对突发任务时&#xff0c;是主动请缨还是被动服从&#xff1b;遇到棘手问题时&#xff0c;是迎难而上还是推诿回避&#xff1b;在荣誉面前&#xff0c;是正确对待还是邀功请赏&#xff1b;汇报工作时&#xff0c;是真实客观还是弄虚作假。 2.不是敷衍了事图轻松&#xff0…

Spring、SpringMVC

文章目录 Spring一、Spring概述二、Spring快速入门1. Spring开发步骤2. Spring配置文件2.1 Bean标签基本2.2 Bean标签范围2.3 Bean生命周期2.4 Bean实例化三种方式 3. Bean的依赖注入3.1 Bean依赖注入概念3.2 Bean依赖注入方式3.3 Bean的依赖注入的数据类型3.4 引入其他配置文件…

Visual Studio 2022使用CMake+MinGW+Clang+LLDB作为开发环境

笔者前面写了两篇关于Visual Studio 2022使用MinGW的博文&#xff1a;《Visual Studio 2022使用MinGW来编译调试C/C程序》、《Visual Studio 2022 CMakeMinGWGDB 调试目标程序》&#xff0c;这两篇博文都是介绍的是GCCGDB的编译与调试&#xff0c;本文笔者介绍的则是ClangLLDB的…

微服务框架 01SOA和传统服务与微服务的差别

SOA思想 1.SOA思想介绍 面向服务的架构&#xff08;SOA&#xff09;是一个组件模型&#xff0c;它将应用程序的不同功能单元&#xff08;称为服务&#xff09;进行拆分&#xff0c;并通过这些服务之间定义良好的接口和协议联系起来。接口是采用中立的方式进行定义的&#xff0c…

20行代码来爬取某某云的榜单音乐

今天来爬爬音乐&#xff0c;一丝丝的无聊 前期准备 软件环境 Python3.8pycharm 模块 requests、re、os 三个 其中requests是第三方模块需要手动安装一下 re、os都是内置模块&#xff0c;不需要安装 浏览器开发者工具 咱们需要学会如何使用开发者工具。 对此很多小伙伴都…

基于Spring Boot的仿豆瓣平台

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 后端为了提高性能和用户体验&#xff0c;该系统平台使用Spring Boot集合Spring&#xff0c;Spring MVC和MyBatis框架做基础&#xff0c;并且集成Spring Data框架&#xff0c;MySQL做数据持久化&#xff0c;Redis缓存提高速度&…

专业解读财务共享实现财务数智化转型的有效路径

近年来&#xff0c;随着数字经济的飞速发展&#xff0c;各大企业全面开启数智化转型之路&#xff0c;作为企业数智化转型的重要内容&#xff0c;财务数智化转型始于财务共享服务。然而&#xff0c;财务共享建设并不是一蹴而就的&#xff0c;如何通过财务共享实现财务数智化转型…

Linux安装与配置ansible

文章目录 Linux安装Ansible一、安装ansible二、配置管理节点和远程主机的连接使用ssh-keygen生成密钥复制SSH密钥到远程主机验证SSH配置 三、主机清单配置文件四、Ansible的配置文件 Linux安装Ansible 在Linux上安装Ansible可以通过以下步骤完成&#xff1a; 一、安装ansible…

npm更换成淘宝镜像源及cnpm使用

1.需求由来 由于node安装插件是从国外服务器下载&#xff0c;受网络影响大&#xff0c;速度慢且可能出现异常。所以如果npm的服务器在中国就好了&#xff0c;所以我们乐于分享的淘宝团队&#xff08;阿里巴巴旗下业务阿里云&#xff09;干了这事。来自官网&#xff1a;“这是一…

Python常见错误(Error)一览大全——初学者必看

路漫漫其修远兮&#xff0c;学习python任重而道远&#xff0c;对于初学者来说&#xff0c;最难受的就是报错&#xff0c;其次是错误翻译好了&#xff0c;可是又不会改了&#xff0c;那么今天&#xff0c;我已IKUN老师的身份&#xff0c;结合我们常见的错误&#xff0c;做一个常…