前言

打算从0开始,用JavaScript刷题。
买了修言老师的小册子:前端算法与数据结构面试:底层逻辑解读与大厂真题训练
希望每天下班之后可以看一篇,争取早日上岸吧~

数据结构

我的想法:不同语言对于数据结构的实现有着不一样的特点。
之后的学习过程应该面向JavaScript的语言特性,针对前端的面试题来学习。

需要需要掌握的数据结构:

  • 数组
  • 队列
  • 链表

数组——重要的开箱即用数据类型

大多数的语言都天然地对数组有着原生的表达,JavaScript 亦然。这意味着我们可以对数组做到“开箱即用”,而不必自行模拟实现,非常方便。

初始化

在算法题中,推荐使用构造函数的方法初始化一个数组:

//推荐方法
const arr=new Array()
//该方法等价于
const arr = []
//使用构造函数创建数组的主要原因是,
//往往是因为我们有“创造指定长度的空数组”这样的需求。需要多长的数组,就给它传多大的参数.
const arr = new Array(7)

数组遍历

for方法

// 获取数组的长度
const len = arr.length
for(let i=0;i<len;i++) {// 输出数组的元素值,输出当前索引console.log(arr[i], i)
}

forEach方法

通过取 forEach 方法中传入函数的第一个入参和第二个入参,我们可以取到数组每个元素的值及其对应索引。
第一个入参:值
第二个入参:下标

arr.forEach((item, index)=> {// 输出数组的元素值,输出当前索引console.log(item, index)
})

map方法

map 方法在调用形式上与 forEach 无异。
区别在于 map 方法会根据你传入的函数逻辑对数组中每个元素进行处理、进而返回一个全新的数组
所以其实 map 做的事情不仅仅是遍历,而是在遍历的基础上“再加工”
当我们需要对数组内容做批量修改、同时修改的逻辑又高度一致时,就可以调用 map 来达到我们的目的:

const newArr = arr.map((item, index)=> {// 输出数组的元素值,输出当前索引console.log(item, index)// 在当前元素值的基础上加1return item+1
})
//这段代码就通过 map 来返回了一个全新的数组,数组中每个元素的值都是在其现有元素值的基础上+1后的结果。

二维数组的初始化

不可用的fill方法及其局限性

 const arr =(new Array(7)).fill([])//生成了7行0列的一个空二维数组

缺陷在于,当你想修改某个坑里的数组的值时:

arr[0][0] = 1

原因
当你给fill传递一个入参时,如果这个入参的类型是引用类型,那么 fill 在填充坑位时填充的其实就是入参的引用
也就是说看似我们给7个坑位各初始化了一个数组。这7个数组对应了同一个引用、指向的是同一块内存空间,它们本质上是同一个数组。 因此当你修改第0行第0个元素的值时,第1-6行的第0个元素的值也都会跟着发生改变。

正确的初始化方法

const len = arr.length
for(let i=0;i<len;i++) {// 将数组的每一个坑位初始化为数组arr[i] = []
}

二维数组的遍历

for循环

// 缓存外部数组的长度
const outerLen = arr.length
for(let i=0;i<outerLen;i++) {// 缓存内部数组的长度const innerLen = arr[i].lengthfor(let j=0;j<innerLen;j++) {// 输出数组的值,输出数组的索引console.log(arr[i][j],i,j)}
}

添加和删除元素的方法

添加方法

  • unshift 方法-添加元素到数组的头部
const arr = [1,2]
arr.unshift(0) // [0,1,2]
  • push 方法-添加元素到数组的尾部
const arr = [1,2]
arr.push(3) // [1,2,3]
  • splice 方法-添加元素到数组的任何位置
const arr = [1,2]
arr.splice(1,0,3) // [1,3,2]

splice方法详解

第一个入参是起始的索引值,
第二个入参表示从起始索引开始需要删除的元素个数
从第三个位置开始的入参,都代表着需要添加到数组里的元素的值

删除方法

  • shift 方法-删除数组头部的元素
const arr = [1,2,3]
arr.shift() // [2,3]
  • pop 方法-删除数组尾部的元素
const arr = [1,2,3]
arr.pop() // [1,2]
  • splice 方法-删除数组任意位置的元素
const arr = [1,2,3]
arr.splice(1,1)//[1,3]
//删除从索引1位置的1个元素

总结

创造指定长度的空数组

const arr = new Array(7)

创建一个长度确定、同时每一个元素的值也都确定的数组

const arr = (new Array(7)).fill(1)
//[1,1,1,1,1,1,1]

当我们需要对数组内容做批量修改、同时修改的逻辑又高度一致时

const newArr = arr.map((item, index)=> {// 输出数组的元素值,输出当前索引console.log(item, index)// 在当前元素值的基础上加1return item+1
})

在 JavaScript 中,栈和队列的实现一般都要依赖于数组,大家完全可以把栈和队列都看作是“特别的数组”。

栈(Stack)——只用 pop 和 push 完成增删的“数组”

栈是一种后进先出的数据结构。
只允许从尾部添加或删除元素。


// 初始状态,栈空
const stack = []
// 入栈过程
stack.push('东北大板')
stack.push('可爱多')
stack.push('巧乐兹')
stack.push('冰工厂')
stack.push('光明奶砖')// 出栈过程,栈不为空时才执行
while(stack.length) {// 单纯访问栈顶元素(不出栈)const top = stack[stack.length-1]console.log('现在取出的冰淇淋是', top)  // 将栈顶元素出栈stack.pop()
}// 栈空
stack // []

队列(Queue)——只用 push 和 shift 完成增删的“数组”

队列是先进先出的一种数据结构
队首出,队尾进。
(哈哈哈哈突然想到了星巴克补货都是先进先出呀。

const queue = []
queue.push('小册一姐')
queue.push('小册二姐')
queue.push('小册三姐')  while(queue.length) {// 单纯访问队头元素(不出队)const top = queue[0]console.log(top,'取餐')// 将队头元素出队queue.shift()
}// 队空
queue // []

链表

链表结点的创建

function ListNode(val) {this.val = val;this.next = null;
}
const node = new ListNode(1)
node.next = new ListNode(2)

JS 数组未必是真正的数组
JS比较特别。如果我们在一个数组中只定义了一种类型的元素const arr=[1,2,3],那么他是连续内存的。
如果定义了不同的元素const arr=[1,''hahah',{a:1}],则是一段非连续的内存。其底层使用哈希映射分配内存空间,是由对象链表来实现的。

二叉树的常用属性

二叉树第i层上最多有 2i−12^{i-1}2i−1个节点
深度为h的二叉树中至多含有2h−12^{h}-12h−1个节点
若在任意一棵二叉树中,有n0n_0n0​个叶子节点,有n2n_2n2​个度为2的节点,则必有n0=n2+1n_0=n_2+1n0​=n2​+1
具有n个节点的完全二叉树深为log2x+1log_2x+1log2​x+1(其中x表示不大于n的最大整数)

二叉树的节点

// 二叉树结点的构造函数
function TreeNode(val) {this.val = val;this.left = null;this.right=null;
}

二叉树遍历

  • 先序遍历:根结点 -> 左子树 -> 右子树(先遍历根节点)
  • 中序遍历:左子树 -> 根结点 -> 右子树 (根节点在中间遍历)
  • 后序遍历:左子树 -> 右子树 -> 根结点(最后遍历根节点)
  • 层序遍历:一层层遍历。

先序遍历

// 所有遍历函数的入参都是树的根结点对象
function preorder(root) {// 递归边界,root 为空if(!root) {return }// 输出当前遍历的结点值console.log('当前遍历的结点值是:', root.val)  // 递归遍历左子树 preorder(root.left)  // 递归遍历右子树  preorder(root.right)
}

中序遍历

// 所有遍历函数的入参都是树的根结点对象
function inorder(root) {// 递归边界,root 为空if(!root) {return }// 递归遍历左子树 inorder(root.left)  // 输出当前遍历的结点值console.log('当前遍历的结点值是:', root.val)  // 递归遍历右子树  inorder(root.right)
}

后序遍历

// 所有遍历函数的入参都是树的根结点对象
function postorder(root) {// 递归边界,root 为空if(!root) {return }// 递归遍历左子树 postorder(root.left)  // 递归遍历右子树  postorder(root.right)// 输出当前遍历的结点值console.log('当前遍历的结点值是:', root.val)  }

层序遍历

前端算法笔记-数据结构基础相关推荐

  1. 一周刷爆LeetCode,算法da神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记

    一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记 教程与代码地址 P1 出圈了!讲课之外我们来聊聊 ...

  2. asp子窗口读取父窗口数据_算法与数据结构基础 - 数组(Array)

    数组基础 数组是最基础的数据结构,特点是O(1)时间读取任意下标元素,经常应用于排序(Sort).双指针(Two Pointers).二分查找(Binary Search).动态规划(DP)等算法.顺 ...

  3. 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority Queue)

    堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...

  4. 由任意二叉树的前序遍历序列和中序遍历序列求二叉树的思想方法_算法与数据结构基础 - 二叉树(Binary Tree)...

    二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...

  5. 《算法笔记》——基础篇习题选择结构

    <算法笔记>--基础篇习题 第二章 C/C++快速入门--2.3选择结构 [习题A] 一元二次方程求根 Problem Description Thinking Notes Code Im ...

  6. 算法与数据结构基础<一>----线性查找法

    开篇: 对于数据结构及算法的学习在17年时就已经在博客中开了专栏: 但是!!!感觉学得有点零散,有c版本的,也有java版本的,没成体系,当然其效果也并没达到自己满意的效果,基于此,这里准备重新开个专 ...

  7. 算法与数据结构基础四----数据结构基础之动态数据结构基础:链表上

    接着上一次https://www.cnblogs.com/webor2006/p/15195969.html的数据结构继续往下学习,这次会进入一个非常重要的数据结构的学习----链表,这个是未来学习复 ...

  8. 先来先服务算法代码_程序员算法与数据结构基础中的基础,栈与递归

    在此之前,我们介绍了动态规划.深度优先搜索等基础算法,但是,有部分好友评论说,难度太难了,我们知道动态规划的自顶向下跟深度优先搜索一般都用递归实现,今天我们就先来讲讲算法与数据结构中,基础中的基础递归 ...

  9. ssm插入数据时候栈溢出_程序员算法与数据结构基础中的基础,栈与递归

    在此之前,我们介绍了动态规划.深度优先搜索等基础算法,但是,有部分好友评论说,难度太难了,我们知道动态规划的自顶向下跟深度优先搜索一般都用递归实现,今天我们就先来讲讲算法与数据结构中,基础中的基础递归 ...

最新文章

  1. ImageNet 存在十万标签错误,你知道吗?
  2. ‘numpy.float64‘ object cannot be interpreted as an integer
  3. mysql使用group by实现组内排序实战
  4. 埋在 MySQL 数据库应用中的17个关键问题!
  5. 08 redis数据类型:hash
  6. 日志汇总:logging、logger
  7. hdu 1760 A New Tetris Game(搜索博弈)
  8. Swift 在 GAIA 平台云端一体化的探索
  9. 分别标记区段的 飞鸽传书 左、右端
  10. linux开源软件_为什么要使用Linux和开源软件?
  11. python 滚动条方法_Python OpenCV 使用滑动条来调整函数参数的方法
  12. 【Linux】Linux的关机和虚拟机克隆、快照
  13. 17.Zend_View
  14. 【手写数字识别】基于matlab知识库手写体数字识别【含Matlab源码 311期】
  15. 华为android已锁定,教你如何查看华为手机是否己解锁bootloader
  16. 星光不问赶路人,时光不负奇舞团
  17. Part-02/通过案例学爬虫(豆瓣电影TOP250)
  18. 剪不断理还乱--C#重载/重写/覆盖
  19. 自动驾驶仿真软件SCANeR studio(四)scenario模式下脚本
  20. python--测试使用不同的方式计算位涡平流项的差异

热门文章

  1. c语言随机数教学成果与反思,教学成果报告-渤海大学.pdf
  2. 读王竹峰老师 《一个数据库十年老兵的思考与总结》 有感
  3. 解密:IT运维艺术之集群(4层AND7层)
  4. 读书笔记 - 《史蒂夫·乔布斯传》
  5. openEuler 知:repo
  6. Delphi XE10.x实现Android下Https双向认证
  7. 无法打开网页版晓木虫怎么办?
  8. LSM303AGR姿态传感器 risc-v Sifive learn inventor基础之硬件i2c与LSM303AGR通信
  9. [转贴]变态的C自增
  10. 【YOLOv5】手把手教你使用LabVIEW ONNX Runtime部署 TensorRT加速,实现YOLOv5实时物体识别(含源码)