剑指offer --- 二维数组中的元素查找

news2024/10/6 14:24:32

目录

一、读懂题目

二、思路分析

三、代码呈现

总结


一、读懂题目

题目:

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的个二维数组和一个整数,判断数组中是否含有该整数。

首先我们分析由于该二维数组有序,且呈现从上到下、从左到右递增的规律,比如该示例:

我们需要从中找出输入数是否存在,如果存在返回true,反之返回false即可。

二、思路分析

        当然在非大数问题的案例而言,我们完全可以使用逐层逐位遍历的方式寻找是否有值存在,但是如果数组行列数比较大时,这种方法的效率是极其有限的。既然题中指出存在横纵两方向的递增规律,那么我们只需要从这部分下手即可降低时间复杂度,减少程序的运行时间。

现假设我们需要查找数字 7 是否位于数组中,有以下思路:

        首先我们选取数组右上角的数字 9。由于 9 大于 7,并且 9 还是第 4 列的第一个(也是最小的)数字,因此 7 不可能出现在数字 9 所在的列。于是我们把这一列从需要考虑的区域内剔除,之后只需要分析剩下的 3 列。

在剩下的矩阵中,位于右上角的数字是 8。同样 8 大于 7,因此 8 所在的列我们也可以剔除。接下来我们只要分析剩下的两列即可。

在由剩余的两列组成的数组中,数字 2 位于数组的右上角。2 小于 7,那么要查找的 7 可能在 2 的右边,也有可能在 2 的下边。在前面的步骤中,我们已经发现 2 右边的列都已经被剔除了,也就是说 7 不可能出现在 2 的右边,因此 7 只有可能出现在 2 的下边。于是我们把数字 2 所在的行也剔除,只分析剩下的三行两列数字。

在乘下的数字中,数字 4 位于右上角,和前面一样,我们把数字 4 所在的行也删除,最后剩下两行两列数字。

在剩下的两行两列矩阵中,因为右上角已经是 index = 7 的值,所以直接返回 true 表示查找成功即可。通过上面的分析,我们不难写出代码,当然理应从代码的鲁棒性出发,特别注意对特殊情况的处理。

正常输入情况:

        1. 二维数组中包含查找的数字 (查找的数字是数组中的最大值和最小值,查找的数字介于数组中的最大值和最小值之间)。
        2. 二维数组中没有查找的数字 (查找的数字大于数组中的最大值,查找的数字小于数组中的最小值,查找的数字在数组的最大值和最小值之间但数组中没有这个数字)。

特殊输入测试:

        输入空指针。

三、代码呈现

利用数组下标访问实现: 

template<typename T>
bool find_num(const T** arr_2, size_t row, size_t col, const T target)
{
	if (arr_2 == nullptr || row <= 0 || col <= 0)
	{
		throw exception("find_num:: the input parameters do not meet the requirements");
	}

	int left = 0;			// 控制行舍去
	int right = col - 1;	// 控制列舍去

	while (left < row && right > 0)
	{
		if (arr_2[left][right] == target) { return true; }
		if (arr_2[left][right] > target)	// 向左遍历求更小值
		{
			right--;
		}
		if (arr_2[left][right] < target)	// 向下遍历求更大值
		{
			left++;
		}
	}
	return false;
}

测试方法一:

当我们需要调用此方法验证时,需要传入动态数组的二阶指针以符合函数参数列表要求,如下:

void test1_1()
{
	int* arr[] = 
    {
	    new int[4] {1,2,8,9},
	    new int[4] {2,4,9,12},
	    new int[4] {4,7,10,13},
	    new int[4] {6,8,11,15}
	};
	int target = 7;

	try
	{
        // 使用const_cast修改的效果仅在转换语句行有效,不会真正改变对象的常量性
		bool ret = find_num<int>(reinterpret_cast<const int**>(const_cast<const int**>(arr)), 
                sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]) / sizeof(arr[0][0]), target);
		if (ret == true) { cout << "已找到!" << endl; }
		else { cout << "未找到" << endl; }
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "other default!" << endl;
	}
    
    // 释放堆区空间
    for (int i = 0; i < 4; i++)
    {
	    delete[] arr[i];
	    arr[i] = nullptr;
    }
}

运行结果:

利用指针偏移访问实现: 

测试方法二:

        出于我们用到的静态数组所占比例大多数情况不比动态数组少,所以最好还得想想办法解决静态二维数组传参的问题,因为考虑到二维数组在内存中是连续存放的,每行以 “Z” 字形被连续存放在内存中,所以可以在该函数功能内直接将其看作一维数组处理,由于传入的指针是二阶指针,所以将其类型强转为一阶指针,再利用指针偏移来访问各个元素即可。来看代码:

函数实现部分:

// 利用指针访问元素
template<typename T>
bool find_num_ptr(const T** arr, size_t row, size_t col, const T target)
{
	if (arr == nullptr || row <= 0 || col <= 0)
	{
		throw exception("find_num:: the input parameters do not meet the requirements");
	}

	const T* arr_2 = reinterpret_cast<T*>(arr);  // 注意强转指针部分
	int left = 0;			// 控制行舍去
	int right = col - 1;	// 控制列舍去

	while (left < row && right > 0)
	{
		if (*(arr_2 + left * col + right) == target) { return true; }
		if (*(arr_2 + left * col + right) > target)	// 向左遍历求更小值
		{
			right--;
		}
		if (*(arr_2 + left * col + right) < target)	// 向下遍历求更大值
		{
			left++;
		}
	}
	return false;
}

测试函数功能部分(传入静态数组): 

void test1_2()
{
	int arr[4][4] =
	{
		{1,2,8,9},
		{2,4,9,12},
		{4,7,10,13},
		{6,8,11,15}
	};

	int target = 7;
	try
	{
        // 正常将二维数组名强转为二阶指针(函数功能内部再将其转换为一阶指针进行元素访问)
		bool ret = find_num_ptr<int>(reinterpret_cast<const int**>(arr), 
									sizeof(arr) / sizeof(arr[0]), 
									sizeof(arr[0]) / sizeof(arr[0][0]), 
									target);
		if (ret == true) { cout << "已找到!" << endl; }
		else { cout << "未找到" << endl; }
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "other default!" << endl;
	}
}

运行结果:

        当然我们可以在函数功能外部就把二维数组数组名直接强转为 T* 类型,但是这样函数参数列表就要变成一阶指针来接收。当我们只看到函数声明的时候,充满迷惑性,一阶指针到底表示的是一维数组还是对二维数组的特殊表示? 所以这种方法可行,但是宏观来说并不可取,就不再代码演示。


总结

        本篇文章本身篇幅不长,着重展开讲述了二维数组中查找的高效方法和二维数组被参数列表中二阶指针接收的处理形式两个方面。通过该题了解到要想减小程序的时间复杂度就要充分挖掘题干条件,尤其是已经有序的数组访问和查找的问题中需要考虑二分等方法缩减每轮下一步问题的规模。

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

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

相关文章

用baostock库获取沪深300成分股

先看效果&#xff1a; 代码&#xff0c;bs_get_hs300.py import baostock as bs import pandas as pd# 登陆系统 lg bs.login() # 显示登陆返回信息 print(login respond error_code:lg.error_code) print(login respond error_msg:lg.error_msg)# 获取沪深300成分股 rs bs…

Android开发工具介绍(adb、AVD、DDMS)

目录 1. adb 1.1 查看设备 1.2 安装软件 1.3 卸载软件 1.4 登录设备 shell 1.5 从计算机上发送文件到目标机 1.6 从目标机上下载文件到计算机 1.7 显示帮助信息 2. AVD 2.1 AVD 的创建 2.2 启动 AVD 模拟器 3. DDMS 3.1 DDMS的启动方法 3.2 DDMS 工…

H5游戏源码分享-密室逃脱小游戏(考验反应能力)

H5游戏源码分享-密室逃脱小游戏&#xff08;考验反应能力&#xff09; 预判安全位置&#xff0c;这个需要快速的反应能力 源码 <!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /&…

WEB登录设备控制台异常——TLS协议问题

问题描述&#xff1a;登录设备web控制台浏览器报错&#xff0c;切换其他浏览器也有问题。 出现这个问题&#xff0c;大概率是网站支持的TLS协议很低&#xff0c;而浏览器的TLS协议很高&#xff0c;那么就是是降浏览器的TLS版本。 解决步骤&#xff1a; 1、火狐浏览器地址栏输…

DbVisualizer和DBeaver启动不来,启动报错

启动报错 大多数启动报错都是因为你没有用管理员身份运行程序&#xff0c;提示的错误都是八竿子打不着的什么jdk、jvm问题。 比如DbVisualizer提示什么jvm配置参数&#xff0c;实际dbvis.exe 用管理员身份打开即可&#xff08;右键 dbvis.exe->属性->兼容性->勾上 “…

计算机毕业设计选题推荐-社区志愿者服务微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Android View 的绘制流程之 Measure 过程详解

由于 performTraversals 方法比较长&#xff0c;看一个简化版&#xff1a; // ViewRootImpl 类 private void performTraversals() {// 这个方法代码非常多&#xff0c;但是重点就是执行这三个方法// 执行测量performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);…

[EFI]asus strix b760-i 13900F电脑 Hackintosh 黑苹果efi引导文件

硬件型号驱动情况主板 asus strix b760-i 处理器 I9 13900F 已驱动内存crucial ddr5-5200 64gb(32gb*2)(overclock 5600)已驱动硬盘 WD black sn850 500g*2 已驱动显卡rx570已驱动声卡Realtek ALCS1220A已驱动网卡Intel I225-V 2.5 Gigabit Ethernet已驱动无线网卡蓝牙Fevi T91…

Spring Cloud之Gateway网关学习【详细】

目录 统一网关Gateway 网关的实现 搭建网关 编写配置文件 路由断言工程 路由的过滤器 全局过滤器 网关过滤器执行顺序 网关的cors跨域配置 问题及解决 统一网关Gateway 网关的实现 SpringCloud中存在两种网关 gateway&#xff1a;基于Spring5中提供的WebFlux实现&a…

[AUTOSAR][诊断管理][ECU][$28] 通信控制

文章目录 一、简介二、 应用场景三、通信控制基本原理四、服务请求请求格式请求实例服务响应正响应格式负响应NRC支持五、常见Bug大揭秘六、示例代码28_comm_ctl.c一、简介 根据ISO14119-1标准中所述,诊断服务28服务主要用于网络中的报文发送与接受,比如控制应用报文的发送与…

java支持3种网络编程模型,以及在web项目中的应用

之前有总结过linux中的5种IO模型 https://blog.csdn.net/weixin_45068892/article/details/127424119 本次主要讲一下java中支持的IO模型。 Java共支持3种网络编程IO模式&#xff0c;及应用场景 BIO NIO AIO https://blog.csdn.net/CSDN_GIA/article/details/128535848 BIO…

idea:解决jsp request.getParameter爆红的问题

文章目录 1. 复现错误2. 分析问题3. 解决问题 1. 复现错误 今天在写jsp代码时&#xff0c;出现如下错误&#xff1a; 2. 分析问题 这是没有引入相关jsp的相关jar包引起的。 我们可按如下步骤&#xff0c;引入jsp的相关jar包。 3. 解决问题 File -> Project Structure -&g…

【Truffle】二、自定义合约测试

一、准备测试 上期我们自己安装部署了truffle&#xff0c;并且体验了测试用例的整个测试流程&#xff0c;实际开发中&#xff0c;我们可以对自己的合约进行测试。 我们首先先明白自定义合约测试需要几个文件 合约文件&#xff1a;既然要测试合约&#xff0c;肯定要有合约的源码…

vue3 Teleport组件

<Teleport> 是一个内置组件&#xff0c;它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层 的位置去。 <template><el-button click"dialogVisible true">打开弹窗</el-button><el-dialogv-model"dialogVisible&…

一文教你解决git请求github时候超时的问题

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一文教你解决git请求github的时候超时问题 一. 问题二. 当前 ssh 实现原理三. 创建ssh key3.1 将ssh key加入github配置中3.2 测试连…

洛谷P1765 手机 / 秋季赛 九宫格

手机 题目描述 一般的手机的键盘是这样的&#xff1a; 要按出英文字母就必须要按数字键多下。例如要按出 x \tt x x 就得按 9 9 9 两下&#xff0c;第一下会出 w \tt w w&#xff0c;而第二下会把 w \tt w w 变成 x \tt x x。 0 0 0 键按一下会出一个空格。 你的任务是…

算法笔记【6】-简单选择排序算法

文章目录 一、基本原理二、实现步骤三、优缺点分析 一、基本原理 在排序算法中&#xff0c;简单选择排序是一种基本且直观的排序方法。尽管它的性能较冒泡排序稍好&#xff0c;但仍然属于较慢的排序算法。本文将详细介绍简单选择排序算法的原理、步骤&#xff0c;并讨论其优缺…

AcWing第 127 场周赛 - AcWing 5283. 牛棚入住+AcWing 5284. 构造矩阵 - 模拟+快速幂+数学

AcWing 5283. 牛棚入住 题目数据范围不大&#xff0c;直接暴力模拟即可 按照题目所说的意思即可。 #include <math.h> #include <stdio.h> #include <algorithm> #include <cstring> #include <iostream> using namespace std; const int N 1…

清华训练营悟道篇之操作系统的内存管理

文章目录 SV39多级页表的硬件机制物理页帧的管理多级页表管理内核与应用的地址空间 SV39多级页表的硬件机制 三级页表 共3*9 27位虚拟页号 第 26-18 位为一级页索引 VPN0第 17-9 位为二级页索引 VPN1第 8-0 位为三级页索引 VPN2 每个页表都用 9 位索引 2^9 512 个页表项每个页…

数据结构-顺序表6

八.返回key的前驱下标&#xff0c;如果不存在&#xff08;key无前驱&#xff0c;在表头&#xff09;返回-1函数 思路&#xff1a;找到key的前驱&#xff0c;调用Search函数找key就可以 这里把i<0写成i<0&#xff0c;也是可以的&#xff0c;因为i0接着进入下面return i-1…