赞
踩
“代码随想录”刷题记录。总结笔记均会放在“算法刷题-代码随想录”该专栏下,以下为原文的链接。
二分查找卡尔题解
给定一个 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]之间。
当题目中包含数据结构为“数组”(比如链表就不可以使用二分查找,因为二分查找只能内存地址连续的数据结构中进行),并且该“数组为有序数组”以及“数组中无重复元素”这三个前提条件,很有可能可以使用“二分查找”。
思路:
//左闭右闭 right = nums.length - 1, while的条件为 left <= right class Solution { public int search(int[] nums, int target) { //左闭右闭的写法 int left = 0,right = nums.length - 1,mid = 0; while(left <= right){ mid = left + ((right - left) >> 1);//防止溢出 if(target > nums[mid]){ left = mid + 1; }else if(target < nums[mid]){ right = mid - 1; }else{ return mid; } } return -1; } }
C++版本
#include<stdio.h> #include<vector> using namespace std; int binarySearch(vector<int> nums,int target){ int left = 0,right = nums.size() - 1,mid = 0; while(left <= right){ mid = left + (right - left) / 2;//right肯定大于left,故right - left不会导致溢出Integer的最大值;并且整个表达式等同于 (right + left)/ 2,可以自己试着将两者转换 if(target < nums[mid]){ right = mid- 1; }else if(target > nums[mid]){ left = mid + 1; }else{ return mid; } } return -1; }
n为数组长度
时间复杂度:O(logn)
空间复杂度:O(1),只增加了left、right、mid,这3个变量。(算法中的nums虽然空间大小是n,但是在计算空间复杂度时只计算额外增加的空间大小)
区间可以选择,左闭右闭或者左闭右开
中间值mid最好写成mid = l + (r - l) / 2,而不是容易想到的mid= (l + r) / 2;
因为l + r的值可能超过int的最大范围而造成溢出。
为什么二分法不建议使用 (right + left) / 2?相关链接
什么叫做“循环不变量”
在每次循环中都满足的条件,在写循环时要清楚循环中的循环不变量是什么。
举例
二分法中的循环不变量是,每次循环时查找target的额区间的区间边界是开还是闭
C++中右移一位(>> 1),在符号的运算中相当于除以2,且右移一位的速度快于/2的速度
并且注意
右移运算的优先级在所有操作符中是最低的,所以如果右移操作在最后,则需要打上括号。
C++位右移是什么
轻松理解 left + ((right -left) >> 1)
>>是算术右移(需要考虑符号位,正数左边补0,负数左边补1),>>>是逻辑右移(不需要考虑符号位,左边统一补0)
链接是右移的例子,位移运算时数为它的补码形式。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。