前端進階: 總結幾個常用的js搜索算法和性能對比

2021-02-19 趣談前端

前言

今天讓我們來繼續聊一聊js算法,通過接下來的講解,我們可以了解到搜索算法的基本實現以及各種實現方法的性能,進而發現for循環,forEach,While的性能差異,我們還會了解到如何通過web worker做算法分片,極大的提高算法的性能。

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

對於算法性能,我們還是會採用上一章《前端算法系列》如何讓前端代碼速度提高60倍中的getFnRunTime函數,大家感興趣的可以查看學習,這裡我就不做過多說明。

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

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

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)

2.forEach循環

/**
  * 普通算法-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循環(先暫且這麼說哈,本質也是如此)。3.while循環

/**
  * 普通算法-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)在這我先給出一個最簡版的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的事件機制傳給瀏覽器,效果十分顯著。

總結

對於複雜數組查詢,for/while性能高於forEach等數組方法

二分查找法的O(logn)是一種十分高效的算法。不過它的缺陷也很明顯:必須有序,我們很難保證我們的數組都是有序的。當然可以在構建數組的時候進行排序,可是又落到了第二個瓶頸上:它必須是數組。數組讀取效率是O(1),可是它的插入和刪除某個元素的效率卻是O(n)。因而導致構建有序數組的時候會降低效率。

哈希表查找的基本用法及使用場景。

條件允許的話,我們可以用web worker來優化算法,讓其在後臺並行執行。

好啦,這篇文章雖然比較簡單,但十分重要,希望大家對搜索算法有更加直觀的認識,也希望大家有更好的方法,一起探討交流。

相關焦點

  • D3.js、echar.js 前端必備大數據技能
    「 前言 」web前端一直都是個講究門面和藝術美感的行業,如果你以為邏輯很強就夠了,那就錯了,你只適合做後端,真正的好前端是對美感和可視化的東西有一種接近痴狂的愛好
  • 從前端性能優化引申出來的5道經典面試題
    以上過程大概講解了一下從 url 到頁面渲染的整個過程,其實涉及到了幾個需要關注的問題,下面來具體講講問題 2:js css 順序對前端優化影響上面我們說到了整個渲染流程,但是沒有說到 css 和 js 對渲染的影響。
  • Web前端慢加密
    只要算法不可逆,那只能窮舉。  窮舉的原理很簡單。只要知道密文是用什麼算法加密的,我們也用相同的 算法,把常用的詞組跑一遍。若有結果和密文一樣,那就猜中了。  窮舉的速度有多快?這和加密算法有關。加密一次有多快,猜一次也這麼 快。  例如 MD5 加密是非常快的。
  • 前端高效開發必備的 js 庫梳理
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫之前有很多人問學好前端需要學習哪些 js 庫, 主流框架應該學 vue 還是 react ? 針對這些問題, 筆者來說說自己的看法和學習總結.
  • 「Web前端開發進階篇」Chrome瀏覽器的調試使用
    作者Web前端開發進階篇自Google發布Chrome瀏覽器以來,其隨著Chrome瀏覽的優化及其附帶的Chrome開發者工具和額外的擴展程序,受到了廣大開發者的喜愛,越來越多的前端開發人員喜歡在Chrome裡開發調試代碼,對開發人員非常友好,許多的優秀插件可以幫助開發人員更高效完成開發工作,另外可以登錄,不管到哪裡,只要一登錄就可以實現同步,極為方便
  • 裴琳 · 微成長--web性能優化與前端工程
    ....任性的分割線web性能優化與前端工程 每個參與過開發企業級web應用的前端工程師或許都曾思考過前端性能優化方面的問題。我們有雅虎14條性能優化原則,還有兩本很經典的性能優化指導書:《高性能網站建設指南》、《高性能網站建設進階指南》。這些性能優化原則大概是在7年前提出的,對於web性能優化至今都有非常重要的指導意義。
  • 聊一聊前端性能與體驗的優化
    前言性能優化 ,每個工程師跑不掉的一個話題。這裡是本人總結的一些優化手法,希望對大家有所幫助,後續也會繼續更新。演示源碼和 PPT 無條件分享。演示 PPT (一定要看,超帥)橫屏觀看更佳:http://118.25.49.69:8086前端性能的影響前端性能的一個重要指標是頁面加載時間,不僅事關用戶體驗,也是搜尋引擎排名考慮的一個因素。
  • 使用reveal.js製作精美的網頁版PPT
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫最近在做季度總結和技術分享,所以需要做個PPT, 來回顧這半年來的技術貢獻.這裡列一下筆者的技術調研方法論, 供大家參考:所以筆者接下來大致按照以上幾個衡量標準, 來帶大家一起感受一下如何快速通過reveal.js實現一個極具動感的PPT.
  • 我這前端五年的總結,希望對你有幫助!
    前端UI高度還原能力你會發現大廠對於UI還原度的要求是100%,但是有些時候,兼容性問題就需要經驗積累了。處理各種兼容性問題js版本的兼容,安卓、iOS版本的兼容。最多的是瀏覽器的版本的功能兼容。因為有一些瀏覽器用的還是舊內核,你需要對新API做兼容。如:vuejs2.0,不建議使用ie9以下的瀏覽器。
  • 前端逐幀動畫性能探究和比較
    同屏幀數對比】24/30/50/60幀:https://www.bilibili.com/video/av19011575/前端方案有哪些?無損壓縮——最精確的拼圖相對有損壓縮而言無損壓縮則會真實的記錄圖像上每個像素點的數據信息,但為了壓縮圖像文件的大小會採取一些特殊的算法。
  • JS版漢字與拼音互轉終極方案,附簡單的JS拼音輸入法
    代碼和DEMO演示github項目地址:https://github.com/liuxianan/pinyinjs完整demo演示:http://demo.liuxianan.com/pinyinjs/帶多音字識別的演示:http://demo.liuxianan.com/pinyinjs/polyphone.html漢字轉拼音:
  • 我發現了 Vue.js 中的性能陷阱
    問題出在 Vue.js 嗎?是 Netlify 嗎?還是因為我們的代碼有缺陷?我必須找出答案。 我內心深處對遊戲的熱愛,讓我一直渴望能自己製作一些電子遊戲。幾個月前我開始將這種夢想變為現實,並第一次參加了全球遊戲大賽(Global Game Jam)。我和我的團隊使用 Vue.js 構建了一個名為「 ZeroDaysLeft 」的遊戲,其形式是 Web 端的單頁面應用程式。
  • 【專欄試讀】(02)初次接觸前端,我們要理解哪些名詞?| Web 前置知識
    2 初識前端,我們需要知道的最基本概念2.1 第一類:初做一個靜態頁面HTML(HyperText Markup Language 超文本標記語言):用來描述網頁的一種語言,它包含很多的「標籤」和「純文本」——HTML 的結構決定這個頁面是否穩定、規範、性能好不好;HTML5:是 HTML 的新標準,它更加的語義化(且增加了許多語義化的標籤)。
  • Vue.js最佳靜態站點生成器對比
    因此在本文中,我會向大家介紹用於靜態站點生成的四大 Vue.js 框架,並對它們做詳細對比,幫助找到適合你用例的選項。https://nuxtjs.org/名單上的第一個是 Nuxt.js,這是一個基於 Vue.js 構建的開源高級框架。Nuxt.js 會抽象出客戶端 - 伺服器分發細節,從而簡化 Web 開發工作。
  • 過去三年使用前端框架的感受
    作者:王力國來源:https://zhuanlan.zhihu.com/p/194311058Angularjs背景:2016.10-2017.7,某雲計算公司,客服雲平臺Angularjs 是我參加工作之初接觸的第一個前端框架
  • js中常用的字符串的方法
    今天來整理一下js中字符串常用的方法常用的方法:charAt():返回指定索引位置的字符indexOf():返回字符串中檢索指定字符第一次出現的位置