前端常用数据结构一览

1 数组

1.1 初始化

fill方法,以及遍历赋值

js 复制代码
// 一维数组
const arr = (new Array(7)).fill(1)

// 二维数组
const arr = new Array(7)
for(let i = 0; i < arr.length; i++){
  arr[i] = []
}

1.2 遍历方法

for循环和for of,其中for性能会稍微好一点,因为它不涉及迭代协议的开销,而且不需要解析元素的迭代器。

js 复制代码
const arr = [2,4,6]
for(let i = 0; i < arr.length; i++){
    console.log(arr[i]) // 2,4,6
}
for(let item of arr){
    console.log(item) // 2,4,6
}

forEach和map,其中map方法会返回一个新数组

js 复制代码
const arr = [2,4,6]
const arr2 = arr.map(item => item+1)
arr2.forEach(item => {
    console.log(item) // 3,5,7
})

1.3 常用方法

  • push方法--添加到数组的尾部
  • pop方法--删除数组尾部元素
  • unshift方法--添加到数组的头部
  • shift方法--删除数组头部元素

slice方法(不改变原数组)用数组的某个片段切出新数组,可接受两个参数,比如 (1, 3),该方法会从开始参数选取元素,直到结束参数(不包括)为止。

js 复制代码
const arr = [1,3,5,7,9] 
const arr2 = arr.slice(1,3) // [3,5]

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

js 复制代码
const arr = [1,3,5]
arr.splice(1,0,8) // 1,8,3,5
arr.splice(0,2) // 3,5

更多数组方法见文章:JavaScript 数组方法总结js数组中常用的"冷门"方法:find,every,some,reduce

2 栈(Stack)

栈是一种后进先出(LIFO,Last In First Out)的数据结构,可以理解为只用 push 和 pop 完成增删的"数组"。

有两个特征:

  • 只允许从尾部添加元素(push)
  • 只允许从尾部取出元素(pop)

在栈元素出栈时,我们关心的是栈顶元素(数组的最后一个元素)

3 队列(Queue)

队列是一种先进先出(FIFO,First In First Out)的数据结构,可以理解为只用 push 和 shift 完成增删的"数组"

有两个特征:

  • 只允许从尾部添加元素(push)
  • 只允许从头部移除元素(shift)

队列元素出队时,我们关心的则是队头元素(数组的第一个元素)。

4 链表

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

结点的结构都包括了两部分的内容:数据域和指针域

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

数据域存储的是当前结点所存储的数据值,而指针域则代表下一个结点(后继结点)的引用。

4.1 链表结点的创建

js 复制代码
// 构造函数
function ListNode(val){
  this.val = val
  this.next = null
}
const node = new ListNode(1)
node.next = new ListNode(2)

4.2 链表元素的添加

js 复制代码
const node1 = new ListNode(1)
const node2 = new ListNode(2)
node1.next = node2

// 插入node3
const node3 = new ListNode(3)
node3.next = node1.next
node1.next = node3

4.3 链表元素的删除

js 复制代码
// 删除node3
node1.next = node3.next

在涉及链表删除操作的题目中,重点不是定位目标结点,而是定位目标结点的前驱结点。做题时,完全可以只使用一个指针(引用),这个指针用来定位目标结点的前驱结点。比如说咱们这个题里,其实只要能拿到 node1 就行了:

js 复制代码
const target = node1.next
node1.next = target.next

4.4 数组和链表解析

如果JS数组中定义了不同的数据类型

js 复制代码
const arr = ['abc' , 1, {x: 2}]

对应的就是一段非连续的内存。此时,JS 数组不再具有数组的特征,其底层使用哈希映射分配内存空间,是由对象链表来实现的。

相对于数组来说,链表有一个明显的优点,就是添加和删除元素都不需要挪动多余的元素

总的来说,链表的插入/删除效率较高,而访问效率较低;数组的访问效率较高,而插入效率较低。

5 二叉树

5.1 理解树结构

  • 树的层次计算规则:根结点所在的那一层记为第一层,其子结点所在的就是第二层,以此类推。
  • 结点和树的"高度"计算规则:叶子结点高度记为1,每向上一层高度就加1,逐层向上累加至目标结点时,所得到的的值就是目标结点的高度。树中结点的最大高度,称为"树的高度"。
  • "度"的概念:一个结点开叉出去多少个子树,被记为结点的"度"。
  • "叶子结点":叶子结点就是度为0的结点。最后一层的结点的度全部为0,所以这一层的结点都是叶子结点。

5.2 理解二叉树

  • 它可以没有根结点,作为一棵空树存在
  • 如果它不是空树,那么必须由根结点、左子树和右子树组成,且左右子树都是二叉树。
  • 注意,二叉树不能被简单定义为每个结点的度都是2的树。普通的树并不会区分左子树和右子树,但在二叉树中,左右子树的位置是严格约定、不能交换的。

上图二叉树结构编码实现

js 复制代码
const tree = {
  val: 'A',
  left: {
	val : 'B',
        left: {
            val: 'D'
  	},
  	right: {
            val: 'E'
  	},
  },
  right: {
  	val: 'C',
        left: {
            val: 'F'
        },
        right: {
            val: 'G'
        },
  },
}

5.3 二叉树编码实现

在 JS 中,二叉树使用对象来定义。它的结构分为三块:

  • 数据域
  • 左侧子结点(左子树根结点)的引用
  • 右侧子结点(右子树根结点)的引用

在定义二叉树构造函数时,我们需要把左侧子结点和右侧子结点都预置为空:

js 复制代码
// 二叉树构造函数
function TreeNode(val){
  this.val = val
  this.left = null
  this.right = null
}

const node = new TreeNode(1)

6 ES6中的Map

Map对象保存键值对,任何值(对象或者原始值) 都可以作为一个键或一个值。

构造函数Map可以接受一个数组作为参数

js 复制代码
// 使用数组初始化 Map 对象
const myMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2'],
  ['key3', 'value3']
]);

// 通过键获取值
console.log(myMap.get('key1')); // 输出 'value1'
console.log(myMap.get('key2')); // 输出 'value2'
console.log(myMap.get('key3')); // 输出 'value3'

6.1 Map和Object的区别

  • Map中的键值是有序的(队列 FIFO 原则),而添加到对象中的键则不是。
  • Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

6.2 Map对象的属性

size:返回Map对象中所包含的键值对个数

js 复制代码
const myMap = new Map([['a',1],['b', 2]])
console.log(myMap.size); // 2

6.3 Map对象的方法

  • set(key, val): 向Map中添加新元素
  • get(key): 通过键值查找特定的数值并返回
  • has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
  • delete(key): 通过键值从Map中移除对应的数据
  • clear(): 将这个Map中的所有元素删除
js 复制代码
const myMap = new Map([['a',1],['b', 2]])
myMap.set('c', 3)
console.log(myMap); // {'a' => 1, 'b' => 2, 'c' => 3}
console.log(myMap.get('b')) // 2
console.log(myMap.has('c')) // true
myMap.delete('b')
console.log(myMap); // {'a' => 1, 'c' => 3}
myMap.clear()
console.log(myMap); // {size: 0}

6.4 遍历方法

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员
js 复制代码
const myMap = new Map([['a',1],['b', 2]])
for(let m of myMap.keys()){
  console.log(m);
}
// a 
// b

for(let m of myMap.values()){
  console.log(m);
}
// 1 
// 2

// for...of...遍历m等同于使用map.entries()
// m 可以换成 [key, value]
for(let m of myMap.entries()){
  console.log(m);
}
// ['a', 1]
// ['b', 2]

myMap.forEach((value, key) => console.log(value, key))
// 1 'a'
// 2 'b'

6.5 Map与其他数据结构的互相转换

  • Map与对象的互换
  • JSON字符串要转换成Map可以先利用JSON.parse()转换成数组或者对象,然后再转换即可。
js 复制代码
// object => map
const obj = {a:1,b:2}
const arr = Object.entries(obj) // [['a',1],['b', 2]]
const myMap = new Map(arr)

// map => object
const myObj = {}
for(let [key, value] of myMap){
  myObj[key] = value
}
console.log(myObj); // {a: 1,b: 2}

7 ES6中的Set

Set对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set 本身是一个构造函数,用来生成Set 数据结构。Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

7.1 Set中的特殊值

Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等,遵循的是严格相等性(===)。有几个特殊值需要特殊对待:

  • +0 与 -0 在存储判断唯一性的时候是恒等的
  • undefined 与 undefined 是恒等的
  • NaN 与 NaN 是不恒等的,但是在 Set 中认为 NaN 与 NaN 相等,所有只能存在一个
js 复制代码
const mySet = new Set([+0,-0,undefined,undefined,NaN,NaN])
console.log(mySet); // {0, undefined, NaN}

7.2 Set实例对象的属性

  • size:返回Set实例的成员总数。
js 复制代码
console.log(mySet.size); // 3

7.3 Set实例对象的方法

  • add(value):添加某个值,返回 Set 结构本身(可以链式调用)。
  • delete(value):删除某个值,删除成功返回true,否则返回false。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。
js 复制代码
const mySet = new Set()
mySet.add(1)
mySet.add('a')
console.log(mySet); // {1, 'a'}
mySet.delete(1)
console.log(mySet) // {'a'}
console.log(mySet.has('a')) // true
mySet.clear()
console.log(mySet) // {size: 0}

7.4 遍历方法

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回键值对的遍历器。
  • forEach():使用回调函数遍历每个成员。

由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

js 复制代码
const mySet = new Set([1, 2, 'a', 'b'])
for(let s of mySet.keys()){
  console.log(s);
}
// 1, 2, a, b

for(let s of mySet.entries()){
  console.log(s);
}
// [1, 1]
// [2, 2]
// ['a', 'a']
// ['b', 'b']

mySet.forEach((value, key) => console.log(value, key))
// 1 1
// 2 2
// a a
// b b

7.5 Set 对象作用

  • 数组去重(利用扩展运算符)
js 复制代码
const mySet = new Set([1, 2, 2, 3])
const arr = [...mySet]
console.log(arr); // [1, 2, 3]
  • 合并两个set对象
js 复制代码
const mySet = new Set([1, 2, 2, 3])
const otherSet = new Set([7, 3, 2, 3])
const arr = [...mySet, ...otherSet]
console.log(arr); // [1, 2, 3, 7, 3, 2]
  • 交集
js 复制代码
const mySet = new Set([1, 2, 2, 3])
const otherSet = new Set([7, 3, 2, 3])
const newSet = new Set([...mySet].filter(item => otherSet.has(item)))
console.log(newSet); // {2, 3}
  • 差集
js 复制代码
const mySet = new Set([1, 2, 2, 3])
const otherSet = new Set([7, 3, 2, 3])
const newSet = new Set([...mySet].filter(item => !otherSet.has(item)))
console.log(newSet); // {1}
相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax