js基本搜索算法實現與170萬條數據下的性能測試

前言

今天讓咱們來繼續聊一聊js算法,經過接下來的講解,咱們能夠了解到搜索算法的基本實現以及各類實現方法的性能,進而發現for循環,forEach,While的性能差別,咱們還會了解到如何經過web worker作算法分片,極大的提升算法的性能。html

同時我還會簡單介紹一下經典的二分算法,哈希表查找算法,但這些不是本章的重點,以後我會推出相應的文章詳細介紹這些高級算法,感興趣的朋友能夠關注個人專欄,或一塊兒探討。前端

對於算法性能,咱們仍是會採用上一章《前端算法系列》如何讓前端代碼速度提升60倍中的getFnRunTime函數,你們感興趣的能夠查看學習,這裏我就不作過多說明。vue

在上一章《前端算法系列》如何讓前端代碼速度提升60倍咱們模擬了19000條數據,這章中爲了讓效果更明顯,我將僞造170萬條數據來測試,不過相信我,對js來講這不算啥。。。react

1.for循環搜索

基本思路:經過for循環遍歷數組,找出要搜索的值在數組中的索引,並將其推動新數組jquery

代碼實現以下:webpack

const getFnRunTime = require('./getRuntime');

 /**
  * 普通算法-for循環版
  * @param {*} arr 
  * 耗時:7-9ms
  */
 function searchBy(arr, value) {
     let result = [];
    for(let i = 0, len = arr.length; i < len; i++) {
        if(arr[i] === value) {
            result.push(i);
        }
    }
    return result
 }
 getFnRunTime(searchBy, 6)
複製代碼

測試n次穩定後的結果如圖:web

2.forEach循環

基本思和和for循環相似:算法

/**
  * 普通算法-forEach循環版
  * @param {*} arr 
  * 耗時:21-24ms
  */
 function searchByForEach(arr, value) {
    let result = [];
    arr.forEach((item,i) => {
        if(item === value) {
            result.push(i);
        }
    })
   return result
}
複製代碼

耗時21-24毫秒,可見性能不如for循環(先暫且這麼說哈,本質也是如此)。vuex

3.while循環

代碼以下:typescript

/**
  * 普通算法-while循環版
  * @param {*} arr 
  * 耗時:11ms
  */
 function searchByWhile(arr, value) {
     let i = arr.length,
     result = [];
    while(i) {
        if(arr[i] === value) {
            result.push(i);
        }
        i--;
    }
    
   return result
}
複製代碼

可見while和for循環性能差很少,都很優秀,但也不是說forEach性能就很差,就不使用了。foreach相對於for循環,代碼減小了,可是foreach依賴IEnumerable。在運行時效率低於for循環。可是在處理不肯定循環次數的循環,或者循環次數須要計算的狀況下,使用foreach比較方便。並且foreach的代碼通過編譯系統的代碼優化後,和for循環的循環相似。

4.二分法搜索

二分法搜索更多的應用場景在數組中值惟一而且有序的數組中,這裏就不比較它和for/while/forEach的性能了。

基本思路:從序列的中間位置開始比較,若是當前位置值等於要搜索的值,則查找成功;若要搜索的值小於當前位置值,則在數列的前半段中查找;若要搜索的值大於當前位置值則在數列的後半段中繼續查找,直到找到爲止

代碼以下:

/**
   * 二分算法
   * @param {*} arr 
   * @param {*} value 
   */
  function binarySearch(arr, value) {
    let min = 0;
    let max = arr.length - 1;
    
    while (min <= max) {
      const mid = Math.floor((min + max) / 2);
  
      if (arr[mid] === value) {
        return mid;
      } else if (arr[mid] > value) {
        max = mid - 1;
      } else {
        min = mid + 1;
      }
    }
  
    return 'Not Found';
  }
複製代碼

在數據量很大的場景下,二分法效率很高,但不穩定,這也是其在大數據查詢下的一點小小的劣勢。

5.哈希表查找

哈希表查找又叫散列表查找,經過查找關鍵字不須要比較就能夠得到須要記錄的存儲位置,它是經過在記錄的存儲位置和它的關鍵字之間創建一個肯定的對應關係f,使得每一個關鍵字key對應一個存儲位置f(key)

哈希表查找的使用場景:

  • 哈希表最適合的求解問題是查找與給定值相等的記錄
  • 哈希查找不適合一樣的關鍵字對應多條記錄的狀況
  • 不適合範圍查找,好比查找年齡18~22歲的同窗

在這我先給出一個最簡版的hashTable,方便你們更容易的理解哈希散列:

/**
 * 散列表
 * 如下方法會出現數據覆蓋的問題
 */
function HashTable() {
  var table = [];

  // 散列函數
  var loseloseHashCode = function(key) {
    var hash = 0;
    for(var i=0; i<key.length; i++) {
      hash += key.charCodeAt(i);
    }
    return hash % 37
  };

  // put
  this.put = function(key, value) {
    var position = loseloseHashCode(key);
    table[position] = value;
  }

  // get
  this.get = function(key) {
    return table[loseloseHashCode(key)]
  }

  // remove
  this.remove = function(key) {
    table[loseloseHashCode(key)] = undefined;
  }
}
複製代碼

該方法可能會出現數據衝突的問題,不過也有解決方案,因爲這裏涉及的知識點比較多,後期我會專門推出一篇文章來介紹:

  • 開放定址法
  • 二次探測法
  • 隨機探測法

使用web worker優化

經過以上的方法,咱們已經知道各類算法的性能和應用場景了,咱們在使用算法時,還能夠經過web worker來優化,讓程序並行處理,好比將一個大塊數組拆分紅多塊,讓web worker線程幫咱們去處理計算結果,最後將結果合併,經過worker的事件機制傳給瀏覽器,效果十分顯著。

總結

  1. 對於複雜數組查詢,for/while性能高於forEach等數組方法
  2. 二分查找法的O(logn)是一種十分高效的算法。不過它的缺陷也很明顯:必須有序,咱們很難保證咱們的數組都是有序的。固然能夠在構建數組的時候進行排序,但是又落到了第二個瓶頸上:它必須是數組。數組讀取效率是O(1),但是它的插入和刪除某個元素的效率倒是O(n)。於是致使構建有序數組的時候會下降效率。
  3. 哈希表查找的基本用法及使用場景。
  4. 條件容許的話,咱們能夠用web worker來優化算法,讓其在後臺並行執行。

好啦,這篇文章雖然比較簡單,但十分重要,但願你們對搜索算法有更加直觀的認識,也但願你們有更好的方法,一塊兒探討交流。

接下來會推出更多優秀的算法,敬請期待哦~

最後,歡迎加入前端技術羣,一塊兒探討前端的魅力

更多推薦

相關文章
相關標籤/搜索