1、数组
1.1 数组的定义
数组是若干个元素按照顺序排列存放的一个集合,并且每个元素至少存在一个 索引(index) 或者 关键字(key) 所标识,每个元素的位置都可以通过计算索引拿到。
一维数组:[1,2,3]; //数组的每一个元素是一个数据类型
二维数组:[["a","b","c"],[1,2,3],123]; //数组的每一个元素是一个一维数组
三维数组:[[["a","b","c"],[1,2,3]],[["a","b","c"],[1,2,3]]]; //数组的每一个元素是一个二维数组
1.2数组的优缺点
数组作为我们工作中最为常见的一种数据结构,其最大的特性莫过于高效的 查询 数据;
但是其缺点也是非常的明显,在进行 插入 和 删除 数据时,需要进行大量的数据移动补位消耗大量的时间。
2、链表
2.1 链表的定义
链表结构其实是内存内部的一种存储方式,链表则是把一系列节点串联起来,每个节点上至少包含两个部分: 数据域 与 指针域。
数据:保存数据;
指针:指向下一个节点的引用;
链表中的每个节点,通过指针域的值,形成一个线性结构。
其中,data中保存着数据,next保存着下一个链表的引用。
我们说 data2 跟在 data1 后面,而不是说 data2 是链表中的第二个元素。
我们将链表的尾元素指向了 null 节点,表示链接结束的位置。
由于链表的起始点的确定比较麻烦,因此很多链表的实现都会在链表的最前面添加一个特殊的节点,称为 头节点,表示链表的头部。
向链表中插入一个节点的效率很高,需要修改它前面的节点(前驱),使其指向新加入的节点,而将新节点指向原来前驱节点指向的节点即可。下面我将用图片演示如何在 data2 节点 后面插入 data4 节点。
从链表中删除一个节点,也很简单。只需将待删节点的前驱节点指向待删节点的,同时将待删节点指向null,那么节点就删除成功了。
2.2 链表的优缺点
因为链表是一种 松散 的结构体,所以当你想要找到其中的某一个节点时,只能够从 头节点 一级一级的往下找,但也因为这种松散的结构使得其进行 插入 和 删除 时只需要改变其 指针域 的指向即可
优点:适合动态插入和删除的应用场景;
缺点:不能快速的定位和随机访问数据。
3、数组和链表的对比
- 数组和链表都是线性数据结构
- 数组为静态结构,静态分配内存。链表支持动态分配内存
- 数组在数据储存时是一段连续的内存空间,链表是非连续的通过指针来串联
- 数组可以根据下标定位快速查找,链表则需要遍历查找
- 数组在插入和删除时会有大量的数据移动补位,链表只需要改变指针指向
4、JS中链表的实现
不同于new Array()
、new Set()
、new Map()
等数据结构,目前js
官方还没有为我们提供一个直接的链表API
实现。不过我们可以通过对象的方式去模拟出一个链表。
链表可以分为三类:
- 单向链表:线型数据结构,指针指向下一个节点,终点指向null
- 双向链表:可以往前或者往后添加节点,指针指向前一个节点和后一个节点
- 循环链表:循环链表的第一个节点指向最后一个节点,最后一个节点指向第一个节点(循环链表又可以划分为 “单向循环链表”和“双向循环链表”)
// 链表对象化,便于理解
const obj = {
data: 1,
next: {
data: 2,
next: {
data: 3,
next: null,
},
},
};
5、使用js实现将数组转为链表,及将链表转为数组
5.1 数组转链表
function arrayList(arr) {
if (arr.length === 0) {
return null
}
var nodes = []
for (var i = 0; i < arr.length; i++) {
var node = {}
node.value = arr[i]
node.next = null
nodes.push(node)
}
for (var i = 0; i < nodes.length - 1; i++) {
nodes[i].next = nodes[i + 1]
}
return nodes[0]
}
//不占用额外空间
function arrayList(arr) {
if (!arr.length) {
return null
}
let node;
let head = {
value: arr[0],
next: null
}
let preNode = head //preNode变量用来保存前一个节点
for (let i = 1; i < arr.length; i++) {
node = {
value: arr[i],
next: null
}
preNode.next = node //将前一个节点的next指向当前节点
preNode = node //将node赋值给preNode
}
return head
}
//递归
function arrayList(ary, start = 0) {
if (start === ary.length) {
return null
}
var node = {
value: ary[start],
next: null
}
var rest = arrayList(ary, start + 1)
node.next = rest
return node
}
5.2 链表转数组
function listArray(head) {
if (!head) {
return []
}
var result = []
var p = head
while (p) {
result.push(p.value)
p = p.next
}
return result
}
function listArray(head) {
if (!head) {
return []
}
var result = [head.value]
var restValues = list2array(head.next)
return result.concat(restValues)
}