聊一聊前端算法面試——二分查找

文章首發於:github.com/USTB-musion…前端

寫在前面

今天來聊一聊前端面試中出現頻率比較高的一種有序數據的算法——「二分查找」。git

先看下幾個常見的面試題:github

  1. 二分查找的時間複雜度怎麼求❓
  2. 二分查找的遞歸和非遞歸怎麼實現❓
  3. 數組中存在重複的數據,怎麼找出元素最後一次出現的位置❓
  4. 數組中存在重複的數據,怎麼找出第一個大於等於目標值的元素位置❓

你能夠先思考一下如何回答上邊的問題🤔,而後帶着答案來閱覽接下來的內容。面試

1.二分查找的時間複雜度怎麼求❓

問題:如何在數組[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);
複製代碼

3.數組中存在重複的數據,怎麼找出元素最後一次出現的位置❓

和上邊介紹的二分查找思路同樣:

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的元素位置
複製代碼
相關文章
相關標籤/搜索