Binary Search
1.回顾
可以用很简单的代码实现二分查找(开始写了另一个函数用来迭代,大概是分治学多了QAQ)
public int search(int[] nums, int target) {
int left=0, right=nums.length-1;
int mid;
while(left < right){
mid=(left + right)/2;
if(target == nums[mid])
return mid;
if(target < nums[mid])
right = mid-1;
else if(target > nums[mid])
left = mid+1;
}
return -1;
}
2.三种主要形式
官方给出的三种对比
1.基础,和上面的代码无差别
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0)
return -1;
int left = 0, right = nums.length - 1;
while(left <= right){
// Prevent (left + right) overflow
int mid = (left + right) / 2;
if(nums[mid] == target)
return mid;
else if(nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
// End Condition: left > right
return -1;
}
- 平方根问题
模仿基础情况写的代码如下,但对大数会超时
public int mySqrt(int x) {
if(x==0) return 0;
int mid, sqr_mid, left=1, right=x/2;//缩小范围
while(left<=right){
mid=(left+right)/2;
sqr_mid=mid*mid;
if(sqr_mid == x || (sqr_mid < x && sqr_mid+2*mid+1 > x))
return mid;
if(sqr_mid < x)
left=mid+1;
else
right=mid-1;
}
return 0;
}
修改后不报错但仍会发生溢出。
AC方案:不定义mid平方(sqr_mid),用mid==x/mid代替原来的检查语句
public int mySqrt(int x) {
if(x==0) return 0;
int mid, left=1, right=x/2+1;//缩小范围
while(left<=right){
mid=(left+right)/2;
if(mid == x/mid)
return mid;
if(mid < x/mid)
left=mid+1;
else
right=mid-1;
}
return right;
}
2.无法描述
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0)
return -1;
int left = 0, right = nums.length;
while(left < right){
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target)
return mid;
else if(nums[mid] < target)
left = mid + 1;
else
right = mid;
}
// Post-processing:
// End Condition: left == right
if(left != nums.length && nums[left] == target) return left;
return -1;
}
终止条件是left==right
- 坏版本问题
初始的代码,同样是会溢出
public int firstBadVersion(int n) {
int left=1, right=n;
int mid;
while(left < right){
mid=(left + right)/2;
if(isBadVersion(mid)==false)
left = mid+1;
else
right = mid;
}
return left;
}
在评论区才明白差在这一步:
start+(end-start)/2有效避免溢出,got√
官方AC代码
public int firstBadVersion(int n) {
int left = 1;
int right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (isBadVersion(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
- 寻找peek
public int findPeakElement(int[] nums) {
int left=0, right=nums.length-1;
int mid;
if(right<=0) return 0;
while(left < right){
mid = left + (right - left)/2;
if(nums[mid] > nums[mid+1])
right = mid;
else
left = mid + 1;
}
return left;
}
- 寻找翻转一次后的peek
——找到比第一个元素小的那部分
public int findMin(int[] nums) {
int left=0, right=nums.length-1;
int mid;
if(nums[left]<nums[right])
return nums[0];//未翻转
while(left < right){
mid = left + (right - left)/2;
if(nums[mid]>=nums[0])
left=mid+1;
else
right=mid;
}
return nums[left];
}
对比而言,我理解的区别大概就是——
第一种问题通常是[A,A,A,B,C,C,C]这种数组中找到B的位置;
而第二种问题是在[A,A,A,B,B,B,B]中找到第一个B的位置。
比如在“坏版本问题”中,如果mid是好的,就把左标记移到mid的下一个;反之,把右标记移到当前的mid,继续逼近…
- 范围查找
逻辑有点混乱,总之是分两步找到区间的左右端点
public int[] searchRange(int[] nums, int target) {
int[] ans={-1,-1};
int left=0, right=nums.length-1;
int mid;
if(right<0) return ans;
while(left < right){ // 区间左端点
mid=(left + right)/2;
if(target <= nums[mid])
right = mid;
else
left = mid+1;
}
if(nums[left]!=target) // 找不到左端点,
return ans; // 表示数组中找不到target
ans[0]=left;
if(nums.length==1){
ans[1]=left;
return ans;
}
left=0;
right=nums.length-1;
while(left < right){ // 区间右端点
mid=(left + right)/2;
if(target >= nums[mid])
left = mid+1;
else
right = mid;
}
ans[1]=left-1;
if(target == nums[left]) ans[1]=left; // 考虑target是数组的最大元素(?
return ans;
}
- k个最近元素
用到集合与数组的一些方法,较难
待回看
- 范围查找
- 翻转列表求最小元素
- 翻转列表求最小元素II
未解
- k个最近元素
- 求幂
来源:CSDN
作者:Ms. Schmidt
链接:https://blog.csdn.net/Mayday__ashin/article/details/104007902