int binarySearch(vector<int> &A, int target) {
// [left, right]
int left = 0, right = (int)A.size() - 1;
while (left <= right) {
int mid = left + (right - left)/2;
if (A[mid] == target) {
return mid;
} else if(A[mid] > target) {
right = mid - 1;
} else if (A[mid] < target) {
left = mid + 1;
}
}
return -1;
}
// 递归写法
int binarySearchRecur(vector<int> &A, int target, int left, int right) {
if (left > right) { return -1; }
int mid = left + (right - left)/2;
if (A[mid] > target) {
return binarySearchRecur(A, target, left, mid-1);
} else if (A[mid] < target) {
return binarySearchRecur(A, target, mid + 1, right);
} else {
return mid;
}
}
复制代码
寻找元素第一次出现的位置。c++
int searchLeftBound(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(target == nums[mid]) {
right = mid - 1;
} else if(target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if(left < nums.size() && nums[left] == target) {
return left;
}
return -1;
}
复制代码
寻找左侧边界,小于目标元素git
int binarySearchLeftBound(vector<int> &A, int target) {
// [left, right]
int left = 0, right = (int)A.size() - 1;
while (left <= right) {
int mid = left + (right - left)/2;
if (A[mid] == target) {
right = mid - 1;
} else if (A[mid] > target) {
right = mid - 1;
} else if (A[mid] < target) {
left = mid + 1;
}
}
return left - 1;
}
复制代码
寻找元素最后一次出现的位置github
int searchRightBound(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(target == nums[mid]) {
left = mid + 1;
} else if(target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if(right >= 0 && nums[right] == target) {
return right;
}
return -1;
}
复制代码
寻找右侧边界,大于目标元素数组
int binarySearchRightBound(vector<int> &A, int target) {
// [left, right]
int left = 0, right = (int)A.size() - 1;
while (left <= right) {
int mid = left + (right - left)/2;
if (A[mid] == target) {
//[mid, right]
left = mid + 1;
} else if (A[mid] > target) {
right = mid - 1;
} else if (A[mid] < target) {
left = mid + 1;
}
}
return left;
}
复制代码
704. 二分查找markdown
374. 猜数字大小oop
34. 在排序数组中查找元素的第一个和最后一个位置(查找目标值的左右边界)spa
最直接的想法是先找左边界,在找右边界:code
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int left = searchLeftBound(nums, target);
int right = searchRightBound(nums, target);
vector<int> v;
v.push_back(left);v.push_back(right);
return v;
}
int searchLeftBound(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(target == nums[mid]) {
right = mid - 1;
} else if(target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if(left < nums.size() && nums[left] == target) {
return left;
}
return -1;
}
int searchRightBound(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(target == nums[mid]) {
left = mid + 1;
} else if(target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if(right >= 0 && nums[right] == target) {
return right;
}
return -1;
}
};
复制代码
658. 找到 K 个最接近的元素(题目有点绕)orm
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] >= nums[left]) {
// 说明在 ...left...mid... [left, mid] 升序
if(target == nums[mid]) {
return mid;
} else if(target >= nums[left] && target < nums[mid]) {
// ...left...target...mid...
right = mid - 1;
} else {
// target 可能 left...mid...target?...
left = mid + 1;
}
} else {
// 说明 [mid, right] 是升序的
if(target == nums[mid]) {
return mid;
} else if(target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
};
复制代码
解题思路是要肯定 target 在哪一个区间,剩下的就好办了,按照二分搜索的模板写就好了。
可是要注意 nums[mid] >= nums[left] 这个判断条件如:[3, 1]
class Solution {
public:
bool search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left)/2;
if(nums[mid] > nums[left]) {
if(target == nums[mid]) {
return true;
} else if(target >= nums[left] && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else if(nums[mid] == nums[left]) {
if(target == nums[left]) {
return true;
} else {
left = left + 1;
}
} else {
if(target == nums[mid]) {
return true;
} else if(target > nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return false;
}
};
复制代码
和没有重复的同样思路,可是要处理重复的元素,因此单拎出来处理,由于不知道具体的状况,故只能一个一个的来。
153. 寻找旋转排序数组中的最小值(无重复元素,找最小值)
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while(left <= right) {
int mid = left + (right - left) / 2;
if(nums[mid] >= nums[left]) {
if(nums[left] > nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
} else {
if(nums[left] > nums[right]) {
left = left + 1;
} else {
right = mid - 1;
}
}
}
return nums[left];
}
};
复制代码
这个和旋转数组的写法同样,肯定上升的和降低的区间,而后不断缩小空间
另外一种写法是考虑右边,由于旋转以后右边的小的可能性较大,并且要考虑的条件也比较少
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while(left < right) {// left == mid == right
int mid = left + (right - left) / 2;
if(nums[mid] < nums[right]) {
right = mid;
} else {
left = mid + 1;
}
}
return nums[left];
}
};
复制代码
154. 寻找旋转排序数组中的最小值 II(有重复值,找最小值)
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while(left < right) {// left == mid == right
int mid = left + (right - left) / 2;
if(nums[mid] < nums[right]) {
right = mid;
} else if(nums[mid] == nums[right]) {
right = right - 1;
} else {
left = mid + 1;
}
}
return nums[left];
}
};
复制代码
这个和没有重复值的思路同样,nums[mid] == nums[right] 的时候要特殊处理以下面的特殊用例:
// mid 在坐边
[2,2,2,2,2,2,2,2,2,0,2,2]
// mid 在右边
[2,2,0,2,2,2,2,2,2,2,2,2]
// 由于不能肯定这两种状况,因此单拎出来处理,逐步向最小值靠拢
复制代码
这个问题是旋转数组的延续
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int left = 1, right = arr.size() - 1;// [1, n - 1]
while(left < right) {
int mid = left + (right - left + 1) / 2;
if(arr[mid - 1] < arr[mid]) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
};
复制代码
往常咱们使用「二分」进行查值,须要确保序列自己知足「二段性」:当选定一个端点(基准值)后,结合「一段知足 & 另外一段不知足」的特性来实现“折半”的查找效果。
但本题求的是峰顶索引值,若是咱们选定数组头部或者尾部元素,其实没法根据大小关系“直接”将数组分红两段。
但能够利用题目发现以下性质:因为 arr 数值各不相同,所以峰顶元素左侧必然知足严格单调递增,峰顶元素右侧必然不知足。
所以 以峰顶元素为分割点的 arr 数组,根据与 前一元素/后一元素 的大小关系,具备二段性:
注意:计算 mid 的时候要向上取整,不然会死循环
事实上,咱们还能够利用「三分」来解决这个问题。
顾名思义,「三分」就是使用两个端点将区间分红三份,而后经过每次否决三分之一的区间来逼近目标值。
具体的,因为峰顶元素为全局最大值,所以咱们能够每次将当前区间分为 [l, m1] 、**[m1,m2] ** 和 **[m2, r]**三段,若是知足 arr[m1] > arr[m2],说明峰顶元素不可能存在与 [m2, r] 中,让 r = m2 - 1 便可。另一个区间分析同理。
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int left = 0, right = arr.size() - 1;// [1, n - 1]
while(left < right) {
int m1 = left + (right - left) / 3;
int m2 = right - (right - left) / 3;
if(arr[m1] > arr[m2]) {
right = m2 - 1;
} else {
left = m1 + 1;
}
}
return left;
}
};
复制代码