题目技巧
二分查找的思路不难理解,但是边界条件容易出错,比如 循环结束条件中 left 和 right 的关系,更新 left 和 right 位置时要不要加 1 减 1。
下面给出两个可以直接套用的模板,记住就好了,免除边界条件出错。
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length - 1; // 注意
while(left <= right) { // 注意
int mid = left + (right - left) / 2; // 注意
if(nums[mid] == target) { // 注意
// 相关逻辑
} else if(nums[mid] < target) {
left = mid + 1; // 注意
} else {
right = mid - 1; // 注意
}
}
// 相关返回值
return 0;
}
}
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length; // 注意
while(left < right) { // 注意
int mid = left + (right - left) / 2; // 注意
if(nums[mid] == target) {
// 相关逻辑
} else if(nums[mid] < target) {
left = mid + 1; // 注意
} else {
right = mid; // 注意
}
}
// 相关返回值
return 0;
}
}
1.二分查找
题目:
思路:
这是一道典型的二分查找题目,
给定一个区间,每次找到区间中间的值与目标值进行比较,
如果匹配上了,直接返回。
如果中间值大于目标值,说明中间值右边的所有都大于目标值(有序),所以淘汰中间值及其右边的值。
反之亦然。
最终区间长度<=0就结束循环或者递归。
代码:
//递归方法
class Solution {
public int search(int[] nums, int target) {
return binarySearch(nums, 0, nums.length - 1, target);
}
public int binarySearch (int[]nums, int star, int end, int target) {
if (star > end) {
return -1;
}
int mid = (star + end) / 2; //注意
if (nums[mid] == target) {
return mid;
}
else if (nums[mid] > target) {
return binarySearch(nums, star, mid - 1, target);
}
else {
return binarySearch(nums, mid + 1, end, target);
}
}
}
// 循环
class Solution {
public int search(int[] nums, int target) {
int pivot, left = 0, right = nums.length - 1;
while (left <= right) {
pivot = left + (right - left) / 2;
if (nums[pivot] == target) return pivot;
if (target < nums[pivot]) right = pivot - 1;
else left = pivot + 1;
}
return -1;
}
}
2.第一个错误版本
题目:
思路:
题目理解:当一个版本为正确版本,则该版本之前的所有版本均为正确版本;当一个版本为错误版本,则该版本之后的所有版本均为错误版本。我们可以利用这个性质进行二分查找。
注意:与上题有所不同,上题最快一次即可匹配成功,本题需要则总是logn次,
因为返回值必须满足:它本身是错误版本,它前面都是正确版本,后面都是错误版本。
解答:假设区间为star,end,mid为中间版本,
每次判断mid是什么版本,如果是坏版本,那么抛弃所有该版本右边的版本(不包括该版本),
反之,抛弃该版本以及所有左边的版本(包括该版本)
最终区间<=0返回答案star。
代码:
// 循环
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
if (isBadVersion(1)) {
return 1;
}
int star = 1, end = n, mid;
while (star < end) {
mid = star + (end - star) / 2; // 防止计算时溢出
if (isBadVersion(mid) ) {
end = mid;
}
else {
star = mid + 1;
}
}
return star;
}
}
3.搜索插入位置
题目:
思路:
题目理解:在一个有序数组中找第一个大于等于 target 的下标。
该题与上两题类似,不断用二分法逼近目标值即可。
代码:
// 循环
class Solution {
public int searchInsert(int[] nums, int target) {
int star = 0, end = nums.length - 1, mid;
while (star <= end) {
mid = (end - star) / 2 + star;
if (nums[mid] == target) {
return mid;
}
else if (nums[mid] > target) {
end = mid - 1;
}
else {
star = mid + 1;
}
}
return star;
}
}