栈
一个后进先出(后入栈的数据先出栈)的数据结构,JS中没有栈但可以使用 Array 模拟;
使用 NodeJs 断点调试(F5启动);
以下执行步骤为入栈了数据1、入栈了数据2,出栈了数据2,出栈了数据1;
const stack = []
stack.push(1) // 入栈
stack.push(2) // 入栈
const item1 = stack.pop() // 出栈
const item2 = stack.pop() // 出栈
以下执行步骤:入栈fun1、入栈fun2、入栈fun3、出栈fun3、出栈fun2、出栈fun1(后进先出)
const fun1 = () => {
fun2()
}
const fun2 = () => {
fun3()
}
const fun3 = () => {}
fun1()
十进制转换二进制
const num = 35
console.log(num.toString(2)) // 100011
队列
一种先进先出的数据结构;
以下执行步骤为:入栈了数据1、入栈了数据2,出栈了数据1,出栈了数据2(先进先出);
const queue = []
queue.push(1)
queue.push(2)
const item1 = queue.shift()
const item2 = queue.shift()
事件循环
当所有同步代码执行完会启动事件循环机制,而 WebApis 中的回调会推送到回调队列中,然后依此执行,先进先出。
链表
链表由多种元素组成,元素储存不连续,用 next 指针联系在一起;
数组非首尾增删需要移动元素,链表只需要更改 next 指向即可
例1
JavaScript 没有链表,但可以使用 Object 模拟
const a = { name: 'zhangsan' }
const b = { name: 'lisi' }
const c = { name: 'wangwu' }
const d = { name: 'zhaoliu' }
模拟链表
a.next = b
b.next = c
c.next = d
遍历
let pointer = a // 指针,初始化指向首个元素
while (pointer) {
console.log(pointer.name) // zhangsan lisi wangwu zhaoliu
pointer = pointer.next
}
c / d 之间插入 c2
const c2 = { name: 'sunqi' }
c.next = c2
c2.next = d
删除 c
b.next = d
使用函数删除 b
const deleteNode = function (node) {
node.name = node.next.name // 当前元素(b)的 name 设置为当前元素下一个元素(c)的 name
node.next = node.next.next // 将当前元素(b)的指针 next 指向当前元素后第二个元素(d)
}
deleteNode(b)
反转指针
const a = { val: 2 }
const b = { val: 4 }
const c = { val: 6 }
const d = { val: 8 }
a.next = b
b.next = c
c.next = d
d.next = null
步骤一:保存当前元素的下一位元素
步骤二:当前元素的指针指向上一位元素
步骤三:保存当前元素
步骤四:当前指针指向下一位元素
const reverseList = function (head) {
let p1 = head // 当前指针
let p2 = null // 当前元素上一个元素的指针
while (p1) {
const tmp = p1.next // 保存当前元素的下一位元素
p1.next = p2 // 当前元素的指针指向上一位元素
p2 = p1 // 保存当前元素
p1 = tmp // 当前指针指向下一位元素
}
return p2
}
reverseList(a)
通过指针进行遍历数据种的每一个 key 找到对象深层中的数据
const json = {
a: { b: { c: 1 } }
}
const keys = ['a', 'b', 'c'] // 数据种的 key 集
let point = json // 指针
keys.forEach(key => {
point = point[key]
})
console.log(point) // {c: 1}
树
分层数据的抽象模型;
如:DOM、级联选择、树形控件,js 中没有树、但可以用 Object 构建树:
const tree = {
val: 'a',
children: [
{
val: 'a-1',
children: [
{
val: 'a-1-1',
children: []
}
]
},
{
val: 'a-2',
children: [
{
val: 'a-2-1',
children: []
}
]
}
]
}
深度优先遍历: 尽可能深的搜索树的分支,即 children 递归
实现:
const dfs = root => {
console.log(root.val) // a a-1 a-1-1 a-2 a-2-1
root.children.forEach(e => {
dfs(e)
})
}
dfs(tree)
广度优先遍历: 先访问离根节点最近的节点
实现:
const bfs = root => {
const q = [root] // 队列
while (q.length) {
const p = q.shift() // 队头出队、删除第一项(返回第一项)
console.log(p.val) // a a-1 a-2 a-1-1 a-2-1
p.children.forEach(e => {
q.push(e)
})
}
}
bfs(tree)
二叉树 树中的节点最多只能有两个子节点; 使用 JS 模拟二叉树结构:
const tree = {
val: 'a',
left: {
val: 'a-left',
left: { val: 'a-left-left', left: null, right: null },
right: { val: 'a-left-right', left: null, right: null }
},
right: {
val: 'a-right',
left: { val: 'a-right-left', left: null, right: null },
right: { val: 'a-right-right', left: null, right: null }
}
}
先序遍历:
访问根节点、对根节点的左子树先序先序遍历、对根节点的右子树进行先序遍历;
输出顺序:a、a-left、a-left-left、a-left-right、a-right、a-right-left、a-right-right
const preOrder = root => {
if (!root) return
console.log(root.val)
preOrder(root.left)
preOrder(root.right)
}
preOrder(tree)
中序遍历:
对根节点左子树进行中序遍历、访问根节点、对根节点的右子树进行中序遍历;
输出顺序:a-left-left、a-left、a-left-right、a、a-right-left、a-right、a-right-right
const inOrder = root => {
if (!root) return
inOrder(root.left)
console.log(root.val)
inOrder(root.right)
}
inOrder(tree)
后序遍历:
根节点左子树进行后序遍历、根节点右子树进行后续遍历、访问根节点
输出顺序:a-left-left、a-left-right、a-left、a-right-left、a-right-right、a-right、a
const inOrder = root => {
if (!root) return
inOrder(root.left)
inOrder(root.right)
console.log(root.val)
}
inOrder(tree)