二分查找
分类:1)找某个数;2)找左边界;3)找右边界
注意点:1)mid = left + (right - left) / 2
2)if中的判断条件,目的是把数组分开,确定更小的搜索区间
java
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
// if (left > nums.length) {
// return left + 1;
// }
// return left;
return left;
}
}
记录:数组是排序好的,从中找到目标值,返回索引。定义初始搜索区间[0, n - 1],是个闭区间,所以结束循环的条件是left <= right,计算mid,调整搜索区间:如果mid的值大于target,说明右半部分区间都大于target,就要缩小搜索区间到左半部分;如果mid的值小于target,说明左半部分区间都小于target,就要缩小搜索区间到右半部分,直到找到target返回下标或者数组中不存在target。
java
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
// 循环判断target是否在某行中
int m = matrix.length;
int n = matrix[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] >= matrix[i][0] && matrix[i][j] <= matrix[i][n-1]) {
// 二分查找
int left = 0;
int right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (matrix[i][mid] == target) {
return true;
} else if (matrix[i][mid] > target) {
right = mid - 1;
} else if (matrix[i][mid] < target) {
left = mid + 1;
}
}
} else {
continue;
}
}
}
// 在某行中使用二分查找
return false;
}
}
记录:二维有序,所以先确定要搜索的行,在某行中进行二分查找,找到target。
3. 在排序数组中查找元素的第一个和最后一个位置 34 中等
java
class Solution {
public int[] searchRange(int[] nums, int target) {
// 定义二位result数组存放结果
int[] result = new int[2];
result[0] = -1;
result[1] = -1;
// if (nums.length == 0) {
// return result;
// }
// if (nums.length == 1 && nums[0] != target) {
// return result;
// }
// 同时寻找左边界和右边界,l_代表左边界,r_代表右边界
int l_left = 0;
int l_right = nums.length;
int r_left = 0;
int r_right = nums.length;
// 左边界
while (l_left < l_right) {
int mid = l_left + (l_right - l_left) / 2;
if (nums[mid] == target) {
l_right = mid;
} else if (nums[mid] < target) {
l_left = mid + 1;
} else if (nums[mid] > target) {
l_right = mid;
}
}
// 右边界
while (r_left < r_right) {
int mid = r_left + (r_right - r_left) / 2;
if (nums[mid] == target) {
r_left = mid + 1;
} else if (nums[mid] < target) {
r_left = mid + 1;
} else if (nums[mid] > target) {
r_right = mid;
}
}
if (l_left < nums.length && nums[l_left] == target) {
result[0] = l_left;
}
// 这里的条件一定要注意!!!
if (r_left - 1 >= 0 && r_left - 1 < nums.length && nums[r_left - 1] == target) {
result[1] = r_left - 1;
}
return result;
}
}
记录:题目中寻找第一个和最后一个位置,相当于搜索左边界和右边界,将问题转换后按逻辑写。
java
// 思路一:排序后进行二分查找
// 思路二:找到旋转点,分成左右两部分,比较一下target和两部分的开始值,确定其中一部分进行二分查找------>最后计算一下在原nums中的下标
// 思路三:直接二分,判断两边那边有序,在顺序区间二分,如果没在,再分为有序和乱序两部分
// 思路二
// class Solution {
// public int search(int[] nums, int target) {
// int n = nums.length;
// int index = 0;
// for (int i = 1; i < n; i++) {
// if (nums[i] <= nums[i - 1]) {
// index = i;
// break;
// }
// }
// int left = 0;
// int right = n - 1;
// // 设个标识标记是前半部分还是后半部分
// int flag = 0; // 0表示二分前半部分,1表示二分后半部分
// // 判断一下二分前一部分,还是后一部分
// if (nums[0] > target) {
// left = index;
// flag = 1;
// } else {
// if (index != 0) {
// right = index - 1;
// }
// }
// // 二分
// while (left <= right) {
// int mid = left + (right - left) / 2;
// if (nums[mid] == target) {
// // if (flag == 0) {
// // System.out.println("111");
// // return mid + (n - index);
// // }
// // return mid - index;
// return mid;
// } else if (nums[mid] < target) {
// left = mid + 1;
// } else if (nums[mid] > target) {
// right = mid - 1;
// }
// }
// return -1;
// }
// }
// 思路三
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] >= nums[left]) {
// 主要是判断下次是选顺序区间还是乱序区间
// 左半部分有序
if (nums[mid] > target && target >= nums[left]) {
right = mid - 1;
} else {
left = mid + 1;
}
// if (nums[left] < nums[mid] && nums[left] >= target) {
// left = mid + 1;
// } else {
// right = mid - 1;
// }
} else if (nums[mid] <= nums[right]) {
// 右半部分有序
// if (nums[left] > target) {
// left = mid + 1;
// } else {
// right = mid - 1;
// }
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
System.out.println("left:" + left + "--mid:" + mid);
}
return -1;
}
}
java
// 思路一:排序后第一个
// class Solution {
// public int findMin(int[] nums) {
// Arrays.sort(nums);
// return nums[0];
// }
// }
// // 思路二:本身有序,旋转是向后移一位------>找到中间点
// class Solution {
// public int findMin(int[] nums) {
// int n = nums.length;
// int result = nums[0];
// for (int i = 1; i < n; i++) {
// if (nums[i] < nums[i-1]) {
// result = nums[i];
// break;
// }
// }
// return result;
// }
// }
// 思路三:二分查找
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
int minValue = 5001;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[left] <= nums[mid]) {
minValue = Math.min(minValue, nums[left]);
left = mid + 1;
} else {
minValue = Math.min(minValue, nums[mid]);
right = mid - 1;
}
}
return minValue;
}
}