解法一:
二分查找的重点在于划分好查找的区间边界问题。解法一采用的是左区间和右区间都闭合的方法。设置左右指针,寻找到中间位置。如果说target比中间值小,就让右指针变换为middle减1(我们已经知道middle不等于target);如果说target比中间值大,就让左指针变换为middle加1。重复上述过程,设置循环体。如果左指针越过右指针代表我们已经遍历完成却仍未找到,为了算法的健壮性,我们设置返回值-1(因为数组的索引里没有-1)。
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right= nums.length-1;
while (left<=right){
int middle=(left+right)/2;
if (nums[middle]> target){
right=middle-1;
}else if (nums[middle]< target){
left=middle+1;
}else if (nums[middle]==target){
return middle;
}
}
return -1;
}
}
解法二:
与解法一类似,但查找的区间由完全闭合改为左闭右开(因为左边最低是0再低为负数求中间值不好计算,而右边便于计算)。左指针设置为0,右指针设置为arr.length。
寻找中间键middle。if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]。(此段文字引用于代码随想录)
注意:while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的。
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right= nums.length;//保持左闭右开
while (left<right){//划分的区间为左闭右开,mention!!!
int middle=(left+right)/2;
if (nums[middle]> target){
right=middle;//middle必然不等于traget,把右指针设置为middle保持区间原则
}else if (nums[middle]< target){
left=middle+1;
}else if (nums[middle]==target){
return middle;
}
}
return -1;
}
}
解法:
先利用二分查找正常寻找目标,如果找到目标则进行返回。如果查找不到目标,按照题目要求,给出的数组是一个递增数组,需要按照顺序插入target。我们可以设置一个查找索引的函数,采用了最简单的顺序查找,查找到距离target的索引,返回插入位置。
优化解法:
采用二分查找找到小于或者等于最接近的数,返回其索引。
😑😑😑博主之后会研究的,用c语言实现(抄写)过一次。
class Solution {
public int searchInsert(int[] nums, int target) {
//二分查找
int left = 0;
int 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;
}else if (nums[middle] < target) {
left = middle+1;
}
}
int index=getIndex(nums,target);
return index;
}
public static int getIndex(int[] nums, int target) {
int index=0;
for (int i=0; i < nums.length; i++) {
if (nums[i] < target) {
index++;
}
}
return index;
}
}
解法:
本题中寻找到的target都是连续的,那么博主想到的最直接的方法是利用普通的二分查找先找到一个满足target的索引。随后利用两个方法向上和向下查找出等于target的上界和下界。
class Solution {
public int[] searchRange(int[] nums, int target) {
int index=getIndex(nums,target);
int[] back={-1,-1};
if(index==-1){
return back;
}
int left=getMin(nums,index,target);
int right=getMax(nums,index,target);
int[] back1={left,right};
return back1;
}
public static int getIndex(int[] nums,int target){
int left=0;
int right=nums.length-1;
while(left<=right){
int middle=(right+left)/2;
if(nums[middle]==target){
return middle;
}else if(nums[middle]<target){
left=middle+1;
}else if(nums[middle]>target){
right=middle-1;
}
}
return -1;
}
public static int getMax(int[] nums,int index,int target){
int count=index;
for(int i=index;i<=nums.length-1;i++){
if(nums[i]!=target){
break;
}
count++;
}
return count-1;
}
public static int getMin(int[] nums,int index,int target){
int count=index;
for(int i=index;i>=0;i--){
if(nums[i]!=target){
break;
}
count--;
}
return count+1;
}
}
求算数平方根,实际上可以看做寻找两个连续的整数,他们的平方分别大于和小于目标值(特殊情况等于),返回那个较小的整数。
对于查找的区间,没有一个数的平方根会比该数字的一半大(0,1除外单独判断)。
如:5=2.236*2.236; 2.236<2.5
更甚者25=5*5; 5<12.5
利用二分查找进行查找并返回。
class Solution {
public int mySqrt(int x) {
if(x==1){
return 1;
}else if(x==0){
return 0;
}
int left=1;
int right=x/2;//大于2的数字其平方根必然小雨该数字的一半
while(left<=right){
int middle=(left+right)/2;
if(middle== x/middle){
return middle;
}else if(middle>x/middle){
right = middle-1;
}else if(middle<x/middle){
left = middle+1;
}
}
return right;
}
}
借用上一道题的解法,先求出给出数字的平方根,再去判断平方根的平方是否等于该数字,如果相等返回true,不相等返回false。
class Solution {
public boolean isPerfectSquare(int num) {
if(num==1){
return true;
}else if(num==0){
return true;
}
int left=1;
int right=num/2;//大于2的数字其平方根必然小雨该数字的一半
while(left<=right){
int middle=(left+right)/2;
if(middle== num/middle){
right=middle;
break;
}else if(middle>num/middle){
right = middle-1;
}else if(middle<num/middle){
left = middle+1;
}
}
//return false;
if(right*right==num){
return true;
}else{
return false;
}
}
}
解法:题单上说的是可以尝试用滑动窗口进行求解,但是博主没有想出来咋办。看了官方的题解后确实是用二分查找比较方便。
有两种思路:
一种是先用二分查找在列上找到小于且最接近目标的数,随后在用一个二分在行上进行查找。
public static boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int l = 0, r = m-1;
// 二分查找:在第0列中二分,找到小于target的最接近的元素,记录行号row
int row = 0;
while (l <= r) {
int mid = l + (r-l)/2;
int cur = matrix[mid][0];
if (cur == target) return true;
else if (cur < target) {
row = mid;
l = mid + 1;
} else { // cur > target
r = mid - 1;
}
}
// 二分查找:在第row行中二分,找target是否存在
l = 1;
r = n-1;
while (l <= r) {
int mid = l + (r-l)/2;
int cur = matrix[row][mid];
if (cur == target) return true;
else if (cur < target) l = mid + 1;
else r = mid - 1;
}
return false;
}
第二种是把整个二维数组看作一维数组进行二分查找,把每一行都拼在第一行的后面。不难看出二维数组arr[][]坐标值可以转换为arr[索引/列==行][索引/列=列]。????官方是这么说的,对着敲跑过了但是我总觉得哪里有问题。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int chang=matrix.length;
int kuan=matrix[0].length;
int right=chang*kuan-1;
int left=0;
while(left<=right){
int mid=(left+right)/2;
if(matrix[mid/kuan][mid%kuan]==target){
return true;
}else if(matrix[mid/kuan][mid%kuan]>target){
right=mid-1;
}else{
left=mid+1;
}
}
return false;
}
}