【js】-【数组】-笔记
声明:本笔记是根据掘金小册总结的,如果要学习更多细节,请移步https://juejin.cn/book/6844733800300150797
1 数组
1.1 一维创建
const arr = []
const arr = new Array()
指定长度
const arr = new Array(7)
创建一个长度确定、同时每一个元素的值也都确定的数组
const arr = (new Array(7)).fill(1)
1.2 一维遍历
for
循环
// 获取数组的长度
const len = arr.length
for(let i=0;i<len;i++) {
// 输出数组的元素值,输出当前索引
console.log(arr[i], i)
}
forEach
方法
arr.forEach((item, index)=> {
// 输出数组的元素值,输出当前索引
console.log(item, index)
})
map
方法
在遍历的基础上“再加工”
const newArr = arr.map((item, index)=> {
// 输出数组的元素值,输出当前索引
console.log(item, index)
// 在当前元素值的基础上加1
return item+1
})
map
来返回了一个全新的数组,数组中每个元素的值都是在其现有元素值的基础上+1后的结果
1.3 二维数组
初始化一个二维数组
const len = arr.length
for(let i=0;i<len;i++) {
// 将数组的每一个坑位初始化为数组
arr[i] = []
}
let dp = Array.from(new Array(3), () => new Array(2).fill(1));
console.log(dp);
[[1, 1], [1, 1], [1, 1]]
1.4 二维数组遍历
# 缓存外部数组的长度
const outerLen = arr.length
for(let i=0;i<outerLen;i++) {
# 缓存内部数组的长度
const innerLen = arr[i].length
for(let j=0;j<innerLen;j++) {
# 输出数组的值,输出数组的索引`const arr = [1,2]
arr.push(3) // [1,2,3]`
console.log(arr[i][j],i,j)
}
}
2 数组增加与删除方法
2.1 数组中增加元素
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]
第一个入参是起始的索引值,第二个入参表示从起始索引开始需要删除的元素个数,第三个参数是传入值
2.2 数组中删除元素
- 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]
3 栈、队列
栈和队列的实现一般都要依赖于数组
两者的区别在于,它们各自对数组的增删操作有着不一样的限制。
3.1 栈
只允许从尾部添加元素
只允许从尾部取出元素
// 初始状态,栈空
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 // []
3.2队列
只用 push 和 shift 完成增删的“数组”
const queue = []
queue.push('小册一姐')
queue.push('小册二姐')
queue.push('小册三姐')
while(queue.length) {
// 单纯访问队头元素(不出队)
const top = queue[0]
console.log(top,'取餐')
// 将队头元素出队
queue.shift()
}
// 队空
queue // []
4 链表
链表中,数据单位的名称叫做“结点”,而结点和结点的分布,在内存中可以是离散的。
要想访问链表中的任何一个元素,我们都得从起点结点开始,逐个访问 next,一直访问到目标结点为止。为了确保起点结点是可抵达的,我们有时还会设定一个 head 指针来专门指向链表的开始位置:
- 创建链表结点,需要一个构造函数:
function ListNode(val) {
this.val = val;
this.next = null;
}
- 使用构造函数创建结点
const node = new ListNode(1)
node.next = new ListNode(2)
数据域值为1,next 结点数据域值为2
3. 任意两结点间插入一个新结点
我们需要变更的是前驱结点和目标结点的 next 指针指向
# 如果目标结点本来不存在,那么记得手动创建
const node3 = new ListNode(3)
# 把node3的 next 指针指向 node2(即 node1.next)
node3.next = node1.next
# 把node1的 next 指针指向 node3
node1.next = node3
- 链表元素的删除
直接让它的前驱结点 node1 的 next 指针跳过它、指向 node3 的后继
node1.next = node3.next
可以只使用一个指针,这个指针用来定位目标结点的前驱结点。
// 利用 node1 可以定位到 node3
const target = node1.next
node1.next = target.next
相对于数组来说,链表有一个明显的优点,就是添加和删除元素都不需要挪动多余的元素
在链表中,只要明确了要插入/删除的目标位置,增删操作的复杂度是常数级别的复杂度,表示为 O(1)。
但是链表也有一个弊端:当我们试图读取某一个特定(第i个)的链表结点时,必须遍历整个链表来查找它,表示为 O(n)。
但在数组中,我们直接访问索引、可以做到一步到位,这个操作的复杂度会被降级为常数级别O(1)
链表的插入/删除效率较高,而访问效率较低;数组的访问效率较高,而插入效率较低