原文鏈接:https://blog.csdn.net/haolexiao/article/details/53541837
二分查找足夠簡單,但是坑點也有不少,下面來總結一下
以下是二分查找的標準寫法
以下這個函數是二分查找nums中[left,right)部分,right值取不到,如果查到的話,返回所在地,如果查不到則返回最後一個小於target值得後一個位置。
//右值點不能取到的情況
int binary_search(vector<int>& nums,int left,int right, int target) {
//坑點(1)right究竟能不能取到的問題,這裏是不能取到的情況
int i = left;
int j= right;
while(i<j){
int mid = i+(j-i)/2; //坑點(2)這裏儘量這麼寫,因爲如果寫成(i+j)/2則有溢出的風險
if(nums[mid]>=target) //坑點(3)這個地方大於還是大於等於要依據情況而定
j = mid; //坑點(4)因爲右值點反正不能取到,所以j就可以等於mid
else
i = mid+1; //坑點(5)
}
return i;
}
//右值點能取到的情況
int searchInsert(vector<int>& nums,int left,int right, int target) {
int i = left;
int j= right;
while(i<=j ){
int mid = i+(j-i)/2;
if(nums[mid]>=target)
j = mid-1;
else
i = mid+1;
}
return i;
}
一般來說寫的二分查找的標準程序應該是右邊right值取不到的情況,所以while循環中要加一步判斷i是否小於等於nums.size();
對於坑點2,如果是右值點可以取到情況下,必須是i<=j否則會出現查找偏差,比如數組中就一個元素[3],讓查找5的位置,理應返回1,但是此時會輸出成0,
對於坑點3,主要應用在如果數組中有重複元素,需要判斷其具體要返回什麼位置。
對於坑點,5,如果i不加1的,比如i = 0,j= 1 的情況,會出現mid = 0然後一直死循環下去
對於坑點4,依據right能不能取到而定,如果right可以取到則,right必須要-1,不減1的話,還是會出現i = j時的死循環。
另外一種二分查找,今天面試頭條的時候,被要求寫一個二分查找,原本以爲足夠的簡單,但是發現坑點還是有很多的,比如面試官問,如果數組中出現了重複元素該怎麼辦?怎麼返回重複元素的第一個位置,如果查不到則返回-1,後來想了想寫了個這個方法
int searchInsert(vector<int>& nums, int target) {
int i = 0;
int j = nums.size() - 1;
while (i < j) {
int mid = i + (j - i) / 2;
if (nums[mid] > target)
j = mid - 1;
else
if (nums[mid] == target) //即繼續二分查找查找相等元素的左邊界即可
j = mid;
else
i = mid + 1;
}
if (nums[i] == target)
return i;
else
return -1;
}
這個有一個簡化的寫法,減少比較次數【注意以下寫法只適合返回最開始出現的位置,如果題目改成出現的最後的一個位置,則還是得用上面的方法】
int binarySearch(vector<int> &array, int target) {
// write your code here
int i = 0;
int j = array.size() - 1;
while (i < j) {
int mid = i + (j - i) / 2;
if (array[mid] >= target)
j = mid ;
else
i = mid + 1;
}
if (array[i] == target)
return i;
else
return -1;
}int binarySearch(vector<int> &array, int target) {
// write your code here
int i = 0;
int j = array.size() - 1;
while (i < j) {
int mid = i + (j - i) / 2;
if (array[mid] >= target)
j = mid ;
else
i = mid + 1;
}
if (array[i] == target)
return i;
else
return -1;
}
二分查找的變種
leetcode 153.Find Minimum in Rotated Sorted Array
就是一個排好序的數組,然後將其平移之後,找最小的元素。
諸如[3 4 5 6 7 1 2]
int findMin(vector<int> &num) {
int start=0,end=num.size()-1;
while (start<end) {
if (num[start]<num[end])
return num[start];
int mid = (start+end)/2;
if (num[mid]>=num[start]) { //這一步爲什麼要≥是因爲如果mid = start的情況
start = mid+1;
} else {
end = mid;
}
}
return num[start];
}
//更簡潔的做法,比右邊,因爲最右邊的必然嚴格小於最左邊,所以等於上面的方法比了一個次小的
int findMin(vector<int>& nums) {
int i = 0, j = nums.size()-1;
while(i<j){
int mid = (i+j)/2;
if(nums[mid]>nums[j])
i =mid+1;
else
j = mid;
}
return nums[i];
}
154. Find Minimum in Rotated Sorted Array II
它的進階形式,如果有重複元素的話,該如何呢
要考慮到這種情況[3 3 3 3 3 3 3 3 1 3 ] [1 1 1 1 1 1 1 1 1 1 2 1 1]
int findMin(vector<int>& nums) {
int i = 0, j = nums.size()-1;
while(i<j){
int mid = (i+j)/2;
if(nums[mid]>nums[j])
i =mid+1;
else
if(nums[mid]<nums[j])
j = mid;
else
j--;
}
return nums[i];
}
但實際上這種方法是有一點小問題的,雖然在這個問題中體現不出來,比如就是如果最小值是重複的,且最小值橫跨數組左右兩個端點的時候,諸如[1 1 1 1 1 1 1 1 1 1 2 1 1]這種情況,它找出的最小值的位置其實嚴格意義上不算最小值的位置。
33. Search in Rotated Sorted Array
就是在平移數組中找到一個目標數target,找不到則返回-1,找到返回下標
這道題一開始的思路是直接二分,然後,後來看了解答,就是用上面的方法找出最小值,然後最小值的位置也就意味着其右平移的距離,所以先假設數組沒有平移,二分查找,mid的位置給換成現在數組的位置 rot_mid = (mid+rotate)%n
int search(vector<int>& nums, int target) {
int i=0,j= nums.size()-1;
while(i<j){
int mid = (i+j)/2;
if( nums[mid]>nums[j])
i = mid+1;
else
j = mid;
}
int rot = i;
i = 0;
j = nums.size()-1;
while(i<=j){ //注意這個地方必須是<=,因爲[1] 找1,如果是<的話就不行了。
int mid = (i+j)/2;
int rot_mid = (mid+rot)%nums.size();
if(nums[rot_mid] == target) return rot_mid;
if(nums[rot_mid]>target)
j = mid-1;
else
i = mid+1;
}
return -1;
}
進階形式,就是數組中有重複元素了怎麼辦
81. Search in Rotated Sorted Array II
bool search(vector<int>& nums, int target) {
int i = 0,j = nums.size()-1;
while(i<j){
int mid = (i+j)/2;
if(nums[mid]>nums[j])
i = mid+1;
else
if(nums[mid]<nums[j])
j = mid;
else
j--;
}
//鑑於[*]處的分析,所以這種最小值是重複的,且橫跨首尾的情況,需要額外處理
if(i == 0 && nums[i] == nums[nums.size()-1]){
i = nums.size()-1;
while(i>0 &&nums[i-1] == nums[i]) i--;
}
int rot = i;
i = 0;
j = nums.size()-1;
while(i<=j){
int mid = (i+j)/2;
int rot_mid = (mid+rot)%nums.size();
if(nums[rot_mid] == target) return true;
if(nums[rot_mid]>target)
j = mid-1;
else
i = mid+1;
}
return false;
}
或者採用我一開始的直接二分判斷的方法
bool search(vector<int>& nums, int target) {
int i = 0,j = nums.size()-1;
while(i<=j){
int mid = (i+j)/2;
if(nums[mid] == target)
return true;
if(nums[mid]>nums[j])
if(target>nums[mid] ||target<=nums[j])
i = mid+1;
else
j = mid-1;
else
if(nums[mid]<nums[j])
if(target>nums[mid] && target<=nums[j])
i = mid+1;
else
j = mid-1;
else
j--;
}
return false;
}