数据结构和算法在前端领域的应用(进阶)

这是一个我即将作的一个《数据结构与算法在前端领域的应用》主题演讲的一个主菜。 若是你对这部份内容比较生疏,能够看个人数据结构和算法在前端领域的应用(前菜)前端

这里我会深刻帮助你们如何根据业务抽离出纯粹的模型,从而转化为算法问题,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 去作一些事情,咱们就须要了解一些编译相关的原理。 这里我举个例子,下图是支付宝的小程序架构。 若是咱们不懂一些算法的话, 是很难像支付宝同样结合本身的业务去作一些突破的。

支付宝小程序架构
(图片来自 www.infoq.cn/article/ull…)

另一些高层的架构中也会有不少算法方面的东西,好比我须要在前端作增量更新的功能。 增量更新在APP中早已不是新鲜的东西了,可是真正作JS等静态资源的实时增量更新还比较少, 这里面会涉及很是复杂的交互和算法。

上面提到的更多的是高层面上,事实上即便是业务层面也有不少值得挖掘的算法模型。 咱们须要从复杂的业务中提炼出算法模型,才能获得实际应用。惋惜的是不少时候咱们缺少这种抽象能力和意志。

除了上一节我讲述的常见场景以外,我还会在下一节介绍几个实际业务场景,从而加深你们的理解。 但愿你们看了以后,可以在本身的实际业务中有所帮助。

性能和优雅,我全都要

从表象上看,使用合适的数据结构和算法有两方面的好处。

第一个是性能,这个比较好理解一点,咱们追求更好的时间复杂度和空间复杂度, 而且咱们须要不断地在二者之间作合理的取舍。

第二点是优雅,使用合适的数据结构和算法。能让咱们处理问题更加简洁优雅。

下面我会举几个我在实际业务场景中的例子,来加深你们对上面两点的印象。

权限系统

假如你如今开发一款相似石墨文档的多人在线协做编辑文档系统。

这里面有一个小功能是权限系统。 用户能够在咱们的系统中建立文件夹和文件, 而且管理角色,不一样的角色能够分配不一样的文件权限。 好比查看,下载,编辑,审批等。

咱们既能够给文件夹分配权限,又能够给文件分配权限,若是对应文件该角色没有权限, 咱们须要递归往上搜索,看有没有相应权限,若是有,则这个角色有文件的该操做权限。

如图,fileA 的权限就须要从 fileA 开始看有没有对应权限,若是有,则返回有权限。 若是没有,则查找 app 文件夹的权限,重复这个过程,直到根节点。

若是你是这个系统的前端负责人,你会如何设计这个系统呢?

其实作这个功能的方案有不少,我这里参考了 linux 的设计。 咱们使用一个二进制来标示一个权限有仍是没有。

这样的话,一方面咱们只须要 4 个 bit 就能够存储权限信息,存储已是极限了。 另外一方面咱们经过位运算便可算出有没有权限,二进制运算在计算性能上也是极限了。

另外代码写起来,也会很是简洁,感兴趣的能够本身试试。

扩展: 假如文件权限不是只有两种可能,好比有三个取值怎么办?

状态机

什么是状态机

状态机表示若干个状态以及在这些状态之间的转移和动做等行为的数学模型。 通俗的描述状态机就是定义了一套状态変更的流程:状态机包含一个状态集合, 定义当状态机处于某一个状态的时候它所能接收的事件以及可执行的行为,执行完成后,状态机所处的状态。

咱们以现实中普遍使用的有限状态机(如下简称 FSM)为例进行讲解

FSM 应用很是普遍, 好比正则表达式的引擎,编译器的词法和语法分析,网络协议,企业应用等不少领域都会用到。

其中正则中使用的是一种特殊的 FSM, 叫 DFA(Deterministic Finite Automaton), 经过分裂树形式来运行。

为何要使用状态机

第一个缘由,也是你们感触最深的一个缘由就是经过状态机去控制系统内部的状态以及状态流转,逻辑会 比较清晰,尤为在逻辑比较复杂的时候,这种做用愈加明显。

第二个缘由是经过状态机,咱们能够实现数据以及系统的可视化。刚才我提到了正则表达式用到了状态机, 那么正则是否能够可视化呢? 答案是确定的,这里我介绍一个可视化正则表达式的一个网站。

实际业务中若是使用状态机来设计系统也能够进行可视化。相似这样子:

(图来自 statecharts.github.io/xstate-viz/)

能够看出,逻辑流转很是清晰,咱们甚至能够基于此进行调试。 固然,将它做为文档的一部分也是极好的,关于状态机的实际意义还有不少,咱们接下来举几个例子说明。

状态机的实际应用场景

匹配三的倍数

实现一个功能,判断一个数字是不是三的倍数。 数字能够很是大,以致于超过 Number 的表示范围, 所以咱们须要用 string 来存储。

一个简单直观的作法是直接将每一位都加起来,而后看加起来的数字是不是三的倍数。 可是若是数字大到必定程度,致使加起来的数字也超过了 Number 的表示范围呢?

一个方法是使用状态机来解决。

咱们发现一个数字除以 3 的余数一共有三种状态,即 0,1,2。 基于此咱们能够构建一个 FSM。 0,1,2 之间的状态流转也不可贵出。

举个例子,假设当前咱们是余数为 0 的状态,这时候再来一个字符。

  • 若是这个字符是 0,3 或者 9,那么咱们的余数仍是 0
  • 若是这个字符是 1,4 或者 7,那么咱们的余数是 1
  • 若是这个字符是 2,5 或者 8,那么咱们的余数仍是 2

用图大概是这个样子:

若是用代码大概是这样的:

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 来实现。

咱们很容易画出整理的流程图:

对于答题部分则稍微有一点麻烦,可是若是你用状态机的思惟去思考就很容易, 咱们不难画出这样的图:

JS 中有不少 FSM 的框架, 你们均可以直接拿过来使用。 笔者以前所在的项目 也用到了这样的技术,可是笔者是本身手写的简化版本 FSM,基本思想是一致的。

其余

事实上,还有不少例子能够举。

假设咱们后端服务器是一主一备,咱们将全部的数据都同时存储在两个服务器上。 假如某一天,有一份数据丢失了,咱们如何快速找到有问题的服务器。

这其实能够抽象成【Signle Number问题】。 所以不少时候,不是缺少应用算法的场景, 而是缺少这种将现实业务进行抽象为纯算法问题的能力。 咱们会被各类细枝末节的问题遮蔽双眼,没法洞察隐藏在背后的深层次的规律。

编程最难是抽象能力,前几年我写了一篇文章《为何咱们的代码难以维护》, 其中一个很是重要的缘由就是缺少抽象。

从如今开始,让咱们来锻炼抽象能力吧。

关注我

最近我从新整理了下本身的公众号,而且我还给他换了一个名字《脑洞前端》,它是一个帮助你打开大前端新世界大门的钥匙 🔑,在这里你能够听到新奇的观点,看到一些技术尝新,还会收到系统性总结和思考。

我会尽可能经过图的形式来阐述一些概念和逻辑,帮助你们快速理解,图解前端是个人目标。

以后个人文章同步到微信公众号 脑洞前端 ,您能够关注获取最新的文章,或者和我进行交流。

gongzhonghao

交流群

如今仍是初级阶段,须要你们的意见和反馈,为了减小沟通成本,我组建了交流群。你们能够扫码进入

QQ 群

qq-group-chat

微信群

JavaScript

(因为微信的限制,100 我的以上只能邀请加入, 你能够添加个人机器人回复“大前端”拉你进群)

相关文章
相关标签/搜索