文章首發於:github.com/USTB-musion…前端
今天來聊一聊前端面試中出現頻率比較高的一種有序數據的算法——「二分查找」。git
先看下幾個常見的面試題:github
你能夠先思考一下如何回答上邊的問題🤔,而後帶着答案來閱覽接下來的內容。面試
問題:如何在數組[5,13,19,21,37,56,64,75,80,88,92]中,找出值爲某個元素(好比21)在數組中的位置。算法
最簡單粗暴的方法就是直接遍歷數組,而後找到對應的值把index返回回來,但這種方法的弊端就是若是運氣很差,要找的值在數組的末尾,那就得遍歷n次才能把該值找出來。時間複雜度爲O(n)。數組
那針對這種有序的數據集合,有沒有效率更高的方法呢,那就須要用到接下來要介紹的二分查找了。bash
二分查找,也稱折半查找。利用二分思想,每次查找的時候把數據分爲兩半,從中間值開始找。ui
如上圖所示,low和high表明數組的兩邊下標,mid表明數組的中間下標。spa
若目標值比中間值大,即目標值在mid與high之間,就修改low的值。再對比中間值。3d
若目標值比中間值小,即目標值在low與mid之間,就修改high的值。再對比中間值。
上述就是二分查找的過程,那它的時間複雜度怎麼求呢❓
假設數組的長度爲n,那麼查找一次後長度變爲n/2,再查找一次後長度變爲n/4,以此類推,最壞狀況下,n/2^k爲空,查找中止。因而咱們有如下的公式:
n * n/2 * n/4 * n/8 * n/2^k ·····
複製代碼
以上是一個等比數列,n / 2^k = 1時,k就是查找的次數。即k=log2n,因此時間複雜度爲O(logn),這是一種很是高效率的算法。
須要注意️的是,這裏的遞歸和非遞歸版針對的是簡單的狀況,"簡單"的意思指的是不存在重複元素,存在重複元素的後邊會介紹:)
function binary_search(arr, key) {
var low = 0, high = arr.length - 1;
while(low <= high){
var mid = parseInt(low + (high - low) / 2);
if(key === arr[mid]){
return mid;
} else if (key > arr[mid]){
low = mid + 1;
} else if (key < arr[mid]){
high = mid -1;
} else {
return -1;
}
}
};
var arr = [5,13,19,21,37,56,64,75,80,88,92];
var result = binary_search(arr, 21);
console.log(result);
複製代碼
須要注意的是,mid的取值不要寫成(low + high) / 2,由於若是low+high很大的話,會溢出。所以寫成low+(high-low)/2就不會有這個問題。更進一步,固然也能夠用位運算來low+((high-low)>>1)代替low+(high-low)/2,並且這種位運算的效率更高一些。
二分查找除了上邊介紹的循環方法外,還能夠用遞歸來實現。
function binary_search(arr,low, high, key) {
if (low > high){
return -1;
}
var mid = low + ((high - low) >> 1);
if(arr[mid] == key){
return mid;
}else if (arr[mid] > key){
high = mid - 1;
return binary_search(arr, low, high, key);
}else if (arr[mid] < key){
low = mid + 1;
return binary_search(arr, low, high, key);
}
};
var arr = [5,13,19,21,37,56,64,75,80,88,92];
var result = binary_search(arr,0, 11, 21);
console.log(result);
複製代碼
和上邊介紹的二分查找思路同樣:
function binary_search(arr, key) {
var low = 0, high = arr.length - 1;
while (low <= high) {
var mid = low + ((high - low) >> 1);
if (arr[mid] > key) {
high = mid - 1;
} else if (arr[mid] < key) {
low = mid + 1;
} else {
if ((mid == arr.length - 1) || (arr[mid + 1] != key)) return mid;
else low = mid + 1;
}
}
return -1;
}
var arr = [5,13,19,21,21,37,56,64,75,80,88,92];
var result = binary_search(arr, 21);
console.log(result);
複製代碼
以上就是二分查找的核心知識了,只要掌握了以上的核心知識,二分查找的變型問題將迎刃而解。能夠在評論區試着寫一下:
數組中存在重複的數據,怎麼找出第一個大於等於目標值的元素位置❓
示例: [5,13,19,21,21,21,37,56,64,75,80,88,92],找出第一個大於等於21的元素位置
複製代碼