面试被问到扁平数据结构转Tree这个问题怎么办?,CSS的标准文档流

23 阅读3分钟

js基础

1)对js的理解? 2)请说出以下代码输出的值? 3)把以下代码,改写成依次输出0-9 4)如何区分数组对象,普通对象,函数对象 5)面向对象、面向过程 6)面向对象的三大基本特性 7)XML和JSON的区别? 8)Web Worker 和webSocket? 9)Javascript垃圾回收方法? 10)new操作符具体干了什么呢? 11)js延迟加载的方式有哪些? 12)WEB应用从服务器主动推送Data到客户端有那些方式?

js基础.PNG

前16.PNG

开源分享:docs.qq.com/doc/DSmRnRG…

[
  {id: 1, name: 'A', parentId: null},
  {id: 2, name: 'B', parentId: 1},
  {id: 3, name: 'C', parentId: 1},
  {id: 4, name: 'D', parentId: 2},
  {id: 5, name: 'E', parentId: 2},
  {id: 6, name: 'F', parentId: 3},
  {id: 7, name: 'G', parentId: 3},
  {id: 8, name: 'H', parentId: 4},
]

在这个例子中,我们可以看到每个节点都有一个唯一的id,一个名称和一个parentId,parentId是指它的父节点的id,如果没有父节点,parentId为null。让你看看你们这帮小学生的思路和我的有何不同。

function buildTree(nodes) {
  const tree = []
  const map = {}

  nodes.forEach(node => {
    map[node.id] = node
    node.children = []
  })

  nodes.forEach(node => {
    if (node.parentId) {
      map[node.parentId].children.push(node)
    } else {
      tree.push(node)
    }
  })

  return tree
}

看到这里,你不禁感慨,这代码写得真好!这不是小学生可以想到的,只有我这样的神仙才能想到啊!(滑稽)

不过,哪有这么简单的事呢?咳咳,这个代码还可以再优化哦!比如,我们可以减少循环次数,使用哈希表来存储节点,甚至还能使用并发机制来提高代码性能!

当然,如果你在面试的时候讲出了这些高级操作,面试官肯定会惊为天人,毫不犹豫地把 offer 递给你了。当然,如果面试官是我这种幽默的人,听完你的回答,估计会冒出一句:“哇,好厉害啊!可以详细讲讲怎么优化吗?”

减少循环次数

在之前的代码中,我们使用了两个forEach循环来遍历节点数组。但是,这会导致多次循环遍历同一个数组,从而降低性能。为了减少循环次数,我们可以使用单个for循环来遍历节点数组。

以下是修改后的代码:

function buildTree(nodes) {
    
  // 这里新增一个排序,避免节点顺序不对照成的问题
  nodes.sort((a, b) => a.id - b.id)
  
  const tree = []
  const map = {}

  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    map[node.id] = node
    node.children = []
    const parentId = node.parentId

    if (!parentId) {
      tree.push(node)
    } else {
      if (!map[parentId]) {
        map[parentId] = { id: parentId, children: [] }
      }
      map[parentId].children.push(node)
    }
  }

  return tree
}


我们将两个forEach循环替换为单个for循环。我们还添加了一个条件语句,以检查节点是否有父节点。如果没有父节点,我们将其添加到树的根节点中,否则我们将其添加到其父节点的children数组中。

使用哈希表

在之前的代码中,我们使用一个映射来存储节点。但是,对于较大的数据集,使用哈希表可以更快地查找节点。因此,我们可以使用JavaScript中的对象来代替映射。

以下是修改后的代码:

function buildTree(nodes) {
  const tree = {}
  const map = {}

  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    map[node.id] = node
    node.children = []
    const parentId = node.parentId

    if (!parentId) {
      tree[node.id] = node
    } else {
      if (!map[parentId]) {
        map[parentId] = { id: parentId, children: [] }
      }
      map[parentId].children.push(node)
    }
  }

  return Object.values(tree)
}

我们将tree从数组改为对象,并使用node.id作为键。我们还将映射从Map对象改为普通对象。这样可以使代码更加简洁,并减少了一些额外的函数调用。

将数据进行排序

如果数据是按照id顺序排列的,可以使用二分查找来查找父节点,从而减少遍历的次数。因此,我们可以将节点数组按照id排序,以优化性能。

以下是修改后的代码:

function buildTree(nodes) {
  const tree = {}
  const map = {}
  nodes.sort((a, b) => a.id - b.id)

  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    map[node.id] = node
    node.children = []
    const parentId = node.parentId

    if (!parentId) {
      tree[node.id] = node
    } else {
      const parentIndex = binarySearch(nodes, parentId)

      if (parentIndex !== -1) {
        const parentNode = nodes[parentIndex]

        if (!parentNode.children) {
          parentNode.children = []
        }

        parentNode.children.push(node)
      } else {
        if (!map[parentId]) {
          map[parentId] = { id: parentId, children: [] }
        }
        map[parentId].children.push(node)
      }
    }
  }

  return Object.values(tree)
}

function binarySearch(arr, id) {
  let start = 0
  let end = arr.length - 1

  while (start <= end) {
    const mid = Math.floor((start + end) / 2)
    const midId = arr[mid].id

    if (midId === id) {
      return mid
    } else if (midId < id) {
      start = mid + 1
    } else {
      end = mid - 1
    }
  }

  return -1
}

我们将节点数组按照id排序,并使用二分查找来查找父节点的索引。如果能够找到父节点,我们将当前节点添加到父节点的children数组中。否则,我们将当前节点添加到一个临时的映射中,并在之后的遍历中再次查找其父节点。这个版本的代码可以在数据集较大时更快地查找父节点。

避免重复遍历

如果同一个父节点有多个子节点,我们在找到该父节点之后,可以缓存其索引,以避免重复遍历。我们可以在map对象中添加一个__index属性,以存储该节点在节点数组中的索引。

以下是修改后的代码:

function buildTree(nodes) {
  const tree = {}
  const map = {}
  nodes.sort((a, b) => a.id - b.id)

  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    map[node.id] = node
    node.children = []
    const parentId = node.parentId

    if (!parentId) {
      tree[node.id] = node
    } else {
      let parentIndex = map[parentId]?.__index

      if (parentIndex === undefined) {
        parentIndex = binarySearch(nodes, parentId)

        if (parentIndex !== -1) {
          map[parentId] = nodes[parentIndex]
          map[parentId].__index = parentIndex
        } else {
          if (!map[parentId]) {
            map[parentId] = { id: parentId, children: [] }
          }
        }
      }

      if (parentIndex !== undefined) {
        const parentNode = nodes[parentIndex]

        if (!parentNode.children) {
          parentNode.children = []
        }

        parentNode.children.push(node)
      } else {
        map[parentId].children.push(node)
      }
    }
  }

  return Object.values(tree)
}

function binarySearch(arr, id) {
  let start = 0
  let end = arr.length - 1



### 最后

面试一面会问很多基础问题,而这些基础问题基本上在网上搜索,面试题都会很多很多。最好把准备一下常见的面试问题,毕竟面试也相当与一次考试,所以找工作面试的准备千万别偷懒。面试就跟考试一样的,时间长了不复习,现场表现肯定不会太好。表现的不好面试官不可能说,我猜他没发挥好,我录用他吧。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**

**96道前端面试题:**

- ![](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/3583f637c9414cb6834a040b46500038~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772525776&x-signature=FPIA3XwBliD0JUM2l41%2BSUbeZzI%3D)

**常用算法面试题:**

- ![](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/ea51ed21409d4eab9454038b718b4a0a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772525776&x-signature=98%2BQWM9jocS%2FFxOr%2FqcTN5V%2BRPs%3D)

**前端基础面试题:**
内容主要包括**HTML,CSS,JavaScript,浏览器,性能优化**

- ![](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/c0fae0008ac947d289010c0c5e297de4~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772525776&x-signature=%2BgYnk49KJgOtCJwzHaqScUqeEMM%3D)