数组
建议这么创建数组
const arr = (new Array(7)).fill(1)
遍历数组方法:
- for
- forEach
- map
const len = arr.length
for(let i=0;i<len;i++) { }
此外还有二维数组也叫矩阵
可以这么初始化二维数组
const len = arr.length
for(let i=0;i<len;i++) {
arr[i] = []
}
栈和队列
JS 中都依靠数组来实现
首先需要了解数组中增加元素的三种方法
- push 添加到尾部
- unshift 添加到头部
- splice 添加到任意位置
还有数组删除元素的三种方法
- shift 删除头部
- pop 删除尾部
- splice 删除任意位置
栈指的是后进先出的数据结构
// 遍历一个栈
while(stack.length) {
// 单纯访问栈顶元素(不出栈)
const top = stack[stack.length-1]
// 将栈顶元素出栈
stack.pop()
}
队列是先进先出
// 遍历一个队列
while(queue.length) {
// 单纯访问队头元素(不出队)
const top = queue[0]
// 将队头元素出队
queue.shift()
}
链表
与数组的区别是每个节点在内存中可以是离散的
{
// 数据域
val: 1,
// 指针域,指向下一个结点
next: {
val:2,
next: ...
}
}
初始化一个链表,需要一个构造函数
function ListNode(val) {
this.val = val;
this.next = null;
}
const node = new ListNode(1)
在两个结点间添加元素,假设添加如下结点
const node3 = new ListNode(3)
把node3的 next 指针指向 node2(即 node1.next),再把node1的 next 指针指向 node3
node3.next = node1.next
node1.next = node3
那么如何删除一个元素呢,还是以删除 node3 为例
node1.next = node3.next
可以看到关键在于定位 node3 的前驱结点,所以只需要如下操作
const target = node1.next // node3
node1.next = target.next
链表和数组的比较
- 增删:链表 - O(1)、数组 - O(n),添加和删除元素都不需要挪动多余的元素
- 访问:链表 - O(n)、数组 - O(1)
原本数组删除或增加元素的复杂度应该是O(n)
但 JS 中不一定是。 JS比较特别。如果我们在一个数组中只定义了一种类型的元素,比如:
const arr = [1,2,3,4]
它是一个纯数字数组,那么对应的确实是连续内存。 但如果我们定义了不同类型的元素:
const arr = ['haha', 1, {a:1}]
它对应的就是一段非连续的内存。此时,JS 数组不再具有数组的特征,其底层使用哈希映射分配内存空间,是由对象链表来实现的。
树与二叉树
首先需要了解树的关键特性和重点概念
- “度”的概念:一个结点开叉出去多少个子树,被记为结点的“度”。
- “叶子结点”:叶子结点就是度为0的结点。
- 树的层次计算规则:根结点所在的那一层记为第一层,其子结点所在的就是第二层,以此类推。
- 结点和树的“高度”计算规则:叶子结点高度记为1,每向上一层高度就加1,逐层向上累加至目标结点时,所得到的的值就是目标结点的高度。树中结点的最大高度,称为“树的高度”。
二叉树:
- 它可以没有根结点,作为一棵空树存在
- 如果它不是空树,那么必须由根结点、左子树和右子树组成,且左右子树都是二叉树
// 二叉树结点的构造函数
function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
新建二叉树结点
const node = new TreeNode(1)
遍历一个二叉树的顺序主要有以下三种
- 根结点 -> 左子树 -> 右子树
- 左子树 -> 根结点 -> 右子树
- 左子树 -> 右子树 -> 根结点
分别对应了二叉树的先序遍历、中序遍历和后序遍历规则,所谓的“先序”、“中序”和“后序”,“先”、“中”、“后”其实就是指根结点的遍历时机
先序遍历
// 所有遍历函数的入参都是树的根结点对象
function preorder(root) {
if(!root) {
return
}
console.log('当前遍历的结点值是:', root.val)
preorder(root.left)
preorder(root.right)
}
中序遍历
function inorder(root) {
if(!root) {
return
}
inorder(root.left)
console.log('当前遍历的结点值是:', root.val)
inorder(root.right)
}
后序遍历
function postorder(root) {
if(!root) {
return
}
postorder(root.left)
postorder(root.right)
console.log('当前遍历的结点值是:', root.val)
}