📁 35. 搜索插入位置
将数组划分为两个区间[0 , left] [right , n],其中有 左区间的元素都小于targe,右区间的元素都大于等于target。
left指针维护左区间结束位置,right维护右区间的起始位置,right所在位置一定是第一个大于等于target的位置。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0 , right = nums.size();
while(left < right)
{
int mid = (left + right) >> 1;
if(nums[mid] < target)
left = mid + 1;
else
right = mid;
}
return left; 循环结束 此时left==right
}
};
📁 74. 搜索二维矩阵
这一道题目还是类似上一道题目,只不过多加了一层循环。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size() , n = matrix[0].size();
for(int i = 0 ; i < m ; ++i)
{
int left = 0 , right = n - 1;
while(left < right)
{
int mid = (left + right) >> 1;
if(matrix[i][mid] < target)
left = mid + 1;
else
right = mid;
}
if(matrix[i][right] == target)
return true;
}
return false;
}
};
📁 34. 在排序数组中查找元素的第一个和最后一个位置
进行两次二分,寻找左区间的右端点 以及 寻找右区间的左端点。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.size() == 0)
return {- 1 , -1};
int n = nums.size();
int left = 0 , right = n - 1;
int begin = -1 , end = -1;
while(left < right)
{
int mid = (left + right) >> 1;
if(nums[mid] < target)
left = mid + 1;
else
right = mid;
}
if(nums[left] != target)
return {begin , end};
begin = left , left = 0 , right = n - 1;
while(left < right)
{
int mid = (left + right + 1) >> 1;
if(nums[mid] > target)
right = mid - 1;
else
left = mid;
}
end = right;
return {begin , end};
}
};
📁 33. 搜索旋转排序数组
二分的条件就是: 有序或部分有序
1. 进行二分后,一定会后一个有序数组,有一个或有序或无序的数组,例如:
a. 4 5 6 [7] 0 1 2
b. 6 7 0 [1] 2 4 5
2. 根据有序数组,缩小搜索范围
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
int left = 0 , right = n - 1;
while(left <= right)
{
int mid = (left + right) >> 1;
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 + 1] && target <= nums[right])
left = mid + 1;
else
right = mid - 1;
}
}
return -1;
}
};
📁 153. 寻找旋转排序数组中的最小值
将 mid 和 数组最后一个元素n 比较:
1. mid > n , 说明数组一定被划分成为两段,其中第一段的元素均大于第二段的元素
2. mid <= n , 说明x要么是最小值,要么最小值在右边(nums本身是递增数组, 只有一段)
int findMin(vector<int>& nums) {
int left = 0 , right = nums.size() - 1;
int x = nums[nums.size() - 1];
while(left < right)
{
int mid = (left + right) >> 1;
if(nums[mid] > x)
left = mid + 1;
else
right = mid;
}
return nums[left];
}
📁 4. 寻找两个正序数组的中位数
规则: 使用一条分割线把两个数组分割成两个部分
1. 左边和右边的元素个数相等,或左边元素的个数比右边个数的多1个
2. 左边元素之和 <= 右边元素之和 (若不满足交叉小于等于的关系 就需要调整分割线的位置)
中位数一定就与分割切两侧的元素相关 确定这个红线使用二分查找
交叉小于等于:
arr1[分割线左侧元素] <= arr2[分割线右侧元素] && arr2[分割线左侧元素] <= arr1[分割线右侧元素]
因为需要通过访问分割线左右两边的元素,因此应该在较短的数组上确定分割线的位置
i: 第一个数组分割线右边的第一个元素的下标 i = 第一个数组分割线左侧元素的个数
j: 第二个数组分割线右边的第一个元素的下标 j = 第二个数组 分割线左侧元素的个数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
//需要访问 "中间分割线" 左右两边的元素,因此需要在较短的数组上确定 "中间分割线"的位置,避免越界
if(nums1.size() > nums2.size())
{
vector<int> tmp = nums1;
nums1 = nums2;
nums2 = tmp;
}
//通过元素个数较少的的数组确定分割线数量,避免数组越界
int m = nums1.size() , n = nums2.size();
int totalLeft = (m + n + 1) / 2; //表示合并后数组中左半部分的元素数量
int left = 0 , right = m;
while(left < right)
{
int i = left + (right - left + 1) / 2;
int j = totalLeft - i;
if(nums1[i - 1] > nums2[j])
right = i - 1;
else
left = i;
}
int i = left , j = totalLeft - i;
//处理边界情况
int num1LeftMax = i == 0 ? INT_MIN : nums1[i - 1];
int num1RightMin = i == m ? INT_MAX : nums1[i];
int num2LeftMax = j == 0 ? INT_MIN : nums2[j-1];
int num2RightMin = j == n ? INT_MAX : nums2[j];
if((m + n ) % 2 == 1)
return max(num1LeftMax , num2LeftMax);
else
return (double)((max(num1LeftMax , num2LeftMax) + min(num1RightMin , num2RightMin))) / 2;
}
};