Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/面试/面经

7字节-抖音团购-全部

3 分钟阅读 · Note

目录树 578 篇

          • 0 面试技巧&HR面⬇️
          • 1磐松资产-前端实习-一面
          • 2字节跳动-豆包前端实习-全部
          • 3百度-前端实习面试-一面
          • 5光庭-前端实习-一面
          • 6懂车帝-前端实习-一面
          • 7字节-抖音团购-全部
          • 8腾讯-前端-全部
          • 9字节&美团-笔试
          • 10 美团-一面
          • 11 B站-一面
          • 12 腾讯-一面-企业微信团队
          • 13 腾讯-一面-腾讯金融科技
          • 14 腾讯-二面-腾讯金融科技
          • 15 美团-一面-核心本地商业
          • 16 斑马智行-全部
          • 17 字节-一面-飞书
          • 18 美团-二面-核心本地商业
          • 19 心影随形-全部
          • 20 thunderbit-全部
          • 21 converge ai-全部
          • 22 字节-国际化广告创意与品牌
        • 可投递企业
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗0 面试技巧&HR面⬇️同一路径↗10 美团-一面同一路径↗11 B站-一面同一路径↗12 腾讯-一面-企业微信团队同一路径↗13 腾讯-一面-腾讯金融科技同一路径↗14 腾讯-二面-腾讯金融科技同一路径
  • 7字节-抖音团购-全部

7字节-抖音团购-全部

> Last Format Time:6/12/2026 21:03:57

一面

提问

主要是围绕着项目来进行的提问,有一些基础的问题。

  • 5个视频是怎么减少重排的:这里没有做优化,就是直接卸载了,每次卸载完再重新装载
  • 视频是怎么进行管理的:使用队列
  • localStorage的大小是多少:5-10MB

代码

将上面的source转化为下面的形式

我自己的实现方式是先遍历一次,将根节点插入到res数组中,然后将这些插入的项移除,然后遍历剩余的部分,将其中pid===id的项添加到父项的children数组中,之后,重复移除,添加的过程,直到res的长度为零(但这其实是我之后想到的,根本不是我面试的想到的,有很多细小的错误,导致我整体的答题效果不是很好,尤其是我一开始的递归思路,source最开始是Array,其Item是对象,自然就不能使用forEach,我没有察觉到这一点。。。)

> ⚠️ 纠错:「source最开始是Array,其Item是对象,自然就不能使用forEach」—— 这个说法有误。Array.prototype.forEach 可以遍历数组中的对象,forEach 是数组方法,数组中存放对象是完全可以使用 forEach 遍历的。这里可能是混淆了其他概念。

上面的思路也是不对的,仔细想想,第一次将根结点取出来了,第二次将一层的节点取出来了,第二层的结点你该如何加入到第一层的节点中呢,所以还是要使用map

// 实现函数将source转换为result
const source = [
  { id: 0, name: "a" },
  { id: 1, name: "b", pid: 0 },
  { id: 2, name: "c", pid: 0 },
  { id: 3, name: "d", pid: 1 },
  { id: 4, name: "e", pid: 2 },
  { id: 5, name: "f" },
  { id: 6, name: "g", pid: 3 },
  { id: 7, name: "h", pid: 5 },
  { id: 8, name: "i", pid: 7 },
];
const result = [
  {
    id: 0,
    name: "a",
    children: [
      {
        id: 1,
        name: "b",
        pid: 0,
        children: [
          {
            id: 3,
            name: "d",
            pid: 1,
            children: [{ id: 6, name: "g", pid: 3 }],
          },
        ],
      },
      { id: 2, name: "c", pid: 0, children: [{ id: 4, name: "e", pid: 2 }] },
    ],
  },
  {
    id: 5,
    name: "f",
    children: [
      { id: 7, name: "h", pid: 5, children: [{ id: 8, name: "i", pid: 7 }] },
    ],
  },
];

答案:

法一:

function arrayToTree(source) {
  const tree = []; // 存放最终的树形结构,存放树的根结点,就是没有pid的结点
  const map = {};  // 哈希表,用于存储 id 到节点的映射

  // 1. 第一次遍历:将所有节点存入 map,并初始化 children 数组
  source.forEach(item => {
    map[item.id] = { ...item, children: [] };
  });

  // 2. 第二次遍历:建立父子关系
  source.forEach(item => {
    const node = map[item.id]; // 当前节点
    
    // 如果存在 pid,说明它不是根节点
    if (item.pid !== undefined && map[item.pid]) {
      // 找到父节点,将当前节点 push 进父节点的 children
      map[item.pid].children.push(node);
    } else {
      // 如果不存在 pid 或找不到父节点,说明它是根节点
      tree.push(node);
    }
  });

  return tree;
}

// 测试数据
const source = [
  { id: 0, name: "a" },
  { id: 1, name: "b", pid: 0 },
  { id: 2, name: "c", pid: 0 },
  { id: 3, name: "d", pid: 1 },
  { id: 4, name: "e", pid: 2 },
  { id: 5, name: "f" },
  { id: 6, name: "g", pid: 3 },
  { id: 7, name: "h", pid: 5 },
  { id: 8, name: "i", pid: 7 },
];

const result = arrayToTree(source);
console.log(JSON.stringify(result, null, 2));

法一的Map实现:

const fn = (source) => {
    let root = []
    let map = new Map()

    // 遍历source,将每个item添加到map中
    source.forEach((item) => {
        // map.set(item.pid, item) 这里将对象展开,添加了一个children属性
        map.set(item.id, { ...item, children: [] })
    })

    console.log('map: ', map)

    map.forEach((item) => {
        console.log(item)
    })

    map.forEach((item) => {
        if (item.pid === undefined) {
            // 如果是根节点,直接添加到root中
            root.push(map.get(item.id))
        } else {
            // 如果不是根节点,将当前item添加到父节点的children中
            console.log(item.pid)
            map.get(item.pid).children.push(map.get(item.id))
        }
    })

    console.log('root: ', root)
    return root
}

fn(source)
// root: [
//     { id: 0, name: 'a', children: [[Object], [Object]] },
//     { id: 5, name: 'f', children: Object },
// ]

法二:

function arrayToTreeRecursive(source, pid = undefined) {
  const tree = [];
  
  // 筛选出所有父级 ID 等于当前 pid 的节点
  source.forEach(item => {
    if (item.pid === pid) {
      // 找到子节点后,递归查找该子节点的子节点
      const children = arrayToTreeRecursive(source, item.id);
      if (children.length > 0) {
        tree.push({ ...item, children });
      } else {
        tree.push({ ...item });
      }
    }
  });
  
  return tree;
}

// 调用方式:默认从 pid 为 undefined 或 null 的根节点开始找
// 注意:你的数据中根节点没有 pid 字段,所以默认传 undefined 即可匹配
const result = arrayToTreeRecursive(source);
console.log(JSON.stringify(result, null, 2));

加分项:

// 在解法一返回前,增加这一步清洗(可选)
function cleanEmptyChildren(node) {
  if (node.children && node.children.length === 0) {
    delete node.children; // 删除空的 children 数组
  } else if (node.children) {
    node.children.forEach(cleanEmptyChildren); // 递归清洗子节点
  }
}

// 使用示例:
// result.forEach(cleanEmptyChildren);

二面

  • 时间:4/22/26(周三)

提问

全部是围绕着项目来的

代码

  • Promise.All() > ⚠️ 纠错:正确写法为 Promise.all()(首字母小写)
  • 大数相加(转为字符串)

两道题都没有写出来,都是写了大体的结构,具体的细节不记得了,反正是刚背完就忘了。。。


三面

提问

  • 围绕着上一段的实习经历来的,我的回答重点在于skill的构建,至于说我的挑战是什么,我也不到。。。

代码

  • 没有写,只需要你复述出来就行
  • 寻找两个链表中的第一个重复的节点
  • 图片的压缩算法、如果你是计算机系统的开发者,你该如何将图片存入外存,你准备使用什么数据结构来存呢

复盘

这三个面试整体上,与之前的日常实习相比来讲,更加的实用主义,直接就是考察代码与项目。现在是周日(26号),算上今天,我已经休息了4天了,啥也没干,就是玩游戏哈哈哈,该复出了。

面试没过,但是被其他部门捞走了,AI全栈开发,我也不会啊,宝子们。。。

一开始的时候我就觉得为什么这次面的这么快,30多分钟就结束了,没想到是被刷了。。。