欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > 二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板

二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板

2024/11/9 4:17:07 来源:https://blog.csdn.net/wer24_25/article/details/142442272  浏览:    关键词:二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

二分查找算法(2) _在排序数组中查找元素的第一个和最后一个_模板

收录于专栏【经典算法练习】
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 题目链接:

2. 题目描述 :

3. 解法 :

    解法一:暴力枚举遍历:

    算法思路 :

    代码展示 :

    结果分析 :

    解法二: 二分查找 :

    算法思路 :

    细节处理:

查找区间的左端点

1. 循环条件

2. 求终点的操作 

 查找区间的右端点 

1. 循环条件

2. 求终点的操作 

    代码展示 :

    结果分析 :

4. 二分算法模板总结


1. 题目链接:

OJ链接: 在排序数组中查找元素的第一个和最后一个

2. 题目描述 :

给你一个按照非递减顺序排列的整数数组 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]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

3. 解法 :

    解法一:暴力枚举遍历:

    算法思路 :

使用两个头尾指针遍历整个数组,在排序数组中查找元素的第一个和最后一个

    代码展示 :

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

 

    结果分析 :

题目顺利通过!时间复杂度为: O(N),不过算法还有进一步优化的空间.

    解法二: 二分查找 :

    算法思路 :

a.分析插⼊位置左右两侧区间上元素的特点:
设插⼊位置的坐标为 index ,根据插⼊位置的特点可以知道:
        •[left, index - 1] 内的所有元素均是⼩于 target 的;
        •[index, right] 内的所有元素均是⼤于等于 target 的。

b.设 left 为本轮查询的左边界, right 为本轮查询的右边界。根据 mid 位置元素的信
息,分析下⼀轮查询的区间:
        ▪ 当 nums[mid] >= target 时,说明 mid 落在了[index, right] 区间上,
        mid 左边包括 mid 本⾝,可能是最终结果,所以我们接下来查找的区间在[left,
        mid] 上。因此,更新 right 到 mid 位置,继续查找。
        ▪ 当 nums[mid] < target 时,说明 mid 落在了[left, index - 1] 区间上,
       mid 右边但不包括 mid 本⾝,可能是最终结果,所以我们接下来查找的区间在[mid
        + 1, right] 上。因此,更新 left 到 mid + 1 的位置,继续查找。

c.直到我们的查找区间的⻓度变为 1 ,也就是 left == right 的时候, left 或者
    right 所在的位置就是我们要找的结果。

总结:

查找区间左端点 --- [..., target) [target, ...]

1. nums[mid] < t left  = mid + 1

2. nums[mid] >= t right = mid

 

 查找区间右端点 --- [..., target] (target, ...]

1. nums[mid] <= t left  = mid

2. nums[mid] > t right = mid - 1

 

    细节处理:

查找区间的左端点
1. 循环条件

while(left < right)

注意这里不能使用while(left <= right) 

1. 有结果: 当我们的left == right时,我们的left即是我们的结果,但如果你是使用while(left <= right) 循环,程序会陷入死循环,因为此时right + left / 2 = right/left

2. 全部大于target: 那我们的right会一直往左走,直到left与right相遇,这时候我们只需要判断它们相遇点的值是否等于target就行,相反使用while(left <= right)循环,程序依旧陷入死循环

3. 全部小于target: 那我们的left会一直往右走,与情况2是一样的

2. 求终点的操作 

left + (right - left) / 2

注意这里不能使用left + (right - left + 1) / 2

当我们判断只剩下两个元素时,left指向第一个元素, right指向第二个元素

1. mid = left + (right - left) / 2 = left

2. mid = left + (right - left + 1) / 2 = right

        当nums[mid]  < target

                1: left = mid + 1 =right 循环成功结束

                2: left = mid + 1 = right + 1成功错过

        当nums[mid] >= target 

                1: right = mid right == left 循环成功结束

                2: right = mid 死循环

 查找区间的右端点 
1. 循环条件

while(left < right)

 原理和查找区间的左端点的一样

2. 求终点的操作 

left + (right - left + 1) / 2 

注意这里不能使用left + (right - left) / 2

当我们判断只剩下两个元素时,left指向第一个元素, right指向第二个元素

1. mid = left + (right - left) / 2 = left

2. mid = left + (right - left + 1) / 2 = right

        当nums[mid]  <= target

                1: left = mid  死循环

                2: left = mid left== right + 1 循环成功结束

        当nums[mid] > target 

                1: right = mid - 1 right == left - 1循环成功错过

                2: right = mid - 1 right == left 循环成功结束

    代码展示 :

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

 

    结果分析 :

这里一定要注意细节问题,不然程序很容易死循环

4. 二分算法模板总结

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

快速记忆:

分类讨论的代码,就题论题即可

下面出现-1,上面就+1,否侧不加 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com