用排除法解二分查找:
1、循环条件:l<r
2、用排除法,考虑什么情况下mid一定不满足条件,然后考虑r或者l的取值,判断依据是左边区间有没有可能出现目标元素,右边条件有没有可能出现目标元素。
3、mid=l+(r-l)/2,但是出现left=mid的情况,为了避免死循话,要写成mid=l+(r-l+1)/2
4、如果数组中不一定会出现目标元素,还应该再判断一次。
1、找一个准确值
- 循环条件:l<=r
- 缩减搜索空间:l=mid+1,r=mid-1
2、找一个数第一次出现的位置,最后一次出现的位置
第一次出现的位置:12223
- 循环条件:l<r。为了防止出现死循环。
- 缩减搜索空间:l=mid+1,r=mid。找到了2,但是并不知道它是不是最左边的元素,所以要保留这个数r=mid,并且把搜索区间向左移动。如果nums[mid]<k,那么就说明一定在右边的区间,所以l=mid+1。
最后一次出现的位置:12223
- 循环条件:l<r。为了防止出现死循环。
- 缩减搜索空间:l=mid,r=mid-1。找到了2,但是并不知道它是不是最右边的元素,所以要保留这个数l=mid,并且把搜索区间向右移动。如果nums[mid]>k,那么就说明一定在左边的区间,所以r=mid-1。
- mid=l+(r-l+1)/2,为了让还剩最后两个数的时候,mid不等于l,不会进入死循环。
3、找一个最接近2的数
- 循环条件:l<r-1。
- 缩减搜索空间:l=mid,r=mid。
- l和r代表左右两个区间,最后还要判断:nums[l]比2大,说明数组中的数字都大于2。nums[r]比2小,说明数组中的数字都小于2。最后再判断l和r,哪一个离2近一点,返回哪个。
todo:https://leetcode-cn.com/problems/search-insert-position/solution/te-bie-hao-yong-de-er-fen-cha-fa-fa-mo-ban-python-/
69. x 的平方根
实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
1062.最长重复有序区间
给定一个字符串s,找出里面长的重复子区间
- 用到了第二个模版:找最右边的满足条件的位置。
- 但是判断条件是f(s,mid),判断在s当中是否存在长度为mid的重复子序列。
33. 搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。
思路:排除二分法,根据mid的值可以把区间分为两部分,一个有序,一个无序。再用排除法。
class Solution {
public int search(int[] nums, int target) {
if(nums.length==0)
{
return -1;
}
int left=0,right=nums.length-1;
while(left<right)
{
//因为出现了left=mid,为了避免出现死循环,所以是right-left+1
int mid=left+(right-left+1)/2;
if(nums[mid]<nums[right])
{
if(nums[mid]<=target&&target<=nums[right])
{
left=mid;
}
else
{
right=mid-1;
}
}
//为了跟上面的统一,上面算mid的加了1,认为left,mid-1是有序的,所以后面都减了1
else{
if(nums[left]<=target&&target<=nums[mid-1])
{
right=mid-1;
}
else{
left=mid;
}
}
}
//为了避免出现数组中不存在target的情况,所以还要再判断一次
if(nums[left]==target)
{
return left;
}
return -1;
}
}
81. 搜索旋转排序数组 II
假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
思路:向前遍历找到第一个出现的mid值是错误的,也不用这样做,因为如果mid小于right,前后有重复的数,也不影响,这样会造成死循环。
如果mid和right相等,那么不知道左右区间哪一个是严格单调的,所以缩减right的值,再次判断。
public boolean search(int[] nums, int target) {
int len = nums.length;
if (len == 0) {
return false;
}
int left = 0;
int right = len - 1;
while (left < right) {
int mid = (left + right + 1 ) >>> 1;
if (nums[mid] < nums[right]) {
// 10 11 4 5 6 7 8 9
// 右边的一定是顺序数组,包括中间数
if (nums[mid] <= target && target <= nums[right]) {
left = mid;
} else {
right = mid - 1;
}
} else if (nums[mid] > nums[right]) {
// 4 5 9 2
// 左边是一定是顺序数组,包括中间数
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid;
}
}else {
//如果mid和right相等的情况,不知道前后区间哪个是严格递增递减的,缩减区间,right=right-1。要先判断right是否是target
if(nums[right] == target){
return true;
}
right = right -1;
}
}
return nums[left] == target;
}
153. 寻找旋转排序数组中的最小值
假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。你可以假设数组中不存在重复元素。
思路:普普通通排除二分法
154. 寻找旋转排序数组中的最小值 II
假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。请找出其中最小的元素。
注意数组中可能存在重复的元素
思路:mid和right相等时缩减right的大小
public int findMin(int[] nums) {
if(nums.length==0)
{
return -1;
}
int l=0,r=nums.length-1;
while(l<r)
{
int mid=l+(r-l)/2;
if(nums[mid]>nums[r])
{
l=mid+1;
}
else if(nums[mid]<nums[r])
{
r=mid;
}
//当nums[mid]=nums[r]时,缩小r的值继续判断
else{
r=r-1;
}
}
return nums[l];
}
300. 最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。
思路:
1)动态规划
2)二分法??????