当前位置: 首页 > news >正文

我要成为算法高手-二分查找篇

目录

  • 题目1:二分查找
    • 总结朴素二分模版
  • 题目2:在排序数组中查找元素的第一个和最后一个位置
    • 总结二分模版
  • 题目3:x的平方根
  • 题目4:搜索插入位置
  • 题目5:山脉数组的峰顶索引
  • 题目6:寻找峰值
  • 题目7:寻找旋转数组中的最小值
  • 题目8:点名

当数组具有二段性时,可以使用二分查找,二分查找是什么?二段性又是什么?请看题目1

题目1:二分查找

题目链接:704. 二分查找 - 力扣(LeetCode)
在这里插入图片描述

暴力解法:

从左往右依次遍历,如果找到返回结果,如果没找到返回-1

二分查找解法:利用数组有序的特点,对暴力解法进行优化,在数组中选取某个位置的值与目标值对比,对比之后能够划分出两个区间,一个区间是小于目标值的,另一个区间是大于目标值的,因此我们可以一口气筛选掉一部分的数据。那么选取哪个位置比较合适?根据概率学的理论,选取数组二分之一位置比较合适,时间复杂度最好

核心思路:

在这里插入图片描述

细节问题:循环何时结束?

当 left>right时,循环结束,left和right区间的数是没有判断的,所以当left和right同时指向一个数时(left=right),循环仍然进行。

二段性:根据规律在数组中选取某个位置之后能够把数组划分出两个区间,根据规律我们可以舍去掉一个区间,进而能在剩下的区间寻找结果。

代码实现:

class Solution {public int search(int[] nums, int target) {int left = 0, right = nums.length-1, ret = -1;while (left <= right) {// int mid = (left + right) / 2;int mid = left+ (right-left)/2;//防止溢出if (target > nums[mid]) {left = mid + 1;} else if (target < nums[mid]) {right = mid-1;} else {ret = mid;break;}}        return ret;}
}

总结朴素二分模版

while (left <= right) {//int mid = (left + right)/2;int mid = left + (right - left)/2;//防止溢出if (.....) {left = mid + 1;} else if (.....) {right = mid-1;} else {return .....;}
}

根据题目要求,根据二段性来填写(…)

还有一个版本

while (left <= right) {//int mid = (left + right + 1)/2;int mid = left + (right - left + 1)/2;//防止溢出if (.....) {left = mid + 1;} else if (.....) {right = mid-1;} else {return .....;}
}

上面两个区别是:如果数组元素个数是偶数,+1和不+1,mid位置不一样,但是对于朴素版本的模版来说,加不加1都无所谓

题目2:在排序数组中查找元素的第一个和最后一个位置

题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

在这里插入图片描述

暴力解法:

非常简单粗暴:

class Solution {public int[] searchRange(int[] nums, int target) {int[] ret = new int[]{-1,-1};if(nums==null){return ret;}for(int i=0;i<nums.length;i++) {if(nums[i]==target) {ret[0]=i;break;}}for(int i=nums.length-1;i>=0;i--) {if(nums[i]==target) {ret[1]=i;break;}}return ret;}
}

二分查找思路如图

在这里插入图片描述

代码实现:

class Solution {public int[] searchRange(int[] nums, int target) {int[] ret = new int[2];ret[0] = -1;ret[1] = -1;if(nums.length==0){return ret;}//求左端点int left = 0,right=nums.length-1;while(left<right){int mid = left+(right-left)/2;if(nums[mid]<target){left = mid+1;} else {right = mid;}}//此时,left=right//判断if(nums[left]==target){ret[0] = left;} else {//没找到return ret;}//求右端点left = 0;right = nums.length -1;while(left<right){int mid = left+(right-left+1)/2;if(nums[mid]>target){right = mid-1;} else{left = mid;}}//此时,left=rightif(nums[right]==target){ret[1] = right;} else {return ret;}return ret;}
}

总结二分模版

1、

while(left < right){int mid = left + (right - left) / 2;if(......) {left = mid +1;} else {right = mid;}
}

2、

while(left < right){int mid = left + (right - left + 1) / 2;if(......) {left = mid;} else {right = mid -1;}
}

根据题目的要求,判断left和right,接着来确定求mid时是否需要+1

题目3:x的平方根

69. x 的平方根 - 力扣(LeetCode)

在这里插入图片描述

解题思路如图:
在这里插入图片描述

根据最终返回的结果,可以将划分为2个部分,一部分是平方值小于等于x,另一部分则是大于x,如果mid落在左边部分,left = mid;如果mid落在右边部分,right = mid-1,接下来我们就直接套模版就好了

class Solution {public int mySqrt(int x) {long left = 0,right = x;while(left<right){long mid = left + (right - left+1)/2;if(mid*mid<=x){left = mid;} else {right = mid-1;}}return (int)left;}
}

题目4:搜索插入位置

35. 搜索插入位置 - 力扣(LeetCode)

在这里插入图片描述

解题思路:二段性如图,找出二段性后,根据left和right的变化直接套模版,但是需要处理一下实例3的情况,如果target都大于数组中最后一个元素,直接返回数组长度即可
在这里插入图片描述

class Solution {public int searchInsert(int[] nums, int target) {if (target > nums[nums.length - 1]) {return nums.length;}int left = 0;int right = nums.length - 1;while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] < target) {left = mid + 1;} else {right = mid;}}return left;}
}

题目5:山脉数组的峰顶索引

852. 山脉数组的峰顶索引 - 力扣(LeetCode)
在这里插入图片描述

解题思路:
在这里插入图片描述

代码实现:

class Solution {public int peakIndexInMountainArray(int[] arr) {int left = 1;int right = arr.length - 2;while (left < right) {int mid = left + (right - left + 1) / 2;if (arr[mid] > arr[mid - 1]) {left = mid;} else {right = mid - 1;}}return left;}
}

题目6:寻找峰值

162. 寻找峰值 - 力扣(LeetCode)

在这里插入图片描述

二分查找:二段性如图所示

在这里插入图片描述

代码实现:

class Solution {public int findPeakElement(int[] nums) {int left = 0;int right = nums.length -1;while(left<right) {int mid = left + (right - left)/2;if(nums[mid]>nums[mid+1]) {right = mid;} else {left = mid+1;}}return left;}
}

题目7:寻找旋转数组中的最小值

153. 寻找旋转排序数组中的最小值

在这里插入图片描述

解题思路:

题目要求的就是数组中的最小值

在这里插入图片描述

代码实现:

class Solution {public int findMin(int[] nums) {int left=0;int right =nums.length-1;while(left<right){int  mid = left + (right-left)/2;if(nums[mid]<nums[right]){right=mid;} else {left =mid +1;}}return nums[left];}
}

题目8:点名

LCR 173. 点名 - 力扣(LeetCode)

在这里插入图片描述

解题思路:

在这里插入图片描述

代码实现:

class Solution {public int takeAttendance(int[] records) {if (records.length == 1) {if (records[0] == 0) {return 1;}return records[0] - 1;}int left = 0;int right = records.length - 1;while (left < right) {int mid = left + (right - left) / 2;if (mid == records[mid]) {left = mid + 1;} else {right = mid;}}if (records[left] == left) {return left + 1;}return left;}
}

http://www.mrgr.cn/news/74811.html

相关文章:

  • 接口类和抽象类在设计模式中的一些应用
  • C# Winform--SerialPort串口通讯(ASCII码发送)
  • Sam Altman:年底将有重磅更新,但不是GPT-5!
  • 【计算机网络】TCP网络程序
  • 【分布式】BASE理论
  • 深圳华为展厅:30寸OLED透明屏中控桌引领科技新风尚
  • 【操作系统】Linux之线程同步二(头歌作业)
  • 前端开发设计模式——责任链模式
  • 在Windows上收发PGP加密电子邮件
  • React Hooks 快速入门指南
  • 介绍一下,Stable Diffusion!文生图的稳定之选
  • asp.net framework下webform、webapi和mvc对于文件增加权限校验
  • Leetcode 整数转罗马数字
  • error: unrecognized arguments: --port
  • 新能源汽车关键技术技能大赛理论知识竞赛题库
  • 一文简单了解Android中的input流程
  • ospf排错学习
  • 分清数据链路层、网络层、传输层的区别,以及这些层面的代表协议
  • 计算机文件msvcp100.dll丢失原因以及5种解决方法详解分享
  • macOS系统下使用SQLark连接达梦数据库
  • 探索大型语言模型(LLMs)能否在不泄露私人信息的情况下联合其他大型语言模型共同解决问题
  • 从前端react动画引发到计算机底层的思考
  • 【图像压缩感知】论文阅读:Self-supervised Scalable Deep Compressed Sensing
  • Process finished with exit code 137 (interrupted by signal 9: SIGKILL)
  • 【Bluedroid】A2dp初始化流程源码分析
  • 重学 Android 自定义 View 系列(六):环形进度条