剑指offer第2版:搜索算法(二分/DFS/BFS)

        查找本质就是排除的过程,不外乎顺序查找、二分查找、哈希查找、二叉排序树查找、DFS/BFS查找  

一、p39-JZ3 找出数组中重复的数字(利用特性)

数组中重复的数字_牛客题霸_牛客网

方法1:全部排序再进行逐个扫描找重复。  时间复杂度n*logn  空间复杂度1  

class Solution {public:int duplicate(vector<int>& nums) {int n = nums.size();if (n == 0) return -1;sort(nums.begin(), nums.end());for (int i = 1; i < n; ++i)if (nums[i] == nums[i - 1]) return nums[i];return -1;}
};

方法2:构造哈希——找到第一个重复的数就返回,否则正常插入    时间复杂度n  空间复杂度n

class Solution {public:int duplicate(vector<int>& nums) {int n = nums.size();if (n == 0) return -1;unordered_set<int> s;for (int i = 0; i < n; ++i)if (s.count(nums[i])) return nums[i];else s.insert(nums[i]);return -1;}
};

那么我们能否避免n的空间复杂度呢?? 

方法3:边排序边比较——我们会发现数组中的数字都在0-n-1的范围内,如果这个数组中没有重复的数字,那么当这个数组排序之后数字i将出现在下标为i的位置,同时数组中有些位置可能没有数据    因此我们可以重拍这个数组,当扫描到下标为i的数字m的时候,首先看看m和i是否相同,如果不相同则拿他和第m个数字进行比较,如果相等说明找到了重复数字,如果不相等则把第i个数和第m个数进行交换,把m放在属于他的位置,接下来再重复这个比较、交换的过程,直到我们发现了一个重复的数字。          此时我们会发现每个数字至多比较2次就可以找到自己的位置,所以时间复杂度是n 空间复杂度1

class Solution {public:int duplicate(vector<int>& nums) {//重排 边排序边比较int n = nums.size();if (n == 0)return -1;for (int i = 0; i < n; ++i) {while (nums[i] != i)if (nums[i] == nums[nums[i]]) return nums[i];else swap(nums[i], nums[nums[i]]);}return -1;}
};

二、扩展p41-JZ3 不修改原数组基础上找出重复数(二分)

287. 寻找重复数 - 力扣(LeetCode)

     这题和上一题相似,但是区别1:不可修改原数组。区别2:该题中1-n只有n个数,但是数组中包含超过n个数,所以根据鸽巢原理,一定至少有一个数字是重复的!

     我们把1-n的数字从中间的数字m分成两部分(概数一定出现在左半部分或者右半部分),前半部分为1->m 后面一半为m->n-1,如果1-m的数字超过了m,那么这一半钟一定包含了重复的数字,否则另一半m+1->n-1的区间里一定包含重复的数字,这样我们可以继续把包含重复数字的区间一分为2,直到找到一个重复的数字。

     按照上面的二分查找的思路,那么countrange会给调用logn次,而每次需要n的时间,所以时间复杂度为n*logn,空间复杂度1   相比于直接用哈希的做法而言,等同于用时间换空间!!但是要注意的是这个算法并不能保证找出所有重复的数字!!也不能确定该数究竟出现了几次  (所以一定要问清面试官的要求,看是要找出任意一个 还是找出所有,或者是对时间性能有什么要求,比如是空间还是时间优先) 

    这里使用左端点区间二分法

class Solution {
public:int countrange(vector<int>& nums, int begin, int end) {int count = 0;int n = nums.size();for (int i = 0; i < n; ++i)if (nums[i] >= begin && nums[i] <= end)++count;return count;}int findDuplicate(vector<int>& nums) {// 不修改原数组的基础上找到重复的数字int n = nums.size();if (n == 0)return -1;int left = 1, right = n - 1;while (left < right) {int mid = left + (right - left) / 2;int count = countrange(nums, left, mid);if (count > (mid - left + 1))right = mid; // 说明区间一定在左边elseleft = mid + 1;}// 此时left==right了 我们去看看该数的数目if (countrange(nums, left, right) > 1)return left;return -1;}
};

三、p44-JZ4 二维数组中的查找某数(杨氏矩阵)(利用特性)

二维数组中的查找__牛客网

       要利用他每一行和每一列都递增的特性!!先从右上角或者左下角进行比较(比如右上角,如果比右上角小,此时就可以排除列,比右上角大就可以排除行,所以无论如何都可以排除一行或者一列!!)。这样的时间复杂度就是O(m+n)

class Solution {public:bool Find(int target, vector<vector<int> >& array) {int i = 0, j = array[0].size() - 1;while (i < array.size() && j >= 0)if (target < array[i][j]) --j;else if (target > array[i][j]) ++i;else return true;return false;}
};

四、p82-JZ11 旋转数组的最小数字(二分)

旋转数组的最小数字_牛客题霸_牛客网

        我们要注意旋转之后的数组实际上可以划分为两个排序的子数组,而前面的元素普遍大于等于后面子数组的元素!最小的元素恰好是这两个子数组的分界线!

       假如该题是严格升序的,那么图应该如下所示,此时我们会发现他具有二段性(根据某个规律可以将数组一分为二,然后再舍去其中一部分!!)

    此时我们要找的是右边区间的左端点,所以要用左端点区间二分法,此时当中间的数比左边大的时候,说明该数字在左区间,左区间要跳过去,当中间的数比右边小的时候,说明该数字在右区间,此时要右区间要跟过来。

但是有两种特殊情况:1、如果恰好旋转了n次,此时相当于原数组没有变化,那么此时直接返回第一个就可以了!!2、因为该题不是严格增,而是非递减,所以存在相等的情况,比如{1,0,1,1,1} 很有可能出现nums[mid]==left==right 此时我们就无法进行二分了!!这个时候我们只能按照笨方法进行一个个遍历了!! 

class Solution {public:int minNumberInRotateArray(vector<int>& nums) {int left = 0, right = nums.size() - 1;while (left < right) {if (nums[left] < nums[right]) return nums[left];//情况1int mid = left + (right - left) / 2;if (nums[mid] > nums[left]) left = mid + 1;else if (nums[mid] < nums[right]) right = mid;else ++left;//此时说明nums[left]==nums[right]==nums[mid] 情况2}return nums[left];}
};

五、p263-JZ53 排序数组中查找数字(二分)

数字在升序数组中出现的次数_牛客题霸_牛客网

如果我们直接用朴素二分找到一个3,那么我们并不确定左边有多少右边有多少,所以我们需要尝试用左端点区间和右端点区间法找到这个数字的范围。 即找到第1个k和最后1个k。

class Solution {
public:int GetNumberOfK(vector<int>& nums, int k) {//只要找到第一个k和最后一个k  就可以统计出k在数组中出现的次数int n=nums.size();if(n==0) return 0;//第一个k要用左端点区间法int left=0,right=n-1;while(left<right){int mid=left+(right-left)/2;if(nums[mid]<k) left=mid+1;else right=mid; //最终会落在区间的左端点}if(nums[left]!=k) return 0;//找不到就返回//第二个k要用右端点区间法int begin=0,end=n-1;while(begin<end){int mid=begin+(end-begin+1)/2;if(nums[mid]<=k) begin=mid;//最终会落在区间的右端点else end=mid-1; }return end-right+1;}
};

 六、扩展p266-JZ53 0~n-1中缺失的数字(二分)

LCR 173. 点名 - 力扣(LeetCode)

        这题有一个只管的做法就是直接用等差求和公式n(n-1)/2求出数字0~n-1所有数字之和,然后减掉整个数组的数字,就可以得到缺失的数字了,但是这种做法需要n的时间复杂度

       我们会发现该题具有二段性,即1、缺席位置之前的数和下标是一样的 2、缺席位置之后的数和下标是不一致的。所以此时我们就可以通过下标和数是否一致来进行二分查找!!

       因为我们要找的是不符合要求的第一个,所以要用左端点区间法

class Solution {
public:int takeAttendance(vector<int>& nums) {int n=nums.size();int left=0,right=n-1;while(left<right){int mid=left+(right-left)/2;if(nums[mid]==mid) left=mid+1;//说明mid在左区间 要跳跃else right=mid;}//此时right指向的就是第一个缺席的同学的位置//但是极端情况如果缺席的正好是最后一个同学 if(right==nums[right]) return right+1;return right;}
};

七、p197-JZ38 字符串的排列(DFS) 

字符串的排列_牛客题霸_牛客网

      我们可以把他拆分成小问题,根据实例2我们知道有重复的数字,所以我们可以先排序一下让相同的字母挨着一块,当前面的数跟自己相等且没有选的时候,那么自己也不能选。 

class Solution {public:vector<string> ret;string path;bool check[10] = {0};void dfs(string s) {if (path.size() == s.size()) {ret.emplace_back(path);return;}for (int i = 0; i < s.size(); ++i) {if(check[i]||i>0&&s[i-1]==s[i]&&!check[i-1])  continue;//但是前面的数没选path.push_back(s[i]);check[i] = true;dfs(s);path.pop_back();check[i] = false;}}vector<string> Permutation(string str) {if (str.empty()) return {};//要去重 所以排序一下sort(str.begin(), str.end());//保证相同的放在一起dfs(str);return ret;}
};

八、P89-JZ12 矩阵中的路径(DFS)

矩阵中的路径_牛客题霸_牛客网

用标记数组和向量数组 

class Solution {public:bool check[21][21] = {0}; //标记数组int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0}; //向量数组int m,n; //长度和宽度bool hasPath(vector<vector<char>>& nums, string word) {m = nums.size();if (m == 0) return false;n = nums[0].size();for (int i = 0; i < m; ++i)for (int j = 0; j < n; ++j)if (nums[i][j] == word[0])if (dfs(nums, word, i, j, 1)) return true;return false;}bool dfs(vector<vector<char>>& nums, string& word, int i, int j, int pos) {if (pos == word.size()) return true;check[i][j] = true;for (int k = 0; k < 4; ++k) {int x = dx[k] + i, y = dy[k] + j;if (x >= 0 && x < m && y >= 0 && y < n && !check[x][y] &&word[pos] == nums[x][y])if (dfs(nums, word, x, y, pos + 1)) return true;}check[i][j] = false; //找错了就回溯return false;}
};

 九、p92-JZ13 机器人的运动范围(DFS/BFS)

机器人的运动范围_牛客题霸_牛客网

思路1:DFS 

class Solution {
public:int dx[4]={-1,1,0,0};int dy[4]={0,0,1,-1};int vis[101][101]={0};//标记数组int ret=0;//统计格子数int movingCount(int threshold, int rows, int cols) {if(threshold==0) return 1;dfs(threshold,rows,cols,0,0);return ret;}void dfs(int threshold, int m, int n,int i,int j){if(i==m||j==n) return;vis[i][j]=true;++ret;for(int k=0;k<4;++k){int x=dx[k]+i,y=dy[k]+j;if(x>=0&&x<m&&y>=0&&y<n&&!vis[x][y]&&check(threshold,x,y))dfs(threshold,m,n,x,y);}}bool check(int threshold,int x,int y){int sum=0;while(x){sum+=x%10;x/=10;}if(sum>threshold) return false;while(y){sum+=y%10;y/=10;} return sum<=threshold;}
};

思路2:BFS

class Solution {public:int dx[4] = {-1, 1, 0, 0};int dy[4] = {0, 0, 1, -1};int vis[101][101] = {0}; //标记数组int movingCount(int threshold, int rows, int cols) {if (threshold == 0) return 1;int ret = 1; //统计格子数queue<pair<int, int>> q;q.push({0, 0});vis[0][0] = true;while (!q.empty()) {auto&[i, j] = q.front();q.pop();for (int k = 0; k < 4; ++k) {int x = dx[k] + i, y = dy[k] + j;if (x >= 0 && x < rows && y >= 0 && y < cols && !vis[x][y] &&check(threshold, x, y)) {q.push({x, y});++ret;vis[x][y] = true;}}}return ret;}bool check(int threshold, int x, int y) {int sum = 0;while (x) {sum += x % 10;x /= 10;}if (sum > threshold) return false;while (y) {sum += y % 10;y /= 10;}return sum <= threshold;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小众宝藏分子生物学实验中常用的软件:InSequence

欢迎使用InSequence&#xff0c;正版免费使用&#xff0c;操作友好&#xff0c;小白也能轻松上手哦~ 1. 全新中文界面与更大操作空间 全中文简洁直观的操作界面&#xff0c;常用功能固定至工具栏&#xff0c;随心自定义更改工具栏&#xff0c;让科研人员能够更快速地上手&…

南京观海微电子----整流滤波电路实用

01 变压电路 通常直流稳压电源使用电源变压器来改变输入到后级电路的电压。电源变压器由初级绕组、次级绕组和铁芯组成。初级绕组用来输入电源交流电压&#xff0c;次级绕组输出所需要的交流电压。通俗的说&#xff0c;电源变压器是一种电→磁→电转换器件。即初级的交流电转化…

python 的框架 dash 开发TodoList Web 应用

TodoList Web 应用 项目简介 这是一个基于 Dash 和 SQLAlchemy 的现代化 TodoList Web 应用&#xff0c;提供了简单而强大的待办事项管理功能。 主要特性 添加新的待办事项删除待办事项标记待办事项为已完成/未完成分页展示待办事项列表实时更新和交互 技术栈 PythonDash …

tenda路由器WriteFacMac存在远程命令执行漏洞(CVE-2024-10697)

一、漏洞简介 tenda路由器WriteFacMac存在远程命令执行漏洞 二、漏洞影响 tenda路由器三、网络测绘&#xff1a; fofa: title"Tenda | LOGIN"四、复现过程 POC 1 GET /goform/WriteFacMac?macls%20%3E/webroot/1.txt HTTP/1.1 Accept: text/html,application/…

无需编码5分钟免费部署云上调用满血版DeepSeek

大家好&#xff0c;我是 V 哥。如何自己部署DeepSeek调用满血版。首先&#xff0c;如果你遇到了使用公共服务器时的延迟或限制&#xff0c;想要本地部署以获得更好的性能和稳定性。你是不是也想自己来部署DeepSeek呢&#xff0c;其实除了自己部署本地DeepSeek&#xff0c;还可以…

linux笔记3----防火墙(ubuntu)

防火墙管理工具 ubuntu里使用ufw来管理防火墙。ufw是一个管理防火墙规则的前端工具。本文阐述如何开启、关闭防火墙&#xff0c;放行指定端口。 因为我采用putty远程来使用&#xff0c;需要关闭防火墙或者放行22端口。 核心思维 因为ufw只是一个前端工具&#xff0c;所以一开…

【音视频】RTSP拉流: RTP负载AAC详解(三)

此文为系列文章&#xff0c;此系列主要讲解RTSP客户端的拉流及播放&#xff0c;文章持续更新&#xff0c;会从rtsp的基本协议讲起&#xff0c;如何一步步实现音视频的拉流过程&#xff0c;包括一系列涉及到的协议&#xff0c;rtsp&#xff0c;sdp&#xff0c; rtp&#xff08;本…

若依系统环境搭建记录

开源若依系统网上资料也很全的&#xff0c;本篇博文记录下自己搭建环境过程中遇到的一些问题。 配置Maven和编辑器选择 我懒得配置Eclipse了&#xff0c;直接用vscode作为编辑器&#xff0c;后面构建运行都用命令行。 配置数据库连接 按照mysql5.7按网上教程即可&#xff1…

【MySql】应用系统等保测评MySQL服务器相关策略设置以及最终验证,MySQL安全策略设置以及最终验证

文章目录 一、概要二、环境及实现三、前期准备四、操作步骤1、所有的数据库需要设置三权账户&#xff1a;系统管理员、网络管理员和安全管理员创建系统管理员账户&#xff1a;创建网络管理员账户&#xff1a;创建安全管理员账户&#xff1a; 2、所有数据库密码的负责度策略需要…

bootplus管理系统 file/download 任意文件下载漏洞

bootplus管理系统 file/download 任意文件下载漏洞 漏洞描述 bootplus是基于SpringBoot + Shiro + MyBatisPlus的,拥有接口管理,权限管理,监控组件等功能的一体化权限管理框架。该项目中的file/download接口存在任意文件下载漏洞, 攻击者可以通过该漏洞下载查看目标系统的…

《open3d qt 网格采样成点云》

open3d qt 网格采样成点云 效果展示二、流程三、代码效果展示 二、流程 创建动作,链接到槽函数,并把动作放置菜单栏 参照前文 三、代码 1、槽函数实现 void on_actionMeshUniformSample_triggered();//均匀采样 void MainWindow::

部署 DeepSeek R1各个版本所需硬件配置清单

DeepSeek-R1 通过其卓越的推理性能和灵活的训练机制&#xff0c;在 2025 年的春节期间受到了广泛关注。 DeepSeek-R1 是一款高性能的 AI 推理模型&#xff0c;主要通过强化学习技术来增强模型在复杂任务场景下的推理能力。 在本地部署 DeepSeek-R1 时&#xff0c;尤其是完整的…

hive高频写入小数据,导致hdfs小文件过多,出现查询效率很低的情况

问题描述 hive高频写入小数据&#xff0c;导致hdfs小文件过多&#xff0c;出现查询效率很低的情况分析过程 先复现现象 select count() from ads.ads_sdd_flow_managemlt_to_ids_mm;–15分钟&#xff0c;小文件10983 select max(mm) from ads.ads_sdd_flow_managemlt_to_ids…

git用法(简易版)

介绍 git是一个版本管理工具 使用方法 建立仓库 第一步 git init&#xff1a;初始化仓库 第二步 git add .&#xff1a;将代码添加到暂存区 第三步 git commit -m "first"&#xff1a;为修改添加备注 第四步 git remote add origin 你的url 第五步 git pus…

顺序表SeqList(c语言)(动态顺序表)

前言&#xff1a; 顺序表是一种数据结构&#xff0c;是内存中存储数据的一种方式&#xff0c;他的内存连续性使得它有较高的缓存利用率&#xff0c;它在内存中广泛使用&#xff0c;比如数组&#xff0c;就是典型的顺序表。 实现思路&#xff1a; 一般是建立三个文件&#xf…

DeepSeek介绍本地部署保姆级教程

2025年春节前后&#xff0c;DeepSeek 如滚烫油锅中溅入的一碗水&#xff0c;瞬间激起千层浪&#xff0c;在网络世界里掀起了一场热议风暴&#xff0c;迅速火遍大江南北。无论是互联网行业的前沿先锋&#xff0c;还是传统行业的资深从业者&#xff1b;无论是专注于开发、测试、运…

Bash 中的运算方式

目录 概述&#xff1a; 1. (()) 运算符 2. let 命令 3. expr 命令 4. $[] 直接运算 5. bc&#xff08;计算器&#xff0c;支持浮点数&#xff09; 6. awk&#xff08;强大的文本处理工具&#xff0c;也可计算&#xff09; 概述&#xff1a; Bash 本身只支持整数运算&am…

主动视觉可能就是你所需要的:在双臂机器人操作中探索主动视觉

AV-ALOHA 系统使用用于 AV 的 VR 耳机实现直观的数据收集&#xff0c;并且 用于作的 VR 控制器或引线臂。这有助于捕捉全身和头部 远程作我们的真实和模拟系统的运动&#xff0c;记录来自 6 个的视频 不同的摄像头&#xff0c;并为我们的 AV 仿制学习策略提供训练数据。 加州大…

centos7 nexus3.77 搭建

1.确保安装了JDK sudo yum install -y java-17-openjdk java-17-openjdk-devel java -version 2.下载Nexus最新版 官网地址:Sonatype Nexus Repository Manager Community Edition | Download csdn下载:https://download.csdn.net/download/supercrsky/90384049 上传到nex…

html 点击弹出视频弹窗

一、效果: 点击视频按钮后,弹出弹窗 播放视频 二、代码 <div class="index_change_video" data-video-src="</