【前端数据结构】

96 阅读4分钟

一个后进先出(后入栈的数据先出栈)的数据结构,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)