二分查找
单纯的二分查找系列
基本思路
- 就是单纯的二分查找,但要注意可能存在的变形情况
74. 搜索二维矩阵
-
https://leetcode-cn.com/problems/search-a-2d-matrix/
-
class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { int m = matrix.size(); int n = matrix[0].size(); int l = 0; int r = m * n - 1; while (l <= r) { int mid = l + (r - l) / 2; int i = mid / n; int j = mid % n; if (matrix[i][j] == target) return true; if (matrix[i][j] < target) { l = mid + 1; } else { r = mid - 1; } } return false; } };
240. 搜索二维矩阵 II
-
https://leetcode-cn.com/problems/search-a-2d-matrix-ii/
-
可以对每行进行二分查找,但一般这道题,是从右上方开始找
-
class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { int i = 0; int j = matrix[0].size() - 1; while (i < matrix.size() && j >= 0) { if (matrix[i][j] == target) return true; if (matrix[i][j] < target) { i++; } else { j--; } } return false; } };
旋转排序数组系列
基本思路
- 二分,然后判断左边有序还是右边有序
- 如果数组内元素不重复,那么当
nums[mid] >= nums[left]
时,就说明左侧有序,否则右侧有序。存在等于是由于mid
和left
可能相同 - 如果数组元素可以重复时
- 当
nums[mid] == nums[left]
时,无法判断左侧是否有序,可能有序也可能无序,此时需要将left++
,然后在判断 - 当
nums[mid] > nums[left]
时,左侧一定有序 - 当
nums[mid] < nums[left]
时,右侧一定有序
- 当
33. 搜索旋转排序数组
-
https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
-
首先判断当前位置是否是目标值。不是则由于排序数组数字不重复,那么就先判断哪边有序,然后在判断
target
是否在有序的那边,是就对有序的那边二分,不是就对另一边二分 -
class Solution { public: int search(vector<int>& nums, int target) { int l = 0; int r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (target == nums[mid]) { return mid; } if (nums[mid] >= nums[l]) { if (target >= nums[l] && target < nums[mid]) { r = mid - 1; } else { l = mid + 1; } } else { if (target > nums[mid] && target <= nums[r]) { l = mid + 1; } else { r = mid - 1; } } } return -1; } };
81. 搜索旋转排序数组 II
-
https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/
-
首先判断当前位置是否是目标值。不是则由于排序数组数字可以重复,那么当
nums[mid] == nums[left]
时,无法判断出哪边有序,所以让left++
然后在二分判断。判断哪边有序,target
是否在有序集合里。在,二分有序集合,不在二分另一边 -
class Solution { public: bool search(vector<int>& nums, int target) { int l = 0; int r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (target == nums[mid]) return true; if (nums[mid] == nums[l]) { l++; } else if (nums[mid] > nums[l]) { if (target >= nums[l] && target < nums[mid]) { r = mid - 1; } else { l = mid + 1; } } else { if (target > nums[mid] && target <= nums[r]) { l = mid + 1; } else { r = mid - 1; } } } return false; } };
153. 寻找旋转排序数组中的最小值
-
https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/
-
数组元素不重复,所以当
nums[mid] >= nums[left]
时,左侧有序,那么就记录下此时最小值和nums[left]
的更小值,然后去搜索另一个区间的最小值 -
class Solution { public: int findMin(vector<int>& nums) { int l = 0; int r = nums.size() - 1; int result = nums[0]; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] >= nums[l]) { result = min(result, nums[l]); l = mid + 1; } else { result = min(result, nums[mid]); r = mid - 1; } } return result; } };
154. 寻找旋转排序数组中的最小值 II
-
https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/
-
数组元素重复,所以当
nums[mid] == nums[left]
时,无法判断哪边有序,此时将left++
在二分判断。 -
左侧有序,那么就记录下此时最小值和
nums[left]
的更小值,然后去搜索另一个区间的最小值。 -
class Solution { public: int findMin(vector<int>& nums) { int l = 0; int r = nums.size() - 1; int result = nums[0]; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] == nums[l]) { result = min(result, nums[l]); l++; } else if (nums[mid] > nums[l]) { result = min(result, nums[l]); l = mid + 1; } else { result = min(result, nums[mid]); r = mid - 1; } } return result; } };
大于等于target的最左侧位置或小于等于target的最右侧位置系列
基本思路
- 在一个有序数组中找大于等于
target
的最左侧位置,就是当前mid
位置值大于等于target
的话,我仍然继续二分左侧区间,在最后退出条件后,left
的位置就是要找的位置。如果说所有的数都小于target
,则left
值就是nums.size()
- 在一个有序数组中找小于等于
target
的最右侧位置,就是当前mid
位置值小于等于target
的话,我仍然继续二分右侧区间,在最后退出条件后,right
的位置就是要找的位置。如果说所有的数都大于target
,则right
值就是-1
- 当然也可以变形,比如说大于
target
的最左位置,小于target
的最右位置
35. 搜索插入位置
-
其实就是找大于等于
target
的最左侧位置 -
class Solution { public: int searchInsert(vector<int>& nums, int target) { int l = 0; int r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] >= target) { r = mid - 1; } else { l = mid + 1; } } return l; } };
34. 在排序数组中查找元素的第一个和最后一个位置
-
就是两个都找
-
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { int l = 0; int r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] >= target) { r = mid - 1; } else { l = mid + 1; } } if (l >= nums.size() || nums[l] != target) return {-1, -1}; int left = l; l = 0; r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] <= target) { l = mid + 1; } else { r = mid - 1; } } return {left, r}; } };
69. x 的平方根
-
其实就是找小于等于
target
的最右侧位置 -
class Solution { public: int mySqrt(int x) { int l = 0; int r = x; while (l <= r) { int mid = l + (r - l) / 2; if ((long)mid * (long)mid <= x) { l = mid + 1; } else { r = mid - 1; } } return r; } };
寻找峰值系列
852. 山脉数组的峰顶索引
-
寻找峰值,可以从
left = 1, right = nums.size() - 2
开始二分,防止出现数组越界 -
class Solution { public: int peakIndexInMountainArray(vector<int>& arr) { int l = 1; int r = arr.size() - 2; while (l <= r) { int mid = l + (r - l) / 2; if (arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) return mid; if (arr[mid] < arr[mid - 1]) { r = mid - 1; } else { l = mid + 1; } } return -1; } };
162. 寻找峰值
-
https://leetcode-cn.com/problems/find-peak-element/
-
首先判断边界条件,即0位置和最后一个位置是否是峰值
-
如果不是,就可以对
left = 1, right = nums.size() - 2
进行二分。如果mid位置是峰值那么就找到了,如果不是,就看哪边是向上增加的,因为边界不是峰值,所以边界处一定是向下的,那么一个向上,一个向下,中间一定有一个峰值存在 -
class Solution { public: int findPeakElement(vector<int>& nums) { if (nums.size() == 1) return 0; if (nums[0] > nums[1]) return 0; int n = nums.size(); if (nums[n - 1] > nums[n - 2]) return n - 1; int l = 1; int r = n - 2; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) return mid; if (nums[mid] < nums[mid + 1]) { l = mid + 1; } else { r = mid - 1; } } return -1; } };
寻找两个排序数组的第k个数系列
基本思路
- 对于两个有序数组
nums1
和nums2
中,长度分别为m
和n
,找两个有序数组中第k小的数。 - 对k进行二分,则在
nums1
的0
到k/2
,和nums2
的0
到k/2
中,目标数,可能在这两组数中,也可能不在,当在的时候,如果说nums2[k/2]
小于nums1[k/2]
,则目标数一定不在nums2
的0
到k/2
中,后面在继续二分
4. 寻找两个正序数组的中位数
-
https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
-
看代码注释吧
-
class Solution { public: // 找两个有序数组的第k小的数 // p1为当前nums1的起始位置 // p2为当前nums2的起始位置 int getK(vector<int>& nums1, vector<int>& nums2, int p1, int p2, int k) { // 如果nums1长度更小,则换一下位置,保证nums2长度更小 if (nums1.size() - p1 < nums2.size() - p2) { return getK(nums2, nums1, p2, p1, k); } // 如果p2越界了,直接从nums1上面找 if (p2 >= nums2.size()) { return nums1[p1 + k - 1]; } // 如果收k==1,就是找第一小的数,就直接从两者中找到最小的返回 if (k == 1) { return min(nums1[p1], nums2[p2]); } // t为分半后,p11为nums1要比较的位置,p22为nums2要比较的位置,由于nums2短,要防止越界 int t = k / 2; int p11 = p1 + t - 1; int p22 = p2 + t - 1 >= nums2.size() ? nums2.size() - 1 : p2 + t - 1; // nums1[p11] > nums2[p22],则砍掉nums2的那一段数据 if (nums1[p11] > nums2[p22]) { return getK(nums1, nums2, p1, p22 + 1, k - (p22 - p2 + 1)); } // nums1[p11] <= nums2[p22],则砍掉nums1的那一段数据 return getK(nums1, nums2, p11 + 1, p2, k - (p11 - p1 + 1)); } double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int n = nums1.size() + nums2.size(); if (n % 2) { return getK(nums1, nums2, 0, 0, n / 2 + 1); } int t1 = getK(nums2, nums1, 0, 0, n / 2); int t2 = getK(nums2, nums1, 0, 0, n / 2 + 1); return (double(t1) + double(t2)) / 2; } };