0


【数据结构与算法】二分查找算法

​🌠 作者:@阿亮joy.
🎆专栏:《数据结构与算法要啸着学》
🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述

活动地址:CSDN21天学习挑战赛

目录

👉前言👈

相信大家之前已经过学习二分查找算法了,也知道二分查找算法使用的前提:严格有序的数组。那是不是永远都需要满足这个前提才能使用二分查找算法呢?本文将给出一些二分查找算法类型的题目,与你一探究竟。

👉二分查找👈

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

示例 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

提示:

  • 你可以假设 nums 中的所有元素是不重复的。
  • n 将在 [1, 10000]之间。
  • nums 的每个元素都将在 [-9999, 9999]之间。

以上就是二分查找的母体了,这是最简单、最基础的。现在我们来写代码实现它。
在这里插入图片描述

intsearch(int* nums,int numsSize,int target){int left =0;int right = numsSize -1;while(left <= right){int mid = left +(right - right)/2;if(nums[mid]> target){
            right = mid -1;}elseif(nums[mid]< target){
            left = mid +1;}elsereturn mid;}return-1;}

在这里插入图片描述
分析:二分查找算法有左闭右闭区间和左闭右开区间两种写法,博主的二分查找算法的写法均采取的左闭右闭区间的写法。采用左闭右闭区间写法时,当 nums[mid] > target 时,right = mid - 1;当 nums[mid] < target 时,left = mid + 1;当nums[mid] == target 时,直接 return mid。还需要注意的是 mid 要定义在 while 循环内部。

👉猜数字大小👈

猜数字游戏的规则如下:

  • 每轮游戏,我都会从 1 到 n 随机选择一个数字。 请你猜选出的是哪个数字。
  • 如果你猜错了,我会告诉你,你猜测的数字比我选出的数字是大了还是小了。

你可以通过调用一个预先定义好的接口 int guess(int num) 来获取猜测结果,返回值一共有 3 种可能的情况(-1,1 或 0):

  • -1:我选出的数字比你猜的数字小 pick < num
  • 1:我选出的数字比你猜的数字大 pick > num
  • 0:我选出的数字和你猜的数字一样。恭喜!你猜对了!pick == num

示例 1:

输入:

n = 10, pick = 6 

输出:

6 

示例 2:

输入:

n = 1, pick = 1

输出:

1

提示:

  • 1 <= n <= 2^31 - 1
  • 1 <= pick <= n
intguessNumber(int n){int left=1;int right=n;int mid=0;while(left<=right){
          mid=left+(right-left)/2;if(guess(mid)==1){
              left=mid+1;}elseif(guess(mid)==-1){
              right=mid-1;}elsereturn mid;}return mid;}

在这里插入图片描述

分析:本题最需要注意的就是,接口函数 guess 返回值的意思,其他的写法都是按照二分查找算法的思路写就行了。

👉搜索插入位置👈

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

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

示例 1:
输入:

nums = [1,3,5,6], target = 5

输出:

2

示例 2:
输入:

nums = [1,3,5,6], target = 2

输出:

1

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排列数组
  • -104 <= target <= 104

在这里插入图片描述

intsearchInsert(int* nums,int numsSize,int target){int left =0;int right = numsSize -1;if(nums[right]< target)return numsSize;if(nums[0]> target)return0;while(left <= right){int mid = left +(right - left)/2;if(nums[mid]< target){

            left = mid +1;}elseif(nums[mid]> target){
            right = mid -1;}elsereturn mid;}return left;}

在这里插入图片描述

分析:当数组 nums 的最后一个元素小于 target 时,那么插入位置就是 numsSize;当数组 nums 的第一个元素大于 target 时,那么插入位置就是0。如果不符合上面两种情况的话,就会进入 while 循环。如果数组 nums 中包含了target,那么二分查找就会找到其下标 mid,也就是插入位置,将插入位置 return 就行了。如果数组 nums 不包含target,那么 left 迟早会大于 right 退出 while 循环,此时 left 就是插入位置,将其 return 就行了。

👉山脉数组的峰顶索引👈

符合下列属性的数组 arr 称为 山脉数组 :

  • arr.length >= 3
  • 存在 i(0 < i < arr.length - 1)使得: - arr[0] < arr[1] < … arr[i-1] < arr[i]- arr[i] > arr[i+1] > … > arr[arr.length - 1]

给你由整数组成的山脉数组 arr ,返回任何满足 arr[0] < arr[1] < … arr[i - 1] < arr[i] > arr[i + 1] > … > arr[arr.length - 1] 的下标 i 。

示例 1:
输入:

arr = [0,1,0]

输出:

1

示例 2:
输入:

arr = [0,2,1,0]

输出:

1

提示:

  • 3 <= arr.length <= 104
  • 0 <= arr[i] <= 106
  • 题目数据保证 arr 是一个山脉数组
intpeakIndexInMountainArray(int* arr,int arrSize){int left =1, right = arrSize -2;int mid =0;while(left <= right){
        mid = left +(right - left)/2;if(arr[mid]< arr[mid +1]){
            left = mid +1;}elseif(arr[mid]< arr[mid -1]){
            right = mid -1;}elsereturn mid;}return mid;}

在这里插入图片描述
分析:该题的二分查找算法和上面的题差不多,当时唯一不同的就是 left 的起始位置是0,right的起始位置是 numsSize - 1,这样初始化的目的是避免数组越界访问。

👉有效的完全平方数👈

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

进阶:不要 使用任何内置的库函数,如 sqrt 。

示例 1:
输入:

num = 16

输出:

true

示例 2:
输入:

num = 14

输出:

false

提示:

  • 1 <= num <= 2^31 - 1
bool isPerfectSquare(int num){int left=0;int right=num;while(left<=right){int mid=left+(right-left)/2;long square=(long)mid*mid;//将mid*mid强制类型转换为long,防止溢出if(square>num){
            right=mid-1;}elseif(square<num){
            left=mid+1;}elsereturn true;}return false;}

在这里插入图片描述
分析:当不满足循环条件时,说明1到 num 之间没有任何一个数的平方等于 num,所以 return false。

👉x 的平方根👈

给你一个非负整数 x ,计算并返回 x 的算术平方根 。

由于返回类型是整数,结果只保留整数部分 ,小数部分将被舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例 1:

输入:

x = 4

输出:

2

示例 2:

输入:

x = 8

输出:

2

解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。

提示:

  • 0 <= x <= 231 - 1
intmySqrt(int x){int left =0;int right = x;while(left <= right){int mid = left +(right - left)/2;long  n =(long)mid * mid;//强制类型转换,防止溢出if(n > x){
            right = mid -1;}elseif(n < x){
            left = mid +1;}elsereturn mid;}return right;}

在这里插入图片描述

分析: 因为退出循环是 left > right,而 left * left 是大于 x 的,right * right是小于 x 的,所以return right。

👉寻找比目标字母大的最小字母👈

给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。

在比较时,字母是依序循环出现的。举个例子:

  • 如果目标字母 target = ‘z’ 并且字符列表为 letters = [‘a’, ‘b’],则答案返回 ‘a’

示例 1:

输入:

letters = ["c", "f", "j"],target = "a"

输出:

"c"

示例 2:

输入:

letters = ["c","f","j"], target = "c"

输出:

"f"

提示:

  • 2 <= letters.length <= 104
  • letters[i] 是一个小写字母
  • letters 按非递减顺序排序
  • letters最少包含两个不同的字母
  • target 是一个小写字母
charnextGreatestLetter(char* letters,int lettersSize,char target){if(target>=letters[lettersSize-1])return letters[0];int left=0;int right=lettersSize-1;while(left<=right){int mid=left+(right-left)/2;if(letters[mid]>target){
             right=mid-1;}else{
             left=mid+1;}}return letters[left];}

在这里插入图片描述

👉总结👈

以上就是二分查找算法的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家啦!!!💖💝❣️


本文转载自: https://blog.csdn.net/m0_63639164/article/details/126214098
版权归原作者 阿亮joy. 所有, 如有侵权,请联系我们删除。

“【数据结构与算法】二分查找算法”的评论:

还没有评论