二分法 Leetcode
二分法
前言
二分法的重点在于 搜索区间 ,常见的有 [left, right] 和 [left, right)。根据题目的不同选择不同的方法。
思路一:[left, right] 代表在循环体内部查找元素
while(left <= right)这种写法表示在循环体内部直接查找元素;- 退出循环的时候
left和right不重合,区间[left, right]是空区间。 - 会对区间内的所有元素进行判断
思路二:[left, right) 代表在循环体内部排除元素
while(left < right)这种写法表示在循环体内部排除元素;- 退出循环的时候
left和right重合,区间 [left, right] 只剩下成 \(1\) 个元素,这个元素有可能就是我们要找的元素。 - 「左右边界向中间走,两边夹」
一、简单的二分查找算法(leetcode 35、leetcode704)
思路一代码(闭区间)
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int l = 0, r = n - 1; //注意,左闭右闭
while (l <= r) {
int mid = l + (r - l) / 2;
if (target > nums[mid]) {
l = mid + 1;
} else if (target < nums[mid]) {
r = mid - 1;
} else if (target == nums[mid]) {
return mid;
}
}
//return -1;
return l;
}
思路二代码(开区间)
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
//if (target > nums[n - 1]) return -1;
int l = 0, r = n; //注意,左闭右开
while (l < r) {
int mid = l + (r - l) / 2;
if (target > nums[mid]) {
l = mid + 1;
} else if (target < nums[mid]) {
r = mid; //右开
} else if (target == nums[mid]) {
r = mid; //右开
}
}
//return nums[l] == target ? l : - 1
return l; //最终 l == r,代表目标位置或要插入的位置
}
总结
对于最简单的二分查找来说,思路一比较简单,直接查找出 target 所在的位置,一旦跳出循环就意味着没有找到。但对于思路二来说,因为是左闭右开,对于下面这种情况(会产生数组越界访问)需要特判。
Input: nums = [-1,0,3,5,9,12], target = 13
Output: -1
二、寻找左侧边界的二分搜索
思路一代码
int left_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
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 if (nums[mid] > target) {
right = mid;
}
}
return left;
}
思路二代码
int left_bound(int[] nums, int target) {
if (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) {
if (mid == 0 || nums[mid-1] != target) return mid;
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return left;
}
三、寻找右侧边界的二分搜索
思路一代码
int left_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
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 if (nums[mid] > target) {
right = mid;
}
}
return left - 1;
}
思路二代码
int left_bound(int[] nums, int target) {
if (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) {
if (mid == 0 || nums[mid-1] != target) return mid;
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return left;
}
四、寻找第一个和最后一个元素的位置(leetcode 34)
思路一代码
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res;
int n = nums.size();
int l = 0, r = n;
while (l < r) {
int mid = l + (r - l) / 2;
if (target < nums[mid]) {
r = mid;
} else if (target == nums[mid]) {
r = mid;
} else if (target > nums[mid]) {
l = mid + 1;
}
}
if (l == n || nums[l] != target) res.push_back(-1);
else res.push_back(l);
l = 0;
r = n;
while (l < r) {
int mid = l + (r - l) / 2;
if (target < nums[mid]) {
r = mid;
} else if (target == nums[mid]) {
l = mid + 1;
} else if (target > nums[mid]) {
l = mid + 1;
}
}
if (l == 0 || nums[l - 1] != target) res.push_back(-1);
else res.push_back(l -1 );
return res;
}
};
思路二代码
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res;
int n = nums.size();
int l = 0, r = n - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (target < nums[mid]) {
r = mid - 1;
} else if (target == nums[mid]) {
if ((mid == 0) || (nums[mid - 1]) != target) {
res.push_back(mid);
break;
} else
r = mid - 1;
} else if (target > nums[mid]) {
l = mid + 1;
}
}
if (res.size() == 0) res.push_back(-1);
l = 0;
r = n - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (target < nums[mid]) {
r = mid - 1;
} else if (target == nums[mid]) {
if ((mid == n - 1) || (nums[mid + 1]) != target) {
res.push_back(mid);
break;
} else
l = mid + 1;
} else if (target > nums[mid]) {
l = mid + 1;
}
}
if (res.size() == 1) res.push_back(-1);
return res;
}
};
五、寻找旋转排序数组的最小值
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while (l <= r) {
if (nums[l] <= nums[r]) return nums[l];
int mid = l + (r - l) / 2;
if (nums[l] <= nums[mid]) {
l = mid + 1;
} else {
r = mid;
}
}
return -1;
}
};
从左边考虑,应该使 mid 靠近 high
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while (l < r){
int mid = l + (r - l + 1) / 2;
if (nums[l] <= nums[mid]) {
l = mid;
} else {
r = mid - 1;
}
}
return nums[(l + 1) % n];
}
};
从右边考虑
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while (l < r){
int mid = l + (r - l) / 2;
if (nums[mid] <= nums[r]) {
r = mid;
} else {
l = mid + 1;
}
}
return nums[l];
}
};

浙公网安备 33010602011771号