数组 - 二分查找
二分查找的意义在于通过二分将遍历的复杂度O(n)降低为O ( log 2 n ) ,故二分的优化意义大于对问题的解决意义
若定义 target 是在一个在左闭右闭的区间里,也就是[left, right],则需要注意两点:
①while(left<=right):此时left==right是有意义的
②if (nums[middle] > target) right 要赋值为 middle - 1:因为nums[middle]是不等于target的
代码实现为:
class Solution {
public int search(int[] nums, int target) {
int left = 0 , right = nums.length-1;
while(left<=right){
int middle = (left+right)/2;
if(nums[middle]==target){
return middle;
}else if(nums[middle]>target){
right = middle-1;//target在左区间[left,middle-1]
}else{
left = middle+1;//target在右区间[middle+1,right]
}
}
return -1;
}
}
看到有序数组,考虑能否使用二分法解决问题,注意遵循循环不变量原则
代码实现为:
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length-1;
while(left<=right){
int middle = (left+right)/2;
if(nums[middle]==target){
return middle;
}else if(nums[middle]<target){
left = middle+1;
}else{
right = middle-1;
}
}
return left;//也可写right+1,包含了三种插入情况,分别是目标值在数组之前、之后以及中间
}
}
采用二分法去寻找左边界和右边界
class Solution {
public int[] searchRange(int[] nums, int target) {
int rightBorder = getRightBorder(nums,target);
int leftBorder = getLeftBorder(nums,target);
//target超出数组(包含之前和之后)
if(rightBorder==-2 || leftBorder==-2){
return new int[]{-1,-1};
}
//数组中存在target
if(rightBorder-leftBorder>1){
return new int[]{leftBorder+1,rightBorder-1};
}
//target在数组范围内,但是没有相等的值
return new int[]{-1,-1};
}
private int getRightBorder(int[] nums,int target){
//找右边界
int left = 0,right = nums.length-1;
int rightBorder = -2;
while(left<=right){
int middle = (left+right)/2;
if(nums[middle]>target){
right = middle-1;
}else{
left = middle+1;
rightBorder = left;
}
}
return rightBorder;
}
private int getLeftBorder(int[] nums,int target){
//找左边界
int left = 0,right = nums.length-1;
int leftBorder = -2;
while(left<=right){
int middle = (left+right)/2;
if(nums[middle]<target){
left = middle+1;
}else{
right = middle-1;
leftBorder = right;
}
}
return leftBorder;
}
}
二分查找最经典的就是分三种情况等于、大于、小于,等于就是开方出来正好是整数,直接return middle;小于可能符合情况,因为是返回整型,所以用res记录下来,但是还得看看有没有比现在的大并且平方小于x的情况,接着赋值eft=middle+1;大于的话一定不符合,直接看左边的right=mid-1。
class Solution {
public int mySqrt(int x) {
int left = 0 , right = x, res = -1;
while(left<=right){
int mid = (left+right)/2;
//注意mid*mid的类型转换,不转换可能会越界
if((long)mid*mid==x){
return mid;
}else if((long)mid*mid>x){
right = mid-1;
}else{
res = mid;
left = mid+1;
}
}
return res;
}
}
class Solution {
public boolean isPerfectSquare(int num) {
int left = 0,right = num;
while(left<=right){
int mid = (left+right)/2;
if((long)mid*mid==num){
return true;
}else if((long)mid*mid<num){
left = mid+1;
}else{
right = mid-1;
}
}
return false;
}
}