JavaScript版数据结构与算法(一)栈、队列、链表、集合、树

一、前言

为什么要学习数据结构与算法?最重要的就是面试要考算法,另外就是如果在实际工作当中,能够使用算法优化代码,会提升代码质量和运行效率,作为一名前端人员可能在实际中用的并不是特别多。数据结构与算法是分不开的,数据结构是计算机存储、组织数据的方式,算法是一系列解决问题的清晰指令,程序就是数据结构+算法。算法刷题大家都知道,就是力扣。刷题顺序推荐按类型刷题,比如栈相关的题,一次刷好几道,巩固巩固。刷题过程中需要重点关注的是通用套路、时间/空间复杂度分析和优化。其实这跟高中初中做数学题很像,有通用的套路可循,但是需要多复习,多看错题,做题的时候尽量把这道题考察的知识点都总结出来。
要学习的数据结构大致分为下面几类:

  • 有序:栈、队列、链表
  • 无序:集合、字典
  • 有相互连接关系:树、堆(特殊的树)、图

学习的算法大致分为下面几类:

  • 链表:遍历链表、删除链表节点。
  • 树、图:深度/广度优先遍历。
  • 数组:冒泡/选择/插入/日并/快速排序、顺序/二分搜索。

关于时间复杂度:
用O()函数表示,定性描述算法运行时间

  • O(1)
    代码只会执行一次,没有任何循环
    let i = 1;
    i += 1;
    
  • O(n)
    for循环中的代码会执行n次
    for (let i = 0; i < n; i += 1) {
    	console. log(i);
    }
    
  • O(1) + O(n) = O(n)
    代码上下顺序执行是两个时间复杂度相加,只算增长趋势较大的时间复杂度,增长趋势较小的时间复杂度可以忽略
    let i = 1;
    i += 1;
    for (let j = 0; j < n; j += 1) {
    	console. log(i);
    }
    
  • O(n) * O(n) = O(n^2)
    两个方法嵌套的时间复杂度需要两个时间复杂度相乘
    for (let i=0;i<n;i+=1){
    	for (let j = 0; j < n; j += 1) {
    		console. log(i, j);
    	}
    }
    
  • O(logN)
    let i = 1;
    while(i < n){
    	console.log(i);
    	i *= 2;
    }
    

空间复杂度
用O()函数表示
算法在运行过程中临时占用存储空问大小的量度,越小越好

  • O(1)
    只有一个变量
    let i = 0;
    i += 1
    
  • O(n)
    在内存中声明了n个变量
    const list = [];
    for (let i=0; i<n;i+=1){
    	list.push(i);
    }
    
  • O(n^2)
    就是一个矩阵
    const matrix = [];
    for(let i = 0;i < n;i += 1){
    	matrix.push([])
    	for(let j=0; j<n; j +=1){
    		matrix[i].push(j)
    	}
    }
    

二、栈

栈就是一个后进先出的数据结构
在这里插入图片描述
javascript中没有栈这种数据结构,但是可以用Array实现栈,模仿栈的操作。

const stack = [];
// 入栈
stack.push(1);
stack.push(2);
// 出栈
// pop()将最后的元素删除,并且返回
const item1 = stack.pop()
console.log(item1)
// 出栈
const item2 = stack.pop()
console.log(item2)

什么场景下用栈?
所有后进先出的场景,例如:

  • 十进制转二进制
    需要用十进制数不断除以2,并且倒序取余数,后算出的余数要排在前面,因此可以将余数依次存入栈中,再出栈,就可以实现余数倒序输出
    在这里插入图片描述
/**
 * 十进制转二进制
 * */
// 1、数字除以2 得到商和余数
// 2、余数push到栈中
// 3、商继续除以2 得商和余数
// 4、余数继续入栈
const trans = function (num) {
    const stack = []
    let n = num;
    while (n > 0) {
        // 获取商
        const shang = Math.floor(n / 2);
        // 获取余数
        const yushu = n % 2;
        // 余数入栈
        stack.push(yushu);
        // n重新赋值
        n = shang;
    }
    return stack.reverse().join('')
}
  • 判断字符串的括号是否有效
    在敲代码的时候,代码编辑器经常要判断括号是否正常闭合,无效的括号会给我们错误提示。这个算法就是编辑器中常用的判断括号是否有效的算法。
    越靠后的左括号,对应的右括号越靠前。
    左括号入栈,右括号出栈,最后栈空了就是合法的。
    在这里插入图片描述

  • 函数调用堆栈
    最后调用的函数,是最先执行完的
    greeting() start -> [1] 操作 -> sayHi() start -> sayHi() end -> [2] 操作 -> greeting() end -> [3] 操作
    JS解释器使用栈来控制函数的调用顺序。
    在这里插入图片描述

三、队列

先进先出
javascript中没有队列,但能用数组实现队列

// 创建队列
const queue = [];
// 入队
queue.push(1)
queue.push(2)
// 出队
const item1 = queue.shift()
console.log(item1)
const item2 = queue.shift()
console.log(item2)

应用场景:
所有先进先出的场景
食堂打饭
js异步任务队列(事件循环)
异步任务会放在任务队列中,先放进队列的先执行
计算最近请求次数

四、链表

多个元素组成的链表
但是元素存储不连续,用 next 指针连在一起。

数组 VS 链表
数组:增删非首尾元素时往往需要移动元素。
链表:增删非首尾元素,不需要移动元素,只需要更改 next 的指向即可。

javascript中可以用 object 模拟链表

前端中的链表–原型链
原型链的本质是链表。
原型链上的节点是各种原型对象,比如Function.prototype, Object.prototype…
原型链通过_proto_属性连接各种原型对象。
原型链知识点
如果 A 沿着原型链能找到 B.prototype,那么A instanceof B 为true。
如果在 A 对象上没有找到x 属性,那么会沿着原型链找 x 属性。
面试题一
instanceof 的原理,并用代码实现。

// 如果 A 沿着原型链能找到 B.prototype,那么A instanceof B 为true。
function instanceOf(A, B){
    let p = A;
    while(p){
        if(p == B.prototype) return true;
        p = p.__proto__;
    }
    return false;
}

五、集合

集合是一种无序且唯一的数据结构。
栈、队列、链表都是有序的数据结构,并且元素都是可以重复的。
前端中的集合: Set
常用操作:
数组去重
[...new Set([1, 2, 3, 1, 2])]
判断某元素是否在集合中
set.has(3)
求两个集合的交集

const set = new Set([1,2,3,2,1])
const set2 = new Set([2,3,4])
// 筛选出set中有,并且set2里面也有的元素
const set3 = new Set([...set].filter(s=>set2.has(s)))
console.log(set3)

字典
与集合类似,字典也是一种存储唯一值的数据结构,但它是以键值对的形式来存储。
ES6 中有字典,名为 Map。
字典的常用操作:键值对的增删改查。

// 增删改查
const m = new Map();
m.set('a', 'aa')
m.set('b', 'bb')
m.delete('b')
// 清空
// m.clear();
// 改直接覆盖set
m.set('a', '啊啊')

// 求两个数组中都存在的元素,要求去重
const arr1 = [1,2, 2,3,4]
const arr2 = [2,3,2,4]
// 字典中的key也是唯一的,所以遍历数组一创建字典就不会有重复的key
const map = new Map();
arr1.forEach(a=>{
    map.set(a, true)
})
const res = []
arr2.forEach(a=>{
    if(map.get(a)) {
        res.push(a)
        map.delete(a)
    }
})
console.log(res)

六、树

树是一种分层数据的抽象模型
前端的树:DOM树、级联选择、树形控件
JS中没有树,只能用Array和Object模拟
树的常用操作:深度/广度优先遍历、二叉树的先中后序遍历。
深度优先遍历:尽可能深的搜索树的分支
广度优先遍历:先访问离根节点最近的节点
下面一图,左侧是深度优先遍历的访问顺序,右侧是广度优先遍历的访问顺序
在这里插入图片描述
深度优先遍历算法口诀 (最为重要)
1、访问根节点
2、对根节点的 children 挨个进行深度优先遍历。

// 深度优先遍历
const tree={
    val: 'a',
    children:[
        {
            val: 'b',
            children:[
                {
                    val: 'd',
                    children:[]
                },
                {
                    val: 'e',
                    children:[]
                }
            ]
        },
        {
            val: 'c',
            children:[
                {
                    val: 'f',
                    children:[]
                },
                {
                    val: 'g',
                    children:[]
                }
            ]
        }
    ]
}


const dfs = (root) =>{
    // 访问根节点
    console.log(root);
    // 对根节点的children依次进行深度优先遍历
    root.children.forEach(dfs);
}

console.log(dfs(tree))

广度优先遍历算法口诀
1、新建一个队列,把根节点入队。
2、把队头出队并访问。
3、把队头的 children 挨个入队。
4、重复第二、三步,直到队列为空。

// 广度优先遍历
const tree = {
    val: 'a',
    children: [
        {
            val: 'b',
            children: [
                {
                    val: 'd',
                    children: []
                },
                {
                    val: 'e',
                    children: []
                }
            ]
        },
        {
            val: 'c',
            children: [
                {
                    val: 'f',
                    children: []
                },
                {
                    val: 'g',
                    children: []
                }
            ]
        }
    ]
}
const bfs = (root) => {
    // 新建一个队列 根节点入队
    const q = [root]
    while (q.length > 0) {
        // 队头出队并访问
        const n = q.shift();
        console.log(n)
        // 队头的children挨个入队
        n.children.forEach(c => q.push(c));
    }
}
bfs(tree)

树在前端中的应用

  • 访问json数据中的所有节点
    使用深度优先遍历实现,深度优先遍历就是先访问根节点,然后对每个根节点进行深度优先遍历
// 访问json中所有节点值
const json = {
    a: {
        b: {
            c: 3
        }
    },
    d: [1, 2]
}

// 深度优先遍历 使用path记录每个节点的路径
const dfs = (n, path) =>{
	// 访问当前节点
    console.log(n, path)
    // 使用Object.keys遍历n的所有子节点
    Object.keys(n).forEach(k=>{
        dfs(n[k], path.concat(k))
    })
}

dfs(json, [])

在这里插入图片描述

七、二叉树

树中每个节点最多只能有两个子节点。
在JS中通常用 Object 来模拟二叉树。

const binaryTree = {
    val: '1',
    left: {
        val: '2',
        left: null,
        right: null
    },
    right: {
        val: '3',
        left: null,
        right: null
    }
}

二叉树的遍历主要有三种:先序遍历、中序遍历、后序遍历
二叉树的先序遍历
根->左->右
在这里插入图片描述

1、访问根节点。
2、对根节点的左子树进行先序遍历。
3、对根节点的右子树进行先序遍历。

const binaryTree = {
    val: '1',
    left: {
        val: '2',
        left: {
            val: '4',
            left: null,
            right: null
        },
        right: {
            val: '5',
            left: null,
            right: null
        }
    },
    right: {
        val: '3',
        left: {
            val: '6',
            left: null,
            right: null
        },
        right: {
            val: '7',
            left: null,
            right: null
        }
    }
}

const preorder = root => {
    if (!root) return;
    console.log(root.val);
    preorder(root.left);
    preorder(root.right);
}
preorder(binaryTree)

二叉树的中序遍历
左->根->右
在这里插入图片描述
1、对根节点的左子树进行中序遍历。
2、访问根节点。
3、对根节点的右子树进行中序遍历。
首先对于整棵树而言,根节点是5,要先对于左子树2进行中序遍历,找2 的左子树,是1,对1中序遍历,1没有左子树,所以1是最先访问的节点
当前节点是1,访问1的根节点,就是2,第二个访问的是2
然后对2这棵树的右子树进行中序遍历,即4,先找4的左子树,即3,访问3,即第三个访问的是3
3访问完,要访问3的根节点,第四个访问的是4
4没有右子树,所以结束访问,2也结束了访问
此时就要访问2节点所在的根节点,即5,第五个访问的是5
在找5的右子树,进行中序遍历,它的右子树6没有左子树,所以先访问根节点6,即第六个访问的是6
访问6之后需要访问6树的右子树,即7,因此第七个访问的是7

二叉树的后序遍历
左->右->根
在这里插入图片描述
1、对根节点的左子树进行后序遍历。
2、对根节点的右子树进行后序遍历。
3、访问根节点。

// 将二叉树定义为一个独立可复用的模块
const binaryTree = require('./binaryTree')
const postorder = (root) => {
    if (!root) return;
    // 1 左子树进行后序遍历
    postorder(root.left);
    // 2 右子树进行后序遍历
    postorder(root.right);
    // 3 访问根节点
    console.log(root.val);
}
postorder(binaryTree)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/301788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在win10和Linux上配置SSH 无密码登录

文章目录 一、用途二、在本地机器上使用ssh-keygen产生公钥私钥对1&#xff09;在Linux (或macOS) 上产生SSH公私钥的方法2&#xff09;在win10上产生SSH公私钥的方法a&#xff09;检查windows 本地是否安装有sshb&#xff09;在本地生成SSH密钥对&#xff08;公钥和私钥&#…

Java TBA访问NetSuite Restlet时的403错误

本周有同学问为啥Java访问NetSuite Restlet时&#xff0c;按照知识会之前的文章分享&#xff0c;会一直报403 INVALID_LOGIN_ATTEMPT错误。 https://nk-community.blog.csdn.net/article/details/131399801https://nk-community.blog.csdn.net/article/details/131399801原因是…

jenkins 参数化构建过程,jenkins下拉框选择要部署项目,jenkins部署java微服务项目,jenkins部署微服务

1. jenkins部署demo 优化点&#xff1a; 选择丢弃旧的构建&#xff0c;最大个数可以选择3或者5个&#xff0c;如果微服务&#xff0c;十个jar,占用很多空间&#xff0c;多来几次部署&#xff0c;硬盘满了 2. 选择参数构建&#xff0c;需要部署哪个项目 名称可以设置为你的项目…

CANFDLog-OTL 的UDS诊断和刷写功能应用简介

随着汽车电控系统比重增加和车辆下线自动化程度的提高&#xff0c;企业生产节拍逐渐加快&#xff0c;整车下线的稳定可靠越来越依靠下线诊断系统。 下线诊断流程如下: 1.程序刷写 对控制器进行程序刷写&#xff0c;支持S19/HEX/BIN等格式 2.防盗匹配 钥匙防盗匹配功能和智能钥匙…

vue-springboot基于java的实验室安全考试系统

本系统为用户而设计制作实验室安全考试系统&#xff0c;旨在实现实验室安全考试智能化、现代化管理。本实验室安全考试管理自动化系统的开发和研制的最终目的是将实验室安全考试的运作模式从手工记录数据转变为网络信息查询管理&#xff0c;从而为现代管理人员的使用提供更多的…

list-watch和节点亲和性和node亲和性

k8s的集群调度 scheduler:负责调度资源&#xff0c;把pod调度到node节点 预算策略 优先策略 1、list-watch k8s集群当中&#xff0c;通过list-watch的机制进行每个组件的协作&#xff0c;保持数据同步&#xff0c;每个组件之间解耦 kubectl配置文件&#xff0c;向APIserv…

Docker极限压缩tar镜像,20G变10G

&#x1f388;普通打包命令&#xff1a; docker save -o [压缩包名字] [镜像名字] &#x1f451;极限压缩命令&#xff1a; docker save [镜像名字] | gzip> [压缩包名字] 先上主题&#xff0c;放上两条命令&#xff0c;请随意取用。 对于动态修改的环境&#xff0c;一…

网络安全学习资源

好久没写博客了&#xff0c;记录一些宝藏学习资源&#xff0c;不定时更新 Regex Learn - Step by step, from zero to advanced. 这是一个我认为最好的正则表达式学习网站&#xff0c;很多正则表达式学习资料都只提供了一个概念&#xff0c;但是正则表达式需要大量的练习&#…

视频怎么做成活码?活码二维码制作的步骤

现在很多人会将自己拍摄的视频做成二维码图片之后分享给其他人&#xff0c;这样就可以通过一张二维码让其他人同时扫码获取视频内容&#xff0c;使用起来更加的方便&#xff0c;那么视频二维码制作的步骤是什么样的呢&#xff1f;下面就让小编来通过本文给大家分享一下二维码生…

个性化语音生成:五种基于Python的方法

引言 随着人工智能技术的不断发展&#xff0c;语音生成已经成为一个热门的研究领域。个性化语音生成技术可以根据用户的需求和特点&#xff0c;生成具有高度相似度的语音&#xff0c;广泛应用于语音助手、虚拟人物、语音合成等领域。本文将介绍五种基于Python的个性化语音生成…

欧盟GDPR 和车联网个人数据保护指南

1. 前言 本文主要讨论三部法律。分别如下&#xff1a; 1.1 GDPR 欧盟《一般数据保护条例》&#xff08;General Data Protection Regulation&#xff0c;简称GDPR&#xff09;在2018年5月25日生效。 在一些媒体的报道中&#xff0c;这一保护条例被称为“史上最严数据保护条…

速卖通店铺销量飙升:掌握自养号测评(补单),轻松提升销售量

很多卖家在经营速卖通店铺时&#xff0c;都希望能提高自己店铺的曝光率。但对于一些新手卖家来说&#xff0c;可能不太清楚曝光率的具体含义以及如何提升。那么&#xff0c;让我们一起来探讨一下这个问题。 曝光率&#xff0c;简而言之&#xff0c;是指您的店铺和产品展示给顾…

计算机毕业设计------JSP实现的图书管理系统

项目介绍 本项目为后台管理项目&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,系统设置,管理员管理,办证费用管理,书架管理,读者类型管理,读者管理,图书管理,图书类型管理,图书借阅,图书归还,图书续借,图书查询等功能。 环境需要 1.运行环境&#xff1a;最好…

ABAP 工单状态读取

CDS代码 CDS代码如下&#xff0c;可自行转换成OPEN SQL代码 AbapCatalog.sqlViewName: ZPPV024 AbapCatalog.compiler.compareFilter: true AbapCatalog.preserveKey: true AccessControl.authorizationCheck: #NOT_REQUIRED EndUserText.label: 读取生产工单状态 define vie…

计算机毕业设计-----ssm停车位租赁系统

项目介绍 该系统采用了经典的springmvc&#xff0c;spring&#xff0c;mybatis的框架组合&#xff0c;对于物业公司来说&#xff0c;有助于管理车位信息。系统分为了两个角色&#xff1a;车主和租客。 车主主要功能包括&#xff1a; 停车位信息 停车位列表 添加停车位 租赁合…

Raw图像处理软件分享:Capture One Pro 23中文 for mac

Capture One Pro 23具有高级色彩管理工具&#xff0c;可以帮助用户精确控制图像的颜色和色调。用户可以对颜色进行校正、调整色彩平衡和饱和度&#xff0c;以及使用颜色分级工具进行精细的色彩分离和调整。调整和编辑工具&#xff1a;Capture One Pro 23提供了各种基本的调整和…

JavaScript基本语法

文章目录 1. JavaScript 是什么1.1 JavaScript 和 HTML 和 CSS 之间的关系1.2 JavaScript 运行过程1.3 JavaScript 的组成 2. JavaScript 的书写形式2.1 行内式2.2 内嵌式2.3 外部式 3. 变量的使用3.1 静态变量和动态变量 4. 基本数据类型4.1 undefined 未定义数据类型4.2 null…

加密世界危机四伏,普通用户该如何应对钓鱼陷阱

据区块链安全公司 Scam Sniffer 称&#xff0c;加密货币网络钓鱼活动在 2023 年有所增加&#xff0c;诈骗者利用钱包盗取恶意软件从受害者那里窃取了近 3 亿美元。金额很惊人是不是&#xff1f;只是没想到素以小心谨慎为口号冲浪的我也栽了一回。还原一下事情经过&#xff1a; …

【LV12 DAY11 硬件控制原理】

CPU通过读写控制器的寄存器从而达到控制硬件的目的

4.2V线性500mA充电管理芯片WT4054

4.2V线性500mA充电管理芯片WT4054 WT4054&#xff0c;一款强大而小巧的锂电池充电IC&#xff0c;为你的便携式设备提供无忧充电。其SOT-23-5L封装小巧轻便&#xff0c;所占空间极小&#xff0c;而其强大的功能将为你的设备提供稳定、高效的电量供应。 这款充电器拥有众多令人惊…