二分查詢演算法(左閉右開區間)
阿新 • • 發佈:2019-02-01
二分查詢演算法是一個基本但用處十分廣泛的演算法,但要寫出一個沒有bug的二分查詢演算法也不容易,《程式設計珠璣》一書中提到僅有百分之十的人可以第一次就寫出沒有bug的二分查詢演算法,主要原因在於尋找中間區間時資料有可能溢位,以及區間的選擇不正確導致死迴圈,陣列越界等等。二分查詢演算法一共有64種形式,由於在計算機中左閉右開區間非常普遍(比如迭代器中就是使用左閉右開區間),我將劃分範圍劃分為左閉右開區間。如下圖所示
(1)左閉右開區間
也就是將區間分為三部分:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); //這裡指向最後一個元素的後一個元素
int mid = 0;
while (left < right) {
mid = left + ((right - left) >> 1);
if(target < nums.at(mid)) //如果小於num[mid],那麼target在區間[left,mid)之間
right = mid;
else if (target > nums.at(mid)) //如果大於num[mid],那麼target在區間[mid+1,right)之間
left = mid + 1;
else //如果等於num[mid],那麼直接返回
return mid;
}
return left; //如果未找到則返回最接近的位置
}
對應的一種在演算法中提到的對稱區間的情況,和上面的思想一樣,只不過區間換成了左閉右閉區間:
(2)左閉右閉區間:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
int mid = 0;
while (left <= right) {
mid = left + ((right - left) >> 1);
if(target < nums.at(mid))
right = mid - 1;
else if (target > nums.at(mid))
left = mid + 1;
else
return mid;
}
return left;
}
一下是二分查詢演算法在表驅動法的階梯表法中的應用
#include <iostream>
#include <string>
using namespace std;
const int LEVEL_LEN = 5;
const string g_degreeLevel[] = {
"E","D","C","B","A"
};
const int g_degreeLimit[]={
60,70,80,90,100
};
//一般階梯訪問法
string FindDegreeLevel(int degree)
{
int index = 0;
while (g_degreeLevel[index] != "A")
{
if(degree > g_degreeLimit[index])
++index;
else
break;
}
return g_degreeLevel[index];
}
//階梯訪問表的二分法應用
string BinaryFindDegreeLevel(int degree)
{
//利用二分法查詢確定區間(理解迴圈不變式以便寫出正確的二分查詢演算法)1、保證尋找的值一定在尋找區間內,2、保證尋找區間一直縮小
int midIndex = 0;
int lowIndex = 0;
int highIndex = LEVEL_LEN - 1;
while (lowIndex <= highIndex) //保證查詢值在[lowIndex,highIndex]之間
{
midIndex = lowIndex + ((highIndex-lowIndex) >> 1); //防止溢位
if(degree > g_degreeLimit[midIndex]) //degree大於區間[lowIndex,midIndex]的值
lowIndex = midIndex + 1; //那麼degree必然在區間[midIndex+1,highIndex]之間
else if(degree < g_degreeLimit[midIndex]) //degree小於區間[midIndex,highIndex]的值
highIndex = midIndex - 1; //那麼degree必然在區間[lowIndex,midIndex-1]之間
else
return g_degreeLevel[midIndex];
}
return g_degreeLevel[lowIndex];
}
//非遞迴二分查詢
int BinarySearch(int *searchObject,int searchSize,int key)
{
if(searchObject == NULL || searchSize == 0)
return -1;
int lowIndex = 0;
int highIndex = searchSize - 1;
int midIndex = 0;
while(lowIndex <= highIndex) //保證在閉區間[lowIndex,highIndex]中存在
{
midIndex = lowIndex + ((highIndex-lowIndex) >> 1);
if(key > searchObject[midIndex]) //key大於區間[lowIndex,midIndex]之間的所有值
lowIndex = midIndex + 1;
else if(key < searchObject[midIndex]) //key小於區間[midIndex,highIndex]之間的所有值
highIndex = midIndex - 1;
else
return midIndex;
}
return -1;
}
//遞迴二分查詢(左閉右閉區間為初始值並保持)
int RecrusionBinarySearch(int *searchObject,int lowIndex,int highIndex,int key)
{
if(searchObject == NULL || lowIndex > highIndex)
return -1;
else
{
int midIndex = lowIndex + ((highIndex-lowIndex) >> 1);
if(key == searchObject[midIndex])
return midIndex;
else if(key > searchObject[midIndex])
RecrusionBinarySearch(searchObject,midIndex+1,highIndex,key);
else
RecrusionBinarySearch(searchObject,lowIndex,midIndex-1,key);
}
}
//遞迴二分查詢(左閉右開區間為初始值並保持)
int BinarySearch2(int *searchArray, int searchSize, int key)
{
if(searchArray == NULL || searchSize == 0)
return -1;
int midIndex = 0;
int leftIndex = 0;
int rightIndex = searchSize - 1;
while (leftIndex < rightIndex - 1) //保證陣列長度減少1
{
midIndex = leftIndex + ((rightIndex-leftIndex) >> 1);
if(key >= searchArray[midIndex]) //key在區間[midIndex,rightIndex)之間
leftIndex = midIndex;
else //key在區間[leftIndex,midIndex)之間
rightIndex = midIndex;
}
//此時leftIndex >=rightIndex-1
if(searchArray[leftIndex] == key)
return leftIndex;
else if (searchArray[rightIndex] == key)
return rightIndex;
else
return -1;
}
//遞迴二分查詢(左閉右開區間為初始值並保持)
int BinarySearch2(int *searchObject,int searchSize,int key)
{
if(searchObject == NULL || searchSize == 0)
return -1;
int lowIndex = 0;
int highIndex = searchSize - 1;
int midIndex = 0;
while(lowIndex < highIndex) //保證在區間[lowIndex,highIndex)中存在
{
midIndex = lowIndex + ((highIndex-lowIndex) >> 1);
if(key > searchObject[midIndex]) //也就是對於searchObject[0-midIndex]均小於key,那麼key在區間[midIndex+1,highIndex)之間,資料減少mid-low+1
lowIndex = midIndex + 1;
else //也就是key<=searchObject[midIndex],保證key在區間[lowIndex,midIndex]之間,也在[lowIndex,midIndex+1)之間
highIndex = midIndex;
}
if(key == searchObject[lowIndex])
return lowIndex;
return -1;
}
int main(int argc,char** argv)
{
//階梯訪問表
int degree = 0;
while(cin >> degree)
{
string result = BinaryFindDegreeLevelRec(degree);
cout << result;
}
//二分查詢法
int array[10];
for (int i=0; i<10; i++)
array[i] = i+1;
cout<<"No recursive:"<<endl;
cout<<"position:"<<RecrusionBinarySearch(array, 0,9, 6)<<endl;
return 0;
}