【leetcode100-063到068】【二分】六题合集

首先还是说一下通用框架,二分的整体结构基本都是设定搜索范围边界,检查中心元素,根据检查结果移动上界或下界来缩小搜索范围,直到范围中只剩一个可选元素(或没有可选)。

【搜索插入位置】

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

思路:

显然,初始的上下界是数组本身的起始和结尾。

循环条件我平时习惯设置为l<r,但本题有可能出现搜索范围内没有target的情况,所以我们设置为l<=r,在搜索范围中只剩一个元素的时候多进行最后一次检查,来确认究竟这个最后剩下的元素是不是我们想找的target。

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int l = 0, r = nums.size() - 1;int mid;while (l <= r) {mid = (l + r) / 2;if (nums[mid] == target)return mid;else if (nums[mid] < target)l = mid + 1;elser = mid-1;}return l;}
};

【搜索二维矩阵】

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非严格递增顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。

思路:

经典框架下有两种选择,一种把矩阵的每一行收尾相接看作一整个数组,然后对这个数组进行二分查找,就多了一个数组下标到矩阵行列号的转换步骤;另一种先在矩阵的第一列二分查找,确定该元素如果存在的话应该在哪一行,然后再在该行进行常规的二分查找即可。

非经典框架下还有一种思路,在该矩阵中,某元素向下走一定遇到比自己更大的元素,向左走一定遇到比自己更小的元素。因此,我们从右上角元素开始依次和target比较,根据比较结果来决定本次向下走或向左走,直到遇见target(查找成功)或走出边界(查找失败)

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int l = 0, r = matrix.size()-1, mid;while (l <= r) {mid = (l + r) / 2;if (matrix[mid][0] == target)return true;else if (matrix[mid][0] < target)l = mid + 1;elser = mid - 1;}if (l == 0)return false;int row = l - 1;l = 0;r = matrix[0].size()-1;while (l <= r) {mid = (l + r) / 2;if (matrix[row][mid] == target)return true;else if (matrix[row][mid] < target)l = mid + 1;elser = mid - 1;}return false;}
};

【找第一个和最后一个】

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

思路:

分两轮,分别查找起始和结束位置,只需要在经典框架的基础上修改一些处理的细节:第一轮使mid在计算时偏左,在mid位置找到target时不是返回而是将右边界移到mid,因为左边可能还有target元素,要一直缩小范围到左边没有才是找到了起始位置,如果起始点查找成功,我们开启第二轮,第二轮和之前相反,使mid在计算时偏右,在mid位置找到target时将左边界移到mid。

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {vector<int> ans(2, -1);if(nums.size()==0)return ans;//找左边界int l = 0, r = nums.size() - 1, mid;while (l < r) {mid = (l + r) / 2;if (nums[mid] < target)l = mid + 1;elser = mid;}if (nums[l] == target) {//找到左边界的话再找右边界ans[0] = l;r = nums.size() - 1;while (l < r) {mid = (l + r + 1) / 2;if (nums[mid] <= target)l = mid;elser = mid - 1;}ans[1] = l;}return ans;}
};

【搜索旋转排序数组】

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

思路:

一种思路是,先把旋转的位置找到并将数组分成各自有序的两段,然后直接判断目标会在哪一段,并在那一段上进行常规的二分查找。

还有一种思路是,每次mid都会将数组分成两段,其中至少有一段是完全有序的,通过这一段的起始和末尾我们可以判断目标是否在这一段中,然后根据结果调整范围。

class Solution {
public:int search(vector<int>& nums, int target) {int l = 0, r = nums.size() - 1;int mid;while (l < r) {mid = (l + r) / 2;if (nums[mid] <= nums.back())r = mid;elsel = mid + 1;}int cut = l;if (cut == 0) {l = 0;r = nums.size() - 1;} else if (target >= nums[0]) {l = 0;r = cut - 1;} else {l = cut;r = nums.size() - 1;}while (l <= r) {mid = (l + r) / 2;if (nums[mid] == target)return mid;if (nums[mid] > target)r = mid - 1;elsel = mid + 1;}return -1;}
};

【旋转数组最小值】

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

思路:

上一题的思路一的第一步...不写了。

class Solution {
public:int findMin(vector<int>& nums) {int l = 0, r = nums.size() - 1;int mid;while (l < r) {mid = (l + r) / 2;if (nums[mid] <= nums.back())r = mid;elsel = mid + 1;}return nums[l];}
};

【两个正序数组的中位数】

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

思路:

一言难尽的题目,作为hard,它写起来确实挺费劲,但主要是费在不停的折腾那些下标和边界情况的处理细节上了,实际上算法本身还挺简单的。

中位数,本质上是找下标为size/2的数,或者说第size/2+1大的数(偶数的话下标为size/2和下标为size/2+1取平均),所以本题要完成的任务,其实就是给定一个k,在两个数组中找到整体第k大的数。

假设我们在两个数组中都找到了第k/2位的数,记为n1和n2,且n1<n2。此时n1最大有可能的位次是多少呢?

由于数组升序,数组1中n1前面的元素肯定都小于n1,也就是k/2-1个数。

而数组2中n2及以后的元素肯定都大于n1,n2前面的数可能小于n1也可能大于n1,最好的情况是全都小于n1,此时也有k/2-1个数。

所以,最好的情况是,比n1小的数有k-2个,此时n1的位次是k-1,这就意味着,n1不可能是我们想找的第k大的数,当然,数组1中n1前面的数只会更小,就更不可能是我们要找的数了,于是我们把数组1中n1及以前的元素全部丢弃,起始位置来到n1的下一个元素。

由于我们丢了一部分元素,在剩下的部分中我们要找的元素就不再排第k位了,而是要减掉丢弃部分的大小。

直到k减小到1时,也就是我们想找所有剩余元素中的最小的那个,此时返回两个数组各自的剩余部分起始元素中较小的那一个就可以了。

然后再来处理一下边界问题,由于两个数组长度不一定相同,是有可能出现一个数组被全丢完了的情况的,此时我们只需要直接返回另一个数组剩余部分中第k大的数就好(k是实时的当前要找的位次)

class Solution {
public:int findKth(int targetIndex, vector<int>& nums1, vector<int>& nums2) {int start1 = 0, start2 = 0;int m = nums1.size();int n = nums2.size();//找到该位序的数while (1) {int step = targetIndex / 2;if (start1 == m) {return nums2[start2 + targetIndex - 1];}if (start2 == n) {return nums1[start1 + targetIndex - 1];}//要找最小的if (targetIndex == 1) {return min(nums1[start1], nums2[start2]);}//找到当前要对比的数字//目前两组都没超int end1 = min(m - 1, start1 + step - 1);int end2 = min(n - 1, start2 + step - 1);// 1组丢if (nums1[end1] <= nums2[end2]) {targetIndex -= end1 - start1 + 1;start1 = end1 + 1;// 2组丢} else {targetIndex -= end2 - start2 + 1;start2 = end2 + 1;}}}double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {int m = nums1.size(), n = nums2.size();if (m + n == 1)return m == 0 ? nums2[0] : nums1[0];if ((m + n) % 2 == 1)return findKth((m + n) / 2 + 1, nums1, nums2);elsereturn (findKth((m + n) / 2, nums1, nums2) +findKth((m + n) / 2 + 1, nums1, nums2)) /2.0;}
};

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

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

相关文章

【Python基础014】字典的用法

1、定义 Python中的字典(dict)与列表、元组都不同&#xff0c;它不是序列&#xff0c;而是一种映射&#xff08;mapping&#xff09;。映射是一个其他对象的集合&#xff0c;但是它们是使用键-值&#xff08;key-value&#xff09;来存储对象&#xff0c;具有极快的查找速度。字…

视频编码器行业研究:预计到2028年全球市场规模将达到180.92亿元

随着AI技术向视频产业生产、传输和消费环节的渗透&#xff0c;AI技术在视频分析中的应用逐渐常态化&#xff0c;智能视频衍生而出。智能视频的多元应用重塑了视频产业链&#xff0c;视频处理技术根据不同的视频应用多维迸发&#xff0c;视频编解码技术与AI技术的结合具有共性和…

【开源】基于JAVA语言的公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…

find命令 – 根据路径和条件搜索指定文件

linux-find find命令通常进行的是从根目录&#xff08;/&#xff09;开始的全盘搜索&#xff0c;有别于whereis、which、locate等有条件或部分文件的搜索。对于服务器负载较高的情况&#xff0c;建议不要在高峰时期使用find命令的模糊搜索&#xff0c;这会相对消耗较多的系统资…

【代码随想录-数组】有序数组的平方

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

Vue学习笔记13-- Vue3之计算属性与监视

7、计算属性与监视 1.computed函数 与Vue2.x中computed配置功能一致 写法 import {computed} from vuesetup(){...//计算属性——简写let fullName computed(()>{return person.firstName - person.lastName})//计算属性——完整let fullName computed({get(){return …

【STM32】STM32学习笔记-BKP备份寄存器和RTC实时时钟(42)

00. 目录 文章目录 00. 目录01. BKP简介02. BKP特性03. BKP基本结构04. RTC简介05. RTC主要特性06. RTC框图07. RTC基本结构08. 硬件电路09. RTC操作注意事项10. 附录 01. BKP简介 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备…

JPDA框架和JDWP协议

前言 在逆向开发中,一般都需要对目标App进行代码注入。主流的代码注入工具是Frida,这个工具能稳定高效实现java代码hook和native代码hook,不过缺点是需要使用Root设备,而且用js开发,入门门槛较高。最近发现一种非Root环境下对Debug App进行代码注入的方案,原理是利用Jav…

【Java语言基础④】Java编程基础——选择结构语句,循环结构语句

选择结构语句 1.if子句 if条件语句 if语句是指如果满足某种条件&#xff0c;就进行某种处理。例如&#xff0c;小明妈妈跟小明说“如果你考试得了100分&#xff0c;星期天就带你去游乐场玩”。 if语句的具体语法如下&#xff1a; if (判断条件) { 执行语句}if…else语句 if…e…

都 2024 年了,该如何搭建新的 React 项目?

在前端技术日新月异的今天&#xff0c;React 社区已经不再将 create-react-app 作为创建新项目的首选工具&#xff0c;而是推荐使用社区中流行的由 React 驱动的框架来创建新项目。本文就来探讨在 2024 年创建 React 项目的方式及其优缺点&#xff01; Create React App 有什么…

vivado 定义和配置I/O端口、

定义和配置I/O端口 您可以使用Vivado IDE导入、创建和配置I/O端口&#xff0c;如中所述以下部分。 导入I/O端口 根据项目类型&#xff0c;可以使用以下方法导入I/O端口&#xff1a; •I/O规划项目&#xff1a;您可以将XDC和CSV文件导入空的I/O规划项目当您使用文件导入功能…

Apache Shiro 安全框架

前言 Apache Shiro 是一个强大且容易使用的Java安全矿建&#xff0c;执行身份验证&#xff0c;授权&#xff0c;密码和会话管理。使用Shiro的易于理解的API您可以快速轻松的获得任何应用程序直到大的项目。 一丶什么是Shiro 1.Shiro是什么 Apache Shiro是一个强大且易于使用…

mysql高可用设计,主库挂了怎么办

实际上高可用就是系统能提供的一种无故障服务能力&#xff0c;就是避免宕机出现不能服务的场景。 首先来说对于无状态服务的高可用设计是比较简单的&#xff0c;发现有不能用的就直接停了换别的服务器就行&#xff0c;比如Nginx。这里说一下无状态服务就是不需要记录你的状态、…

编程笔记 html5cssjs 058 css计数器

编程笔记 html5&css&js 058 css计数器 一、带计数器的自动编号二、嵌套计数器三、CSS 计数器属性练习小结 CSS 计数器是由 CSS 保持的“变量”&#xff0c;其值可以通过 CSS 规则递增&#xff08;以跟踪其使用次数&#xff09;。计数器使您可以根据内容在文档中的位置来…

防御保护---NAT实验

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一. 练习 PC4配置 FW2配置 sys int g0/0/0 ip add 192.168.100.3 24 service-manage all permit sys int l0 ip add 1.1.1.1 24 int g0/0/0 ip add 12.0.0.1 24 int g0/0/2 ip add 21.0.0.1 …

zuul网关

zuul网关 zuul自定义过滤器hystrix和ribbon时间RibbonAutoConfiguration自动配置FeignAutoConfiguration自动配置RibbonEurekaAutoConfigurationSendErrorFilter过滤器EnableZuulServerHasFeatures EnableZuulProxy zuul自定义过滤器 继承ZuulFilter类&#xff0c;实现其方法f…

Linux/Uinx 系统编程:进程管理(1)

Linux/Uinx 系统编程&#xff1a;进程管理&#xff08;1&#xff09; 文章目录 Linux/Uinx 系统编程&#xff1a;进程管理&#xff08;1&#xff09;什么是进程进程来源INIT 和 守护进程登录进程sh进程进程的执行模式进程管理的系统调用关于syscall中参数b&#xff0c;c&#x…

谷歌出品!读懂 QUIC 协议:更快、更高效的通信协议

QUIC结构 QUIC协议模型如下图所示&#xff0c;其放弃了TCP∕IP网络中使用五元组(源IP,源端口,目的IP,目的端口,协议标识符)来唯一标识一条连接的方式,而使用一个全局唯一的随机生成的ID(即Connection ID) 来标识一条连接。 由低向上分层讨论QUIC协议&#xff1a; •UDP层:在U…

MongoDB莫名崩溃的问题定位与解决纪实

MongoDB莫名崩溃的问题定位与解决纪实 国庆之前发布的软件版本一直运行正常&#xff0c;国庆之后&#xff0c;测试同事跑自动化测试脚本&#xff0c;发现该软件频繁异常&#xff0c;通过查看log发现&#xff0c;该软件使用的MongoDB崩溃了。 该软件是个Windows的桌面软件&…

【QT+QGIS跨平台编译】之十二:【libpng+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文件目录 一、libpng介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libpng介绍 PNG(Portable Network Graphics,便携式网络图形),是一种采用无损压缩算法的位图格式,支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性。 PNG使用从LZ77派生的无损数据压缩算…