什么题考虑二分?
- 有序数组,大概率都能用
从简单到难,做了以下三道题,虽然我是按题号做的,第一道最难。。。不过三道ac 100%,还是很开心的
leetcode 35. 搜索插入位置
- 这道题有序,找一个位置,要求ologn,肯定是二分查找,但是稍微变了一点点,其实就是终止条件变了下
- 当我们找到了,直接返回下标
- 如果mid的值小于target时,我们要判断:
- 1.如果mid已经是最后一个了,那么直接返回最后一位下标+1,也就是len
- 2.如果不满足1,那么mid后至少有一个点,如果mid+1的值>target,那么我们也找到了target的合适位置,返回mid+1;
- 3.如果1、2都不行,再进行二分搜索
- mid大于的情况也类似
基于上面的思路,代码就很简单了
public static int searchInsert(int[] nums, int target) {
int len = nums.length;
int left = 0,right = len -1;
while(left <= right){
int mid = (left + right) / 2;
int value = nums[mid];
if (value == target) return mid;
if (value < target){
if (mid == len -1){ return len;}
if (nums[mid +1] > target){return mid +1;}
left = mid +1;
}
if (value > target){
if (mid == 0){ return 0;}
if (nums[mid -1] < target){return mid;}
right = mid - 1;
}
}
return 0;
}
leetcode 34. 在排序数组中查找元素的第一个和最后一个位置
思路
- 有序,但是有重复的值,可以用二分搜索确定一个阈值
- 也是很简单,如果mid==target,先把mid赋值给答案[i,j],此时从mid发散,找前后的区间
public static int[] searchRange(int[] nums, int target) {
int[] arr = {-1,-1};
int len = nums.length;
if (len == 0) return arr;
int left = 0, right = len -1;
while (left <= right){
int mid = (left + right) / 2;
if (nums[mid] < target){
left = mid +1;
}else if (nums[mid] > target){
right = mid -1;
}
else {
arr[0] = mid;
arr[1] = mid;
for (int i =mid-1;i >=0;i--){
if (nums[i]<target) {break;}
arr[0] = i;
}
for (int i =mid+1;i<len;i++){
if (nums[i]>target) {break;}
arr[1] = i;
}
return arr;
}
}
// find(nums, target, 0, len -1, arr);
return arr;
}
leetcode 33. 搜索旋转排序数组
思路:
- 这道题部分有序,其实也可以想到用二分,但是我当时第一次做,没ac出来
- 其实就是分为两部分,至少一部分是有序的,在有序的部分里找target,另一部分可能是有序的(恰好分在点上了),不过大概率是无序的,但是也是一个翻转有序数组,递归处理就行了,思路比前面的还简单
public int search(int[] nums, int target) {
int len = nums.length;
int index = -1;
if (len == 1){
return nums[0] == target ? 0 : index;
}
return search(nums, target, 0, len-1);
}
public int search(int[] nums, int target, int start, int end){
if (start == end) return nums[start] == target ? start : -1;
if (start > end) return -1;
if (nums[start] == target) return start;
if (nums[end] == target) return end;
int mid = (start + end) /2;
if (nums[mid] == target) return mid;
int i1=-1,i2=-1;
if (nums[mid] > nums[start]){
i1 = find(nums, target, start, mid -1);
i2 = search(nums, target, mid +1, end);
}else {
i1 = find(nums, target, mid+1, end);
i2 = search(nums, target, start, mid-1);
}
if (i1!=-1) return i1;
if (i2!=-1) return i2;
return -1;
}
// 二分查找的的另一种递归写法,
public int find(int[] nums, int target, int start ,int end){
if (start > end){
return -1;
}
if (start == end){
return nums[start]==target?start:-1;
}
int mid = (start + end) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) return find(nums, target, mid +1,end);
else return find(nums, target, start,mid-1);
}