二分查找(binary search)
1 二分查找的思想
每次將待查找元素與區間中間元素進行比較,將查找區間縮減為之前的一半,直到找到待查找元素或查找區間大小為0。
2 實現及關鍵點
2.1 關鍵點
1)循環退出條件
循環退出條件為low <= high,其中low為查找區間的下邊界、high為上邊界,而不是low < high。如果條件為low < high,那麽當查找區間大小為1即low = high時,循環退出就無法與最後一個數據元素進行比較。
2)區間中間索引mid的取值
一般來說mid = (low + high) / 2,但是當low + high超出其數據類型範圍時會造成溢出,所以這樣的mid取值是不穩妥的。mid = low + (high - low) / 2這樣減少了溢出的可能,或者mid = low + ((high - low) >> 1)這樣通過位運算使得計算更加快速。
3)區間上下邊界的更新
更新應為low = mid + 1、high = mid - 1而不是low = mid、high = mid。因為如果是low = mid、high = mid的更新方式,那麽當查找區間為1時而最後的數據元素又不等於查找元素,那麽程序會進入死循環。
2.2 實現
1 /*查找升序數組中是否存在數據元素num,存在返回其在數組中的位置,不存在則返回-1*/ 2 int BinarySearch(int* pArrNum, int n, int num) { 3 int low = 0; //低索引 4 int high = n - 1; //高索引 5 int mid; //查找區間中間元素索引 6 7 while (low <= high) { 8 mid = low + ((high - low) >> 1); 9 if (num == pArrNum[mid]) return mid; 10 else if (num > pArrNum[mid]) low = mid + 1; 11 else high = mid - 1; 12 } 13 14 return-1; 15 }
2.3 復雜度
時間復雜度O(logn),空間復雜度O(1)。
3 適用場景
1)有序數組。二分查找算法依賴於按照下標隨機訪問元素,所以必須是數組;其次,二分查找針對的數據必須是有序的,如果無序要先對其進行排序。
2)數據量太小不適合二分查找。如果數據量太小,例如只有10個數據元素,那麽順序查找就足夠了。但是如果兩個數據元素的比較操作耗時較多時,例如長度較大的字符串比較,那麽還是二分查找合適,因為二分查找比較次數較少。
3)數據量太大不適合二分查找。數組這種數據結構的對內存依賴較大,要求分配連續的內存空間,當數據量較大時很可能會導致內存分配失敗。
4 二分查找的變式
1)查找第一個值等於給定值的元素;
2)查找最後一個值等於給定值的元素;
3)查找第一個大於等於給定值的元素;
4)查找最後一個小於等於給定值的元素;
變體的二分查找問題要註意一下幾個細節:循環退出條件、區間上下界更新方法和返回值選擇。寫二分查找代碼時不要過於追求完美、整潔的寫法,代碼易讀、沒有bug更重要。
1 /*查找第一個等於給定值的元素,存在返回其在數組中的位置,不存在則返回-1*/ 2 int SearchFirstElem(int* pArrNum, int n, int num) { 3 int low = 0; 4 int high = n - 1; 5 int mid; 6 int first = -1; //第一個等於給定值的元素下表 7 8 while (low <= high) { 9 mid = low + ((high - low) >> 1); 10 if (pArrNum[mid] == num) first = mid, high = mid - 1; 11 else if (pArrNum[mid] > num) high = mid - 1; 12 else low = mid + 1; 13 } 14 15 return first; 16 } 17 18 /*查找最後一個等於給定值的元素,存在返回其在數組中的位置,不存在則返回-1*/ 19 int SearchLastElem(int* pArrNum, int n, int num) { 20 int low = 0; 21 int high = n - 1; 22 int mid; 23 int last = -1; //最後一個等於給定值的元素下表 24 25 while (low <= high) { 26 mid = low + ((high - low) >> 1); 27 if (pArrNum[mid] == num) last = mid, low = mid + 1; 28 else if (pArrNum[mid] > num) high = mid - 1; 29 else low = mid + 1; 30 } 31 32 return last; 33 } 34 35 /*查找第一個大於等於給定值的元素,存在返回其在數組中的位置,不存在則返回-1*/ 36 int SearchFirstGigOrEqualElem(int* pArrNum, int n, int num) { 37 int low = 0; 38 int high = n - 1; 39 int mid; 40 int first = -1; //第一個大於等於給定值的元素下表 41 42 while (low <= high) { 43 mid = low + ((high - low) >> 1); 44 if (pArrNum[mid] >= num) first = mid, high = mid - 1; 45 else low = mid + 1; 46 } 47 48 return first; 49 } 50 51 /*查找最後一個大於等於給定值的元素,存在返回其在數組中的位置,不存在則返回-1*/ 52 int SearchLastLittleOrEqualElem(int* pArrNum, int n, int num) { 53 int low = 0; 54 int high = n - 1; 55 int mid; 56 int last = -1; //第一個等於給定值的元素下表 57 58 while (low <= high) { 59 mid = low + ((high - low) >> 1); 60 if (pArrNum[mid] <= num) last = mid, low = mid + 1; 61 else high = mid - 1; 62 } 63 64 return last; 65 }
該篇博客是自己的學習博客,水平有限,如果有哪裏理解不對的地方,希望大家可以指正!
二分查找(binary search)