前言
之前小六六一直觉得自己的算法比较菜,算是一个短板吧,以前刷题也还真是三天打鱼,两天晒网,刷几天,然后就慢慢的不坚持了,所以这次,借助平台的活动,打算慢慢的开始开刷,并且自己还会给刷的题总结下,谈谈自己的一些思考,和自己的思路等等,希望对小伙伴能有所帮助吧,也可以借此机会把自己短板补一补,希望自己能坚持下去呀
数组的文章合集
题目
给定一个 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]之间。
思路
这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。
二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 while(left < right)
还是 while(left <= right)
,到底是right = middle
呢,还是要right = middle - 1
呢?
大家写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
二分法的主流套路
二分法的写法共分两种分别是:1.定义target在左闭右闭区间 2.定义target在左闭右开区间。 方法1. 定义target在左闭右闭区间,即[left, right] 1.while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=。 2.if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle – 1。
方法2. 定义target在左闭右开区间,即[left, right) 1.while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的。 2.if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]。
反正大家记住 如果是左闭右闭,那么我们就是 middle - 1, 如果是左闭右开 我们就是mmiddle
class Solution { public int search(int[] nums, int target) { int left = 0; int right = nums.length-1; while (left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid]>target){ right=mid-1; }else if (nums[mid]==target){ return mid; }else { left=mid+1; } } return -1; } }
结束
今天二分法就刷完了,大家一定要记住,如果 left <= right,那么 return的时候一定是 left or right+1
然后就是 当 重新赋值mid的时候 一定是mid+1 或者mid-1,好了我是小六六,三天打鱼,两天晒网!