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

算法<C++>——二分查找

一、二分的基本概念

二分查找,也称为折半查找(BinarySearch),是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将目标值与数组中间的元素进行比较,如果目标值小于中间元素,则在数组的左半部分继续查找,否则在右半部分查找,不断缩小搜索范围,直到找到目标值或确定目标值不存在为止。二分查找的时间复杂度为O(logn),即查找效率较高。

二、代码实现

1.闭区间写法:

class Solution {// lower_bound 返回最小的满足 nums[i] >= target 的下标 i// 如果数组为空,或者所有数都 < target,则返回 nums.size()// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]int lower_bound(vector<int>& nums, int target) {int left = 0, right = (int) nums.size() - 1; // 闭区间 [left, right]while (left <= right) { // 区间不为空// 循环不变量:// nums[left-1] < target// nums[right+1] >= targetint mid = left + (right - left) / 2;if (nums[mid] >= target) {right = mid - 1; // 范围缩小到 [left, mid-1]} else {left = mid + 1; // 范围缩小到 [mid+1, right]}}// 循环结束后 left = right+1// 此时 nums[left-1] < target 而 nums[left] = nums[right+1] >= target// 所以 left 就是第一个 >= target 的元素下标return left;}

2.左闭右开区间写法

class Solution {// lower_bound 返回最小的满足 nums[i] >= target 的下标 i// 如果数组为空,或者所有数都 < target,则返回 nums.size()// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]int lower_bound(vector<int>& nums, int target) {int left = 0, right = nums.size(); // 左闭右开区间 [left, right)while (left < right) { // 区间不为空// 循环不变量:// nums[left-1] < target// nums[right] >= targetint mid = left + (right - left) / 2;if (nums[mid] >= target) {right = mid; // 范围缩小到 [left, mid)} else {left = mid + 1; // 范围缩小到 [mid+1, right)}}// 循环结束后 left = right// 此时 nums[left-1] < target 而 nums[left] = nums[right] >= target// 所以 left 就是第一个 >= target 的元素下标return left;}

3.开区间写法

class Solution {// lower_bound 返回最小的满足 nums[i] >= target 的下标 i// 如果数组为空,或者所有数都 < target,则返回 nums.size()// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]int lower_bound(vector<int>& nums, int target) {int left = -1, right = nums.size(); // 开区间 (left, right)while (left + 1 < right) { // 区间不为空// 循环不变量:// nums[left] < target// nums[right] >= targetint mid = left + (right - left) / 2;if (nums[mid] >= target) {right = mid; // 范围缩小到 (left, mid)} else {left = mid; // 范围缩小到 (mid, right)}}// 循环结束后 left+1 = right// 此时 nums[left] < target 而 nums[right] >= target// 所以 right 就是第一个 >= target 的元素下标return right;}//写法二
class Solution {// lower_bound 返回最小的满足 nums[i] >= target 的下标 i// 如果数组为空,或者所有数都 < target,则返回 nums.size()// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]int lower_bound(vector<int>& nums, int target) {int left = -1, right = nums.size(); // 开区间 (left, right)while (left + 1 < right) { // 区间不为空// 循环不变量:// nums[left] < target// nums[right] >= targetint mid = left + (right - left) / 2;(nums[mid] >= target ? right : left) = mid; // 注:只有开区间二分可以这样写}// 循环结束后 left+1 = right// 此时 nums[left] < target 而 nums[right] >= target// 所以 right 就是第一个 >= target 的元素下标return right;}

三、总结题型

需求写法如果不存在
≥x 的第一个元素的下标lowerBound(nums,x)结果为 n
>x 的第一个元素的下标lowerBound(nums,x+1)结果为 n
<x 的最后一个元素的下标lowerBound(nums,x)−1结果为 −1
≤x 的最后一个元素的下标lowerBound(nums,x+1)−1结果为 −1

四、例题巩固

34. 在排序数组中查找元素的第一个和最后一个位置 | 力扣 | LeetCode | (medium)

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

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

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

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

按照题意:
找出第一个大于等于target索引和第一个小于等于target的索引,对照表格,直接写出答案

书写一下lower_bound函数:

 int lower_bound(vector<int> &nums,int target){int n=nums.size();int l=-1,r=n;while(l+1<r){int mid=l+(r-l)/2;if(nums[mid]>=target){r=mid; }else{l=mid;  }}return r;}

根据提议,正确的调用函数:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {//start求得是大于等于target的第一个数int start = lower_bound(nums,target);if(start==nums.size()||nums[start]!=target){return {-1,-1};}//end求得是大于tag的左边的数int end=lower_bound(nums,target+1)-1;return {start,end};}int lower_bound(vector<int> &nums,int target){int n=nums.size();int l=-1,r=n;while(l+1<r){int mid=l+(r-l)/2;if(nums[mid]>=target){r=mid; }else{l=mid;  }}return r;}
};

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

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

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

输入: nums = [1,3,5,6], target = 5
输出: 2

示例2:

输入: nums = [1,3,5,6], target = 2
输出: 1

按照题意,找到第一个大于等于target的索引,没找到就是大于的索引,直接套用模板

闭区间实现:

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

704. 二分查找 | 力扣 | LeetCode | (easy)

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果
target 存在返回下标,否则返回 -1。

你必须编写一个具有 O(log n) 时间复杂度的算法。
示例1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
class Solution {
public:int search(vector<int>& nums, int target) {int n = nums.size();int l = -1, r = n;while (l + 1 < r) {int mid = l + (r - l) / 2;if (nums[mid] >= target) {r = mid;} else {l = mid;}}if (r < n && nums[r] == target)return r;return -1;}
};

744. 寻找比目标字母大的最小字母 | 力扣 | LeetCode | (easy)

给你一个字符数组 letters,该数组按非递减顺序排序,以及一个字符 target。letters 里至少有两个不同的字符。

返回 letters 中大于 target 的最小的字符。如果不存在这样的字符,则返回 letters 的第一个字符.

示例1:

输入: letters = ['c', 'f', 'j'],target = 'a'
输出: 'c'
解释:letters 中字典上比 'a' 大的最小字符是 'c'

示例2:

输入: letters = ['c','f','j'], target = 'c'
输出: 'f'
解释:letters 中字典顺序上大于 'c' 的最小字符是 'f'

按照题意,查找第一个大于target的元素值

第一种代码:

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

第二种:

class Solution {
public:char nextGreatestLetter(vector<char>& letters, char target) {int n=letters.size();int l=-1,r=n;while(l+1<r){int mid=l+(r-l)/2;if(letters[mid]<=target){l=mid;}else{r=mid;}}return l==n-1?letters[0]:letters[l+1];//或者是把l+1换成r}  
};

2529. 正整数和负整数的最大计数 | 力扣 | LeetCode | (easy)

给你一个按 非递减顺序 排列的数组 nums ,返回正整数数目和负整数数目中的最大值。

换句话讲,如果 nums 中正整数的数目是 pos ,而负整数的数目是 neg ,返回 pos 和 neg二者中的最大值。 注意:0
既不是正整数也不是负整数。

示例1:

输入:nums = [-2,-1,-1,1,2,3]
输出:3
解释:共有 3 个正整数和 3 个负整数。计数得到的最大值是 3

示例2:

输入:nums = [-3,-2,-1,0,0,1,2]
输出:3
解释:共有 2 个正整数和 3 个负整数。计数得到的最大值是 3

方法一(遍历):

class Solution {
public:int maximumCount(vector<int>& nums) {int n1=0,n2=0;for(int num:nums){if(num<0) n1++;if(num>0) n2++;}return max(n1,n2);}
};

方法二(二分):

class Solution {
public:int maximumCount(vector<int>& nums) {int n=nums.size();int n1=lower_bound(nums,0);int n2=lower_bound(nums,-1);return max(n-n1,n2);}int lower_bound(vector<int> &nums,int target){int l=-1,r=nums.size();while(l+1<r){int mid=l+(r-l)/2;(nums[mid]>target?r:l)=mid;}return r;}
};

1170.比较字符串中最小字母的出现频次 | 力扣 | LeetCode | (medium)

定义一个函数 f(s),统计 s 中(按字典序比较)最小字母的出现频次 ,其中 s 是一个非空字符串。

例如,若 s = “dcce”,那么 f(s) = 2,因为字典序最小字母是 “c”,它出现了 2 次。

现在,给你两个字符串数组待查表 queries 和词汇表 words 。对于每次查询 queries[i] ,需统计 words 中满足
f(queries[i]) < f(W) 的 词的数目 ,W 表示词汇表 words 中的每个词。

请你返回一个整数数组 answer 作为答案,其中每个 answer[i] 是第 i 次查询的结果
示例1:

输入:queries = ["cbd"], words = ["zaaaz"]
输出:[1]
解释:查询 f("cbd") = 1,而 f("zaaaz") = 3 所以 f("cbd") < f("zaaaz")

示例2:

输入:queries = ["bbb","cc"], words = ["a","aa","aaa","aaaa"]
输出:[1,2]
解释:第一个查询 f("bbb") < f("aaaa"),第二个查询 f("aaa")f("aaaa")> f("cc")
class Solution {
public:int my_lower_bound(vector<int>& nums, int target) {int left = -1;int right = nums.size();while (left + 1 < right) {int mid = left + (right - left)/2;if (nums[mid]<target) {left = mid;}else {right = mid;}}return right;}int get_num(const string& str) {string s = str;sort(s.begin(), s.end());char min_char = s[0];int count = 0;for (char c : s) {if (c == min_char) {count++;} else {break;}}return count;}vector<int> numSmallerByFrequency(vector<string>& queries, vector<string>& words) {int n = queries.size();int m = words.size();vector<int> nums(m);vector<int> ans(n);for (int i = 0; i < m; i++) {nums[i] = get_num(words[i]);}sort(nums.begin(), nums.end());for (int i = 0; i < n; i++) {ans[i] = m - my_lower_bound(nums, get_num(queries[i]) + 1);}return ans;}
};

首先进行最小字母的统计,返回count,然后找到第一个大于count的答案。

1385. 两个数组间的距离值 | 力扣 | LeetCode | (easy)

给你两个整数数组 arr1 , arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。

「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素 arr2[j] 满足
|arr1[i]-arr2[j]| <= d

示例1:

输入:arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2
输出:2
解释:
对于 arr1[0]=4 我们有:
|4-10|=6 > d=2 
|4-9|=5 > d=2 
|4-1|=3 > d=2 
|4-8|=4 > d=2 
所以 arr1[0]=4 符合距离要求对于 arr1[1]=5 我们有:
|5-10|=5 > d=2 
|5-9|=4 > d=2 
|5-1|=4 > d=2 
|5-8|=3 > d=2
所以 arr1[1]=5 也符合距离要求对于 arr1[2]=8 我们有:
|8-10|=2 <= d=2
|8-9|=1 <= d=2
|8-1|=7 > d=2
|8-8|=0 <= d=2
存在距离小于等于 2 的情况,不符合距离要求 故而只有 arr1[0]=4 和 arr1[1]=5 两个符合距离要求,距离值为 2

示例2:

输入:arr1 = [1,4,2,3], arr2 = [-4,-3,6,10,20,30], d = 3
输出:2

按照题意进行先排序两个数组:

class Solution {
public:int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {ranges::sort(arr1);ranges::sort(arr2);int l=0,r=0;for(int x:arr1){while(r<arr2.size()&&arr2[r]<x-d){r++;}if(r==arr2.size()||arr2[r]>x+d){l++;}}return l;}
};
http://www.dtcms.com/a/565725.html

相关文章:

  • MIDI协议与Arduino编程
  • 【开题答辩全过程】以 儿童口腔诊所私域管理系统为例,包含答辩的问题和答案
  • 什么网站做app好网站建设的后如何发布
  • 从零开始的Qt开发指南:(二)使用Qt Creator构建项目与Qt底层机制的深度解析
  • UVa 1326 Jurassic Remains
  • Readest(电子书阅读器) v0.9.91
  • Flink 优化-数据倾斜
  • 遵义网站网站建设江阴便宜做网站
  • 大模型RLHF:PPO原理与源码解读
  • Mojo变量知识点解读
  • Linux之rsyslog(2)输入输出配置
  • 整体设计 全面梳理复盘 之17 三套表制表的支持和支撑以及编程基础 之2
  • 凯文·凯利《2049:未来10000天的可能》
  • 网站百度建设高端网站设计百家号
  • ctypes.pythonapi.PyThreadState_SetAsyncExc作用详解
  • pyside6常用控件: QPushButton()按钮切换、带图片的按钮
  • Python逻辑运算符
  • MinGW下载、安装和使用教程(附安装包,适合新手)
  • lol做任务领头像网站微商城网站建设平台
  • 百日挑战——单词篇(第十二天)
  • (单调队列、ST 表)洛谷 P2216 HAOI2007 理想的正方形 / P2219 HAOI2007 修筑绿化带
  • Spark RDD 编程从驱动程序到共享变量、Shuffle 与持久化
  • 网站 面包屑网站开发工作流审批流
  • 网站建设广金手指六六十四在线建站系统
  • 排序还有分页
  • electron对于图片/视频无法加载的问题
  • TDengine 字符串函数 CHAR 用户手册
  • 股票信息收集系统设计
  • 深圳网站建设 设计首选公司红色扁平化网站
  • 深度学习PINN!从入门到精通!