八、二分查找(Binary Search)
一、概述
二分查找(Binary Search,也稱折半查找)——針對有序數據集合的查找算法
1、基本思想
類似分治思想,每次都通過跟區間的中間元素進行對比,將代查找的區間縮小為之前的一半,直到找到要查找的元素,或者區間被縮小為0(不存在該元素)。
2、時間復雜度分析——O(logn)
不妨假設數據量為n,每次查找后數據量均為原來的1/2,最壞的情況下,直到查找區間被縮小為空,才停止。設經過k次區間縮小操作區間縮小到1,可得 k=log2n 時間復雜度為O(k),也就是O(logn)。
O(1) 常量級時間復雜度的算法可能還沒有O(logn)的算法執行效率高。因為:大O標記法表示時間復雜度時,會省略掉常數、系數和低階,O(1)可能表示一個非常大的常量值,eg: O(10000)。
二、簡單實現
二分查找最簡單的情況:有序數組中不存在重復元素
1、遞歸方法實現
#include<iostream> using namespace std;int BinarySearchRecursive(int *array, int low, int high, int key) {if(low > high)return -1;// int mid = (low + high) / 2; 若low和high比較大,兩者之和就可能溢出int mid = low + (high - low) / 2; // 若性能有要求,可將除以2操作轉化為位運算,以提升效率// int mid = low + ((high - low)>>1)if(array[mid] == key)return mid;else if(array[mid] < key)return BinarySearchRecursive(array, mid+1, high, key);elsereturn BinarySearchRecursive(array, low, mid-1, key); }int main() {int array[10];for(int i = 0; i < 10; i++)array[i] = i;cout<< "Recursive:"<<endl;cout<<"postion:"<<BinarySearchRecursive(array,0,9,4)<<endl;return 0; }2、非遞歸方法
int BinarySearch(int *array, int aSzie, int key) {if(array == NULL || aSize == 0)return -1;int low = 0;int high = aSize - 1;int mid = 0;while(low <= high){mid = low + (high - low) / 2;if(array[mid] == key)return mid;else if(array[mid] < key)low = mid + 1;elsehigh = mid - 1;}return -1; }3、應用場景的局限性
適用于:插入、刪除操作都不頻繁,一次排序多次查找且數據量較大的場景。
-
二分查找依賴的是順序表數據結構,也就是數組(可以按照下標隨機訪問元素)。
-
二分查找的數據是有序的:排序算法時間復雜度最低為O(nlogn),若動態變化的數據集合,不再適用。
-
適合數據量較大的場景,而非特別大。因為數組為了支持隨機訪問的特性,要求內存空間連續。
特例:若數據之間的比較操作特別耗時,不管數據量大小,都推薦使用二分查找。同on個過盡可能較少比較次數來提升性能。
三、應用實例
1、實例
假設我們有 1000 萬個整數數據,每個數據占 8 個字節,如何設計數據結構和算法,快速判斷某個整數是否出現在這 1000 萬數據中? 我們希望這個功能不要占用太多的內存空間,最多不要超過 100MB,你會怎么做呢?
2、分析
由于每個數據占 8 個字節,內存占用差不多為80M < 100M,符合內存限制。
==》先從小到大排序,然后利用二分查找算法找數據。
注意:散列表、二叉樹這些支持快速查找的動態數據結構。但是,在該情況卻不行的。雖然在大部分情況下,用二分查找可以解決的問題,用散列表、二叉樹都可以解決。但是都會需要比較多的額外的內存空間。所以,如果用散列表或者二叉樹來存儲這
1000 萬的數據,用 100MB 的內存肯定是存不下的。而二分查找底層依賴的是數組,除了數據本身之外,不需要額外存儲其他信息,是最省內存空間的存儲方式,所以剛好能在限定的內存大小下解決這個問題。
四、二分查找的進級(變形問題)
前提:數據都是從小到大排列的,且數據中存在重復的數據。
1、常見的變形問題
- 查找第一個值等于給定值的元素
- 查找最后一個值等于給定值的元素
- 查找第一個大于等于給定值的元素
- 查找最后一個小于等于給定值的元素
2、查找第一個值等于給定值的元素
(1)方法一
int bsearch1(int *array, int aSize, int key) {int low = 0;int high = aSize - 1;while (low <= high) {int mid = low + ((high - low)/2);if(array[mid] > key)high = mid - 1;else if(array[mid] < key)low = mid + 1;else// 當array[mid]=key時,// 需要確認一下這個 array[mid] 是不是第一個值等于給定值的元素{// 若mid等于0,說明該元素為第一個元素;// array[mid]的前一個元素array[mid-1]不等于key,說明該元素為第一個元素;if((mid == 0)||(array[mid-1]!=key))return mid;// 否則更新區間elsehigh = mid - 1;}}return -1; }(2)方法二
int bsearch2(int *array, int aSize, int key) {int low = 0;int high = aSize - 1;while (low <= high){int mid = low + ((high - low) / 2);if(array[mid] >= key){high = mid - 1;}else{low = mid + 1;}}if(array[low] == key) return low;else return -1; }3、查找最后一個值等于給定值的元素
int bSearch(int *array,int aSize,int key) {int low = 0;int high = aSize - 1;while(low <= high){int mid = low + ((high - low) / 2);if (array[mid] >= key)high = mid - 1;else if (array[mid < key]) {low = mid + 1;}else{if((mid == aSize - 1)||(array[mid+1] != key))return mid;elselow = mid + 1;}}return -1; }4、查找第一個大于等于給定值的元素
int bSearch(int *array,int aSize,int key) {int low = 0;int high = aSize - 1;while(low <= high){int mid = low + ((high - low) / 2);if (array[mid] >= key){if((mid == 0)||(array[mid-1] < key))return mid;elsehigh = mid - 1;}else{low = mid + 1;}}return -1; }5、查找最后一個小于等于給定值的元素
int bSearch(int *array,int aSize,int key) {int low = 0;int high = aSize - 1;while(low <= high){int mid = low + ((high - low) / 2);if (array[mid] > key){high = mid - 1;}else{if((mid == aSize - 1)||(array[mid+1] > key))return mid;elselow = mid + 1;}}return -1; }總結
以上是生活随笔為你收集整理的八、二分查找(Binary Search)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 七、排序(4)——qsort()
- 下一篇: 九、跳表(Skip List)