1、情况归类
选择二分法的使用,首先在于区间边界的选择和需要寻找的位置以及等于号的舍取。
1、1: while(left<=right)时,且sum为不降序数组
left (1)target mid (2)target right
当 mid 大于 target 时,移动right的位置。
当 mid 小于 target 时,移动left的位置。
target不占数组位置,只表示大概位置。
以上均为数组下标值。
(1)mid 大于 target ,移动right的位置。
right = mid - 1;
则:left mid( 1 target) right (此时数组观察三个元素)
此时若判断条件为:if ( num[mid] <= target ),则 移动 left 位置。
left = mid + 1;
则:target left(right)(mid) (此时数组观察一个元素)
继续判断并执行,
right = mid - 1;
则:target right left (此时right小于left)
while循环结束,可以观察到此刻 left 距离 target 最近,且处于 target 右边。故此时,我们可以取得 target 的结束地址。
// 二分查找,寻找target的右边界(不包括target)
// 如果rightBorder为没有被赋值(即target在数组范围的左边,例如数组[3,3],target为2)
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else { // 当nums[middle] == target的时候,更新left,这样才能得到target的右边界
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
left (1)target mid (2)target right
(2)mid 小于 target ,移动 left 的位置。
left = mid + 1;
则:left mid( 2 target) right (此时数组观察三个元素)
此时若判断条件为:if ( num[mid] >= target ),则 移动 right 位置。
right = mid - 1;
则:left(right)(mid) target (此时数组观察一个元素)
继续判断并执行,
left = mid + 1;
则:right left target (此时right小于left)
while循环结束,可以观察到此刻 right 距离 target 最近,且处于 target 右边。故此时,我们可以取得 target 的起始地址。
// 二分查找,寻找target的左边界leftBorder(不包括target)
// 如果leftBorder没有被赋值(即target在数组范围的右边,例如数组[3,3],target为4)
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] >= target) { // 寻找左边界,就要在nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
总结:
当我们使用二分法查询不降序数组时,若循环判断条件为:while (left<=right) 时,读入 target 的值后,
(1)我们需要获取 target 值的左边界,则if内的判断条件为:nums[middle] >= target(大于等于);且返回值为 right 。
(2)我们需要获取 target 值的右边界,则if内的判断条件为:nums[middle] <= target(小于等于);其返回值为left。
查询不升序数组时同理:
(1)我们需要获取 target 值的左边界,则if内的判断条件为:nums[middle] >= target(大于等于);且返回值为left 。
(2)我们需要获取 target 值的右边界,则if内的判断条件为:nums[middle] <= target(小于等于);其返回值为right。
口诀:
左大右小,返回等于看前面;移动则相反,定位具体要加减一。
取左值就是大于号,然后返回值、等于号看前面;前面取左,则返回左;前面大于,则等于跟着大于号;移位变化则相反,前面是左大右小,反过来是小左大又;具体target取值,就要把起始地址,结束地址各往里缩小一位。