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;
}
};
複製代碼