Leetcode上必做的二分查找题目
二分查找,有序数组直接考虑二分查找,如旋转有序数组的最小值、旋转有序数组查找目标值、有序数组目标值的最左和最右位置、二分查找寻找峰值,二分查找问题,要考虑while(left <= right)是否可以等于,right = mid还是right = mid +1,返回值是left还是right,反正多考虑考虑边界问题。
1.在排序数组中查找元素的第一个和最后一个位置
寻找左边界:
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要立即返回
而要收紧右侧边界以锁定左侧边界
寻找右边界:
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid
因为我们需找到 target 的最右侧索引
所以当 nums[mid] == target 时不要立即返回
而要收紧左侧边界以锁定右侧边界
又因为收紧左侧边界时必须 left = mid + 1
所以最后无论返回 left 还是 right,必须减一
public int[] searchRange(int[] nums, int target) {
int left = getLeftBound(nums, target);
int right = getRightBound(nums, target);
return new int[]{left, right};
}
private int getLeftBound(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
right = mid;
}else if (nums[mid] < target) {
left = mid + 1;
}else {
right = mid;
}
}
if (left == nums.length) {
return -1;
}
return nums[left] == target ? left : - 1;
}
private int getRightBound(int[] nums, int target) {
int left = 0;
int right = nums.length ;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
left = mid + 1;
}else if (nums[mid] < target) {
left = mid + 1;
}else {
right = mid ;
}
}
if (left == 0)return -1;
return nums[left - 1] == target ? left - 1 : -1;
}
2. 搜索插入位置
public int searchInsert(int[] nums, int target) {
if (nums == null || nums.length < 0)return -1;
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 {
right = mid - 1;
}
}
//left = right + 1 时候退出循环
return left;
}
3. 搜索旋转排序数组
33. 搜索旋转排序数组
题目要求 O(logN)O(logN) 的时间复杂度,基本可以断定本题是需要使用二分查找,怎么分是关键。
由于题目说数字了无重复,举个例子:
1 2 3 4 5 6 7 可以大致分为两类,
第一类 2 3 4 5 6 7 1 这种,也就是 nums[start] <= nums[mid]。此例子中就是 2 <= 5。
这种情况下,前半部分有序。因此如果 nums[start] <=target<nums[mid],则在前半部分找,否则去后半部分找。
第二类 6 7 1 2 3 4 5 这种,也就是 nums[start] > nums[mid]。此例子中就是 6 > 2。
这种情况下,后半部分有序。因此如果 nums[mid] <target<=nums[end],则在后半部分找,否则去前半部分找。
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return -1;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
if (nums[left] <= nums[mid]) { //前半部分有序
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1;
}else {
left = mid + 1;
}
}else { //后半部分有序
if (target > nums[mid] && target <= nums[right]) {
left = mid + 1;
}else {
right =mid - 1;
}
}
}
return -1;
}
}
4. 81. 搜索旋转排序数组 II
https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/
本题是需要使用二分查找,怎么分是关键,举个例子:
第一类
1011110111 和 1110111101 这种。此种情况下 nums[start] == nums[mid],分不清到底是前面有序还是后面有序,此时 start++ 即可。相当于去掉一个重复的干扰项。
第二类
22 33 44 55 66 77 11 这种,也就是 nums[start] < nums[mid]。此例子中就是 2 < 5;
这种情况下,前半部分有序。因此如果 nums[start] <=target<nums[mid],则在前半部分找,否则去后半部分找。
第三类
66 77 11 22 33 44 55 这种,也就是 nums[start] > nums[mid]。此例子中就是 6 > 2;
这种情况下,后半部分有序。因此如果 nums[mid] <target<=nums[end]。则在后半部分找,否则去前半部分找。
public boolean search(int[] nums, int target) {
if (nums == null || nums.length == 0){
return false;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return true;
}
if (nums[mid] == nums[left]) { //分不清前面有序 还是后面有序 left++ 取出一个重复项
left++;
continue;
}
if (nums[left] < nums[mid]) {
if(nums[left] <= target && target < nums[mid]) {
right = mid - 1;
}else {
left = mid + 1;
}
}else{
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
}else {
right = mid - 1;
}
}
}
return false;
}
5. 寻找旋转排序数组中的最小值
设置left,right左右边界,算出中间数nums[mid]
当nums[mid] > nums[right]时,说明出现了无序的地方在右边
left = mid+1
否则无序点在左侧
right = mid
两边夹逼直到left == right ,剩下的一个元素即为无序点
找最小值,一定存在不需要 left<= right,二分法寻找可能不存在的值才需要取等号。
-
while(left < right) 在循环体外输出
-
while(left <= right) 在循环体内输出
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) { //[left, right)
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
left = mid + 1;
}else {
right = mid;
}
}
return nums[left]; //左右夹逼,缩小区间到只剩一个元素
}
6. 寻找旋转排序数组中的最小值
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (right - left) / 2 + left;
if (nums[mid] == nums[right]) {
right--;
continue;
}
if (nums[mid] > nums[right]) {
left = mid + 1;
}else {
right = mid;
}
}
return nums[left];
}
7. 搜索二维矩阵 II
取数组左上角的元素
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0)return false;
int row = 0;
int col = matrix[0].length - 1;
while (row < matrix.length && col >= 0) {
if (matrix[row][col] == target) {
return true;
}else if (matrix[row][col] < target) {
row++;
}else {
col--;
}
}
return false;
}
8. 4. 寻找两个有序数组的中位数
9. H指数 II
275. H指数 II
先复习一下 274. H指数
求图中正方形的最大边长
public int hIndex(int[] citations) {
Arrays.sort(citations);
reverse(citations);
for (int i = 0; i < citations.length; i++) {
if (i + 1 == citations[i]) return i + 1;
if (i + 1 > citations[i])return i;
}
return citations.length;
}
private static void reverse(int[] citations) {
int left = 0;
int right = citations.length - 1;
while (left < right) {
int temp = citations[left];
citations[left] = citations[right];
citations[right] = temp;
left++;
right--;
}
}
H指数 II
数组已经按照升序排列
- 直接把上面解法中的排序去掉,代码复制过去就可以AC
- 看 nums[mid] 和 [mid, len - 1]的长度 即 len - 1 + mid + 1 = len - mid的长度
- 要返回的是nums中的值
- [0, 1, 2, 5, 6]
public int hIndex(int[] citations) {
int len = citations.length;
if (len == 0 || citations[len - 1] == 0)return 0;
int left = 0;
int right = citations.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (citations[mid] < len - mid) { //[0 , 1, 2 ,5 ,6] 2 < (5 - 2 = 3)比长度小 就应该去掉该值
left = mid + 1;
}else {
//比长度大是满足的,我们应该继续让mid王座去尝试看看有没有更小的mid的值
//可以满足 mid对应的值大于等于从[mid, len - 1]的长度 [0, 1, 2, 3, 5, 6]
right = mid;
}
}
return len - left;
}