數據結構與算法在前端領域的應用(進階)

這是一個我即將作的一個《數據結構與算法在前端領域的應用》主題演講的一個主菜。
若是你對這部份內容比較生疏,能夠看個人數據結構和算法在前端領域的應用(前菜)前端

這裏我會深刻幫助你們如何根據業務抽離出純粹的模型,從而轉化爲算法問題,linux

若是你們對數據結構和算法感興趣,歡迎關注個人我的公衆號,或者入羣和我交流,二維碼在文章末尾。git

關於我

我是一個對技術充滿興趣的程序員, 擅長前端工程化,前端性能優化,前端標準化等。程序員

作過 .net, 搞過 Java,如今是一名前端工程師。github

除了個人本職工做外,我會在開源社區進行一些輸出和分享,GitHub 共計得到 1.5W star。比較受歡迎的項目有leetcode 題解 , 宇宙最強的前端面試指南
個人第一本小書web

以上帝角度來看前端

讓咱們以更高的層次來看一下,從大的範圍上前端領域都在作什麼?面試

從業務上來講,咱們會去作各個端的開發、網關、接口、工程化。
從技術上說,則是基於 WEB、Node 的通用技術,以及各平臺(最多見的就是安卓和 IOS)的專有技術。正則表達式

在這裏我以本身的標準總結了如下三點:算法

  1. 架構和平臺

其實平臺建設也是架構中的一環,之因此列出來單獨講是由於這塊內容相對比較大。
每一個公司,部門,項目都有本身的架構設計和規範。它們環環相套組成了整個公司的架構體系。編程

不少公司在作工具鏈,在作跨端方案,在作底層融合等,這些都屬於這個範疇。
好比最近比較火的 Serverless 也是屬於這個範疇。

  1. 規範和標準化

前端行業規範目前來看的話就兩個,一個是 ECMA 的規範,一個是 W3C 的規範。
前端行業規範是很是重要的,否則前端會很是混亂,想一下前端剛剛誕生出來的時候就知道了。

公司內部也會有一些規範,可是很難上升到標準層次。 目前國內沒有一個行業承認的標準化組織,
這算是一個遺憾吧。 好消息是國人在標準化組織的參與感愈來愈強,作了更多的事情。

其實這部分咱們的感知是比較弱的,一個緣由就是咱們一直在努力對接行業的標準,不多去本身創造一些標準。
緣由有幾點,一方面本身作標準,維護更新標準很難,另外一方面本身作標準須要學習成本和轉換成本。

可是這並不意味這作公司標準或者行業領域規範就沒有用,相反很是有用。我以前作過一個《標準化能給咱們帶來什麼》的
分享,詳細介紹了標準化對於咱們的重要性。

  1. 生態體系

其實前端的工做就是人機交互,這其中涉及的東西不少,相關領域很是普遍。

好比智能手錶、智能 TV、智能眼鏡、頭戴 AR,VR 等新的交互模式咱們如何去融入現有開發體系中 ?
人工智能在前端開發能夠發揮怎麼樣的做用 ? 這些其實不少公司已經在嘗試,而且取得了很是不錯的效果。

好比 IDE 是開發過程很是重要的工具,咱們是否能夠去作標準化的 IDE,甚至放到雲端。

無處不在的算法

上面咱們從多個方面從新審視了一下前端,除了人工智能部分,其餘部分根本沒有提到算法。
是否是算法在前端領域應用不多呢? 不是的。

一方面就像上一節介紹的,咱們平常開發中使用的不少東西都是通過數據結構和算法的精心封裝,
好比 DOM 和 VDOM,以及 JSON。 JSON的序列化和反序列化是咱們無時無刻使用的方法,
好比咱們須要和後端進行數據交互,須要和其餘線程(好比webworker)進行數據交互都要通過
序列化和反序列化,如何減小數據傳輸,如何提升序列化和反序列化的效率,如何在二者
之間尋求一種平衡都是咱們須要研究的。

JSON 也是一種樹結構

甚至還有不少框架以數據結構直接命名,好比 GraphQL,就是
用圖這種數據結構來命名,從而體現其強大的關聯查詢能力。 好比 tensorflow 以張量(tensor)來加深你們對上面兩點的印象命名,

TensorFlow™ 是一個採用數據流圖(data flow graphs),用於數值計算的開源軟件庫。節點(Nodes)在圖中表示數學操做,圖中的線(edges)則表示在節點間相互聯繫的多維數據數組,即張量(tensor)。

上面提到的各個環節都或多或少會用到算法。首先網絡部分就涉及到不少算法,
好比有限狀態機,滑動窗口,各類壓縮算法,保障咱們信息不泄漏的各類加密算法等等,簡直不要太多。
雖然這些網絡部分不少都是現成的,可是也不排除有一些須要咱們本身根據當前實際場景本身去搭建一套的可能。
這在大公司之中是很是常見的。

咱們再來看下執行咱們代碼的引擎,以 V8 爲例,其自己涉及的算法不算在內。
可是當咱們基於 V8 去作一些事情,咱們就須要瞭解一些編譯相關的原理。
這裏我舉個例子,下圖是支付寶的小程序架構。 若是咱們不懂一些算法的話,
是很難像支付寶同樣結合本身的業務去作一些突破的。

16c4be71f9a30347?w=1494&h=454&f=png&s=124940
(圖片來自 https://www.infoq.cn/article/...

另一些高層的架構中也會有不少算法方面的東西,好比我須要在前端作增量更新的功能。
增量更新在APP中早已不是新鮮的東西了,可是真正作JS等靜態資源的實時增量更新還比較少,
這裏面會涉及很是複雜的交互和算法。

上面提到的更多的是高層面上,事實上即便是業務層面也有不少值得挖掘的算法模型。
咱們須要從複雜的業務中提煉出算法模型,才能獲得實際應用。惋惜的是不少時候咱們缺少這種抽象能力和意志。

除了上一節我講述的常見場景以外,我還會在下一節介紹幾個實際業務場景,從而加深你們的理解。
但願你們看了以後,可以在本身的實際業務中有所幫助。

性能和優雅,我全都要

從表象上看,使用合適的數據結構和算法有兩方面的好處。

第一個是性能,這個比較好理解一點,咱們追求更好的時間複雜度和空間複雜度,
而且咱們須要不斷地在二者之間作合理的取捨。

第二點是優雅,使用合適的數據結構和算法。能讓咱們處理問題更加簡潔優雅。

下面我會舉幾個我在實際業務場景中的例子,來加深你們對上面兩點的印象。

權限系統

假如你如今開發一款相似石墨文檔的多人在線協做編輯文檔系統。

這裏面有一個小功能是權限系統。 用戶能夠在咱們的系統中建立文件夾和文件,
而且管理角色,不一樣的角色能夠分配不一樣的文件權限。 好比查看,下載,編輯,審批等。

咱們既能夠給文件夾分配權限,又能夠給文件分配權限,若是對應文件該角色沒有權限,
咱們須要遞歸往上搜索,看有沒有相應權限,若是有,則這個角色有文件的該操做權限。

16c4be7aac2ddd08?w=743&h=518&f=jpeg&s=79243

如圖,fileA 的權限就須要從 fileA 開始看有沒有對應權限,若是有,則返回有權限。
若是沒有,則查找 app 文件夾的權限,重複這個過程,直到根節點。

若是你是這個系統的前端負責人,你會如何設計這個系統呢?

其實作這個功能的方案有不少,我這裏參考了 linux 的設計。
咱們使用一個二進制來標示一個權限有仍是沒有。

16c4be7d2b3e44e6?w=547&h=475&f=jpeg&s=61364

這樣的話,一方面咱們只須要 4 個 bit 就能夠存儲權限信息,存儲已是極限了。
另外一方面咱們經過位運算便可算出有沒有權限,二進制運算在計算性能上也是極限了。

另外代碼寫起來,也會很是簡潔,感興趣的能夠本身試試。

16c4be800eba463e?w=630&h=469&f=jpeg&s=62786

擴展: 假如文件權限不是隻有兩種可能,好比有三個取值怎麼辦?

狀態機

什麼是狀態機

狀態機表示若干個狀態以及在這些狀態之間的轉移和動做等行爲的數學模型。
通俗的描述狀態機就是定義了一套狀態変更的流程:狀態機包含一個狀態集合,
定義當狀態機處於某一個狀態的時候它所能接收的事件以及可執行的行爲,執行完成後,狀態機所處的狀態。

咱們以現實中普遍使用的有限狀態機(如下簡稱 FSM)爲例進行講解

FSM 應用很是普遍, 好比正則表達式的引擎,編譯器的詞法和語法分析,網絡協議,企業應用等不少領域都會用到。

其中正則中使用的是一種特殊的 FSM, 叫 DFA(Deterministic Finite Automaton), 經過分裂樹形式來運行。

爲何要使用狀態機

第一個緣由,也是你們感觸最深的一個緣由就是經過狀態機去控制系統內部的狀態以及狀態流轉,邏輯會
比較清晰,尤爲在邏輯比較複雜的時候,這種做用愈加明顯。

第二個緣由是經過狀態機,咱們能夠實現數據以及系統的可視化。剛纔我提到了正則表達式用到了狀態機,
那麼正則是否能夠可視化呢? 答案是確定的,這裏我介紹一個可視化正則表達式的一個網站。

16c4be86a028363a?w=1421&h=212&f=jpeg&s=36372

實際業務中若是使用狀態機來設計系統也能夠進行可視化。相似這樣子:

16c4be8883ef3e9e?w=1413&h=621&f=jpeg&s=75645

(圖來自 https://statecharts.github.io...

能夠看出,邏輯流轉很是清晰,咱們甚至能夠基於此進行調試。
固然,將它做爲文檔的一部分也是極好的,關於狀態機的實際意義還有不少,咱們接下來舉幾個例子說明。

狀態機的實際應用場景

匹配三的倍數

實現一個功能,判斷一個數字是不是三的倍數。 數字能夠很是大,以致於超過 Number 的表示範圍,
所以咱們須要用 string 來存儲。

一個簡單直觀的作法是直接將每一位都加起來,而後看加起來的數字是不是三的倍數。
可是若是數字大到必定程度,致使加起來的數字也超過了 Number 的表示範圍呢?

一個方法是使用狀態機來解決。

咱們發現一個數字除以 3 的餘數一共有三種狀態,即 0,1,2。 基於此咱們能夠構建一個 FSM。
0,1,2 之間的狀態流轉也不可貴出。

舉個例子,假設當前咱們是餘數爲 0 的狀態,這時候再來一個字符。

  • 若是這個字符是 0,3 或者 9,那麼咱們的餘數仍是 0
  • 若是這個字符是 1,4 或者 7,那麼咱們的餘數是 1
  • 若是這個字符是 2,5 或者 8,那麼咱們的餘數仍是 2

用圖大概是這個樣子:

16c4be8bbf5231ea?w=530&h=411&f=jpeg&s=62446

若是用代碼大概是這樣的:

function createFSM() {
  return {
    initial: 0,
    states: {
      0: {
        on: {
          read(ch) {
            return {
              0: 0,
              3: 0,
              9: 0,
              1: 1,
              4: 1,
              7: 1,
              2: 2,
              5: 2,
              8: 2
            }[ch];
          }
        }
      },
      1: {
        on: {
          read(ch) {
            return {
              0: 1,
              3: 1,
              9: 1,
              1: 2,
              4: 2,
              7: 2,
              2: 0,
              5: 0,
              8: 0
            }[ch];
          }
        }
      },
      2: {
        on: {
          read(ch) {
            return {
              0: 2,
              3: 2,
              9: 2,
              1: 0,
              4: 0,
              7: 0,
              2: 1,
              5: 1,
              8: 1
            }[ch];
          }
        }
      }
    }
  };
}

const fsm = createFSM();
const str = "281902812894839483047309573843389230298329038293829329";
let cur = fsm.initial;

for (let i = 0; i < str.length; i++) {
  if (!["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(str[i])) {
    throw new Error("非法數字");
  }
  cur = fsm.states[cur].on.read(str[i]);
}
if (cur === 0) {
  console.log("能夠被3整除");
} else {
  console.log("不能夠被3整除");
}

其實代碼還能夠簡化,讀者能夠下去嘗試一下。

能夠看出,咱們這種方式邏輯清晰,且內存佔用不多,不會出現溢出的狀況。

正則是基於自動機實現的,那麼使用正則匹配會是怎麼樣的呢?你們能夠本身試一下。
答題活動

通過上面的熱身,咱們來一個真實的項目來練練手。

有這樣一個業務場景,咱們須要設計一款答題活動,讓用戶過來進行答題,
咱們預先設置 N 道題目。 規則以下:

  • 初始狀態用戶會進入歡迎頁面
  • 答對以後能夠直接進入下一個題目
  • 答錯了可使用復活卡從新答,也可使用過關卡,直接進入下一題
  • 用戶能夠經過其餘途徑獲取復活卡和過關卡
  • 答對所有 N 道題以後用戶過關,不然失敗
  • 無論是過關仍是失敗都展現結果頁面,只不過展現不一樣的文字和圖片

這實際上是一個簡化版本的真實項目。 若是要你設計這樣的一個系統,你會如何設計?

相信你確定能想出不少種方法來完成這樣的需求,接下來我會用 FSM 來實現。

咱們很容易畫出整理的流程圖:

16c4be8f4e682331?w=783&h=241&f=jpeg&s=53309

對於答題部分則稍微有一點麻煩,可是若是你用狀態機的思惟去思考就很容易,
咱們不難畫出這樣的圖:

16c4be9141e833c9?w=792&h=319&f=jpeg&s=83353

JS 中有不少 FSM 的框架, 你們均可以直接拿過來使用。 筆者以前所在的項目
也用到了這樣的技術,可是筆者是本身手寫的簡化版本 FSM,基本思想是一致的。

其餘

事實上,還有不少例子能夠舉。

假設咱們後端服務器是一主一備,咱們將全部的數據都同時存儲在兩個服務器上。
假如某一天,有一份數據丟失了,咱們如何快速找到有問題的服務器。

這其實能夠抽象成【Signle Number問題】。 所以不少時候,不是缺少應用算法的場景,
而是缺少這種將現實業務進行抽象爲純算法問題的能力。
咱們會被各類細枝末節的問題遮蔽雙眼,沒法洞察隱藏在背後的深層次的規律。

編程最難是抽象能力,前幾年我寫了一篇文章《爲何咱們的代碼難以維護》,
其中一個很是重要的緣由就是缺少抽象。

從如今開始,讓咱們來鍛鍊抽象能力吧。

關注我

最近我從新整理了下本身的公衆號,而且我還給他換了一個名字《腦洞前端》,它是一個幫助你打開大前端新世界大門的鑰匙 🔑,在這裏你能夠聽到新奇的觀點,看到一些技術嘗新,還會收到系統性總結和思考。

我會盡可能經過圖的形式來闡述一些概念和邏輯,幫助你們快速理解,圖解前端是個人目標。

以後個人文章同步到微信公衆號 腦洞前端 ,您能夠關注獲取最新的文章,或者和我進行交流。

16c395633a33b721?w=430&h=430&f=jpeg&s=41111

交流羣

如今仍是初級階段,須要你們的意見和反饋,爲了減小溝通成本,我組建了交流羣。你們能夠掃碼進入

QQ 羣

16c395636c87d23c?w=226&h=290&f=png&s=19216

微信羣

16c39563516faf00?w=512&h=512&f=jpeg&s=56509

(因爲微信的限制,100 我的以上只能邀請加入, 你能夠添加個人機器人回覆「大前端」拉你進羣)

相關文章
相關標籤/搜索