基本算法之二分查找

查找方式有哪些,效率如何

 	不同的数据结构有不同的查找效率。
 		1.底层数据结构是以数组实现的,那么其查找数组下标效率为O(1),查找特定值得效率为O(n)
 		2.底层数据结构是以链表实现的,那么其查找迭代器(类似于数组查询下标)效率为O(n),查找特定值得效率为O(n)
		3.底层数据结构是以哈希表实现的,那么其查找迭代器和特定值效率均为O(1)
	当然,以上查找效率只是针对于简单的数据结构而言,优化过的数据结构暂且不谈。
	二分查找的目的就是在数组查询存储的特定目标值(target)能够提升其查询效率。
	二分查找的时间复杂度为:O(logn) 空间复杂度为:O(1)

二分查找的前提条件

当然对于无序的数组,想要提高查询target的效率是无法做到的。
二分查找需要满足的前提条件是:数组有序排列(升序或者降序)(严格递增或递增均可),升序和降序情况类似。一下均已升序情况讨论

二分查找的形式以及代码实现

三种实现形式:
	1.简单查询target值,target位置不唯一。就是说查询一个有序数组中是否存在target值,可能数组中存在多个target。(C++标准库中已经实现 binary_search())
	2.查询第一个target值出现的位置,即左边界。就是查询有序数组中第一个出现的target的索引。(C++标准库中的实现 lower_bound())
	3.查询最后一个target值出现的位置,即右边界。就是查询有序数组中最后一个target出现的索引。(C++标准库中的实现 upper_bound())
//情况一:查询是否存在target,返回target值得索引(从0开始,索引不唯一)
int binary_search(vector<int>& nums,int target){
        // 查找有序数组中是否存在target,有输出target位置(不唯一),没有输出-1;	
        int left = 0,right = nums.size() - 1;  // 此时查找范围是[0,nums.size() - 1];
        while (left <= right){                 // 根据查找范围,右边可以取到所有加等号
            int middle = left + ((right - left) >> 1);
            if (nums[middle] == target) return left;
            else if (nums[middle] < target) left = middle + 1;
            else if (nums[middle] > target) right = middle - 1; // 当前middle已经取过,下一次查找范围应该不能包括middle;
        }
        return -1;
}
//情况二:查找有序数组target值的左边界,返回索引
int searchLeftRange(vector<int>& nums, int target) {
        // 二分查找寻找左边界
        // 类似于lower_bound();
        // 找到左侧第一个大于等于target的数的下标
        // 注意left的值,nums[left]是备选值(等于target则选,否则不能选)
        int left = 0,right = nums.size();                // 查找范围[0,nums.size());左闭右开
        while (left < right){
            auto middle = left + ((right - left) >> 1);
            if (nums[middle] >= target) right = middle; // 二分查找,当中间值大于等于目标值时,左移右边界。
                                                        // 为什么right = middle 而不是 right = middle - 1?
                                                        //原因是每次查找范围是左闭右开,而nums[middle]每次都查询过,下一次查询时应该不能取到
            else left = middle + 1;
        }
        if (left == nums.size()) return -1;           // left取值到达最右边,无法取到,没有左边界
        return nums[left] == target ? left : -1; // left合法时,还需要判断nums[left]是否等于target
}
//情况三:寻找有序数组target的右边界,返回索引
int searchRightRange(vector<int>& nums, int target) {
        // 二分查找寻找右边界
        // 类似于upper_bound();
        // 寻找第一个大于target的值
        // nums[left - 1] 是备选值 nums[left - 1] == target 则选,否则不选
        left = 0,right = nums.size();
        while (left < right){
            auto middle = left + ((right - left) >> 1);
            if (nums[middle] <= target) left = middle + 1;
            else right = middle;
        }
        if (left == 0) return -1;
        return nums[left - 1] == target ? (left - 1) : -1;
}

后记

	值得注意的点:搜索区间的选取(左闭右开,左闭右闭)、更新时右边值是否能取到(right = middle - 1 or right = middle)、区间能不存在target时最后的判断条件。
	二分查找是一个高频的算法技巧,在算法实现中常常用来优化算法的查找速度,尤其对于数据规模较大的情况下效果尤为明显。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值