数据结构(基于JavaScript)—— 5种数据结构

该文章是本人在学习前端算法与数据结构面试:底层逻辑解读与大厂真题训练小册的笔记第一篇~

需要掌握的数据结构

  • 数组
  • 队列
  • 链表
  • 树(二叉树)

数组

何为数组?

必要条件:存储在连续的内存空间里

数组的创建

javascript 复制代码
const arr = [1, 2, 3, 4]   

创建指定长度的空数组,例如创建一个长度为7的空数组:

javascript 复制代码
const arr = new Array(7)

创建指定长度的全为1的数组,例如创建一个长度为7的全为1的数组:

javascript 复制代码
const arr = new Array(7).fill(1)

数组的遍历

  • 【推荐】for循环(从性能上看,for 循环遍历起来是最快的)

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

    javascript 复制代码
    arr.forEach((item, index)=> {
        // 输出数组的元素值,输出当前索引
        console.log(item, index)
    })
  • map方法

    map 方法在调用形式上与 forEach 无异,区别在于 map 方法会根据你传入的函数逻辑对数组中每个元素进行处理、进而返回一个全新的数组。

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

    这段代码就通过 map 来返回了一个全新的数组,数组中每个元素的值都是在其现有元素值的基础上+1后的结果。

初始化一个二维数组

推荐大家采纳的二维数组初始化方法非常简单(而且性能也不错)。直接用一个 for 循环来解决:

*for 循环中,每一次迭代我们都通过"[]"来创建一个新的数组,这样就不会出现使用fill([])导致的指向问题了。

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

栈和队列

在 JavaScript 中,栈和队列的实现一般都要依赖于数组,所以可以将栈和队列看作是"特别的数组"。

数组、栈、队列三者之间的区别在于 ------ 对数组的增删操作有不同的限制。

数组 ------ 灵活删减

数组中增加元素的三种方法:

  1. unshift(a) 将元素a添加到数组的头部

  2. push(a) 将元素a添加到数组的尾部

  3. splice(index, num, a) 添加元素到数组的任何位置

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

  1. shift() 删除数组头部的元素
  2. pop() 删除数组尾部的元素
  3. splice(index, num) 删除从index位置开始num个元素

栈(Stack)------只用 pop 和 push 完成增删的"数组" *堆放东西

栈是一种后进先出(LIFO,Last In First Out)的数据结构。

栈有两个特征:

  • 只允许从尾部添加元素 => 只能用 push 来添加元素
  • 只允许从尾部移除元素 => 只能用 pop 来移除元素

队列(Queue)------只用 push 和 shift 完成增删的"数组" *排队

队列是一种先进先出(FIFO,First In First Out)的数据结构。

队列有两个特征:

  • 只允许从尾部添加元素 => 只能用 push 来添加元素
  • 只允许从头部移除元素 => 只能用 shift 来移除元素

链表

链表和数组相似,它们都是有序的列表、都是线性结构(有且仅有一个前驱、有且仅有一个后继)。不同点在于,链表中,数据单位的名称叫做"结点",而结点的分布,在内存中可以是离散的。

例如,一个内容为1->2->3->4->5的链表,在内存中的形态可以是散乱如下的:

在链表中,每一个结点的结构都包括了两部分的内容:数据域和指针域(指向下一个结点)。

javascript 复制代码
{
   // 数据域
   val: 1,
   // 指针域,指向下一个结点
   next: {
       val:2,
       next: ...
   }
}   

*要想访问链表中的任何一个元素,我们都得从起点结点开始,逐个访问 next,一直访问到目标结点为止。

有时还会设定一个 head 指针来专门指向链表的开始位置:

链表节点的创建

创建列表结点,我们需要一个构造函数:

javascript 复制代码
function ListNode(val) {
    this.val = val;
    this.next = null;
}

然后我们就可以使用构造函数创建结点。传入val作为参数,并指定next即可

javascript 复制代码
const node = new ListNode(1)  
node.next = new ListNode(2)

这样我们就创建出了一个数据域值为1,next 结点数据域值为2的链表结点:

链表元素的添加

要想完成这个动作,我们需要变更的是前驱结点目标结点的 next 指针指向。

javascript 复制代码
// 如果目标结点本来不存在,那么记得手动创建
const node3 = new ListNode(3)     
// 把node3的 next 指针指向 node2(即 node1.next)
node3.next = node1.next
// 把node1的 next 指针指向 node3
node1.next = node3

链表元素的删除

删除的标准是:在链表的遍历过程中,无法再遍历到某个结点的存在。

其实也就是让这个结点脱离这个链表的关系链。

注意:在涉及链表删除操作的题目中,重点不是定位目标结点,而是定位目标结点的前驱结点

在js中的数组与链表

*JS 数组未必是真正的数组 JS 数组和常规数组的不同:

  1. 在js中,若在一个数组中只定义了一种类型元素,那么对应的就是连续内存
  2. 但如果在数组中定义了不同类型的元素,它对应的就是一段非连续的内存。此时,JS 数组不再具有数组的特征 ,其底层使用哈希映射分配内存空间,是由对象链表来实现的。

*链表的特点

【优点】高效的增删操作:添加和删除元素都不需要挪动多余的元素。O(1)

【缺点】麻烦的访问操作:当我们试图读取某一个特定的链表结点时,必须遍历整个链表来查找它。O(n) 而在数组中可以通过直接访问索引实现一步访问:O(1)

因此涉及到数组与链表两种数据结构选型时,要明确两者的特性:

  • 链表的插入/删除效率较高,而访问效率较低;

  • 数组的访问效率较高,而插入效率较低。

二叉树结构

  • 它可以没有根结点,作为一棵空树存在。
  • 如果它不是空树,那么必须由根结点、左子树和右子树组成,且左右子树都是二叉树
  • 且与普通的树不同,二叉树中会严格区分左右子树。

创建二叉树

创建二叉树,我们需要一个构造函数:

  • 数据域
  • 左侧子结点(左子树根结点)的引用
  • 右侧子结点(右子树根结点)的引用
javascript 复制代码
// 二叉树结点的构造函数
function TreeNode(val) {
    this.val = val;
    this.left = null;
    this.right = null;
}

当你需要新建一个二叉树结点时,直接调用构造函数、传入数据域的值val就行了:

javascript 复制代码
const node  = new TreeNode(1)

以这个结点为根结点,我们可以通过给 left/right 赋值拓展其子树信息,延展出一棵二叉树。

相关推荐
小曲曲27 分钟前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS2 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
yqcoder6 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript