前端面试常见笔试算法题(二)

283 阅读3分钟

数组转树

题目:

const input = [
  {id: 1, parentId: null},
  {id: 2, parentId: 1},
  {id: 3, parentId: 2},
];

转换成:
const output = {
  id: 1,
  children: [
    {
      id: 2,
      children: [
        {
          id: 3,
        },
      ],
    },
  ],
};

解题:首先用一个map将数组所有项保存下来,key是每一项的id,value是每一项本身。再循环这个map,查找每一项的parentId,如果不存在则这一项是根,存在就把这一项push到parentId对像的children中。

function arrToTree(arr) {
  const result = [];

  // 先将数组每一项保存到map中
  const map = arr.reduce((acc, cur) => {
    acc[cur.id] = cur;
    return acc;
  }, {});

  // 循环每一项,放到父元素的children中
  for (const key in map) {
    const item = map[key];
    if (item.parentId === null) { // 根直接push到result
      result.push(item);
    } else {
      const parent = map[item.parentId]; // 获取这一项的父对象
      if (parent) {
        parent.children = parent.children || [];
        parent.children.push(item); // 把这一项push到父对象的children中
      }
    }
  }

  return result;
}

控制请求最大并发数

function control(list, num) {
  let activeCount = 0; // 当前并发数

  const fn = () => {
    // 当前并发数大于给定的并发数 或 请求列表为空时,直接返回
    if (activeCount > num || !list.length) return;
    const f = list.shift();
    activeCount++; // 并发数+1
    f().finally(() => {
      activeCount--; // 执行完一个后,并发数-1,执行下一个
      fn();
    });
  };

  const max = Math.min(list.length, num); // 兼容num > list.length,这种情况下,最大并发数为list.length
  for (let i = 0; i < max; i++) {
    fn();
  }
}

给定一个数组 nums 和一个目标值 target,在该数组中找出和为目标值的两个数d

例如 nums: [8, 2, 6, 5, 4, 1, 3] ; target:7;return [2,5]

function twoNumber(nums, target) {
  const map = new Map(); // 使用map保存已经遍历过的元素

  for (let i = 0; i < nums.length; i++) {
    const el = nums[i];
    if (map.has(target - el)) { // 如果map中存在目标值,说明找到了
      return [target - el, el];
    } else {
      map.set(el, el); // 保存遍历过的元素
    }
  }

  return [];
}

输入一个字符串,找到第一个不重复字符的下标。例如:'abcabcde' => 6

题解:先遍历整个字符串将出现的个数用map保存下来,再遍历这个map找到第一个不重复的字符即可。

function findOneStr(str) {
  const map = new Map(); // map的key是字符本身,value是重复的次数
  for (const v of str) {
    const count = map.get(v); 
    map.set(v, count ? count + 1 : 1); // 如果已存在则加1,不存在设为1
  }

  for (const [k, v] of map) {
    if (v === 1) { // 找到第一个不重复的字符
      return str.indexOf(k);
    }
  }

  return -1;
}

斐波那契数列: 一个人正在爬楼梯,楼梯有 n 级台阶。每次这个人可以爬1级或2级台阶。请问这个人有多少种不同的方法爬到楼梯的顶部?

题解:爬到第 n 级台阶的方法数等于爬到第 n−1 级台阶的方法数与爬到第 n−2 级台阶的方法数之和。用公式表示就是:f(n)=f(n−1)+f(n−2)

function climbStairs(n) { 
    if (n <= 2) { return n; } //边界条件,当只有一层或两层时,对应只有一种和两种方法
    let prev1 = 1, prev2 = 2; // 记录前两次的数据
    for (let i = 3; i <= n; i++) { 
        const curr = prev1 + prev2; 
        prev1 = prev2; //更新前两次数据
        prev2 = curr; 
    } 
   return prev2; 
}

翻转链表

题解:通过迭代的方法,逐个翻转链表的节点。使用三个指针:prevcurrnextprev 指向已经翻转的部分,curr 指向当前正在处理的节点,next 用于临时保存下一个节点。

function reverseList(head) {
  let prev = null;
  let curr = head;

  while (curr !== null) {
    let next = curr.next; // 临时保存下一个节点
    curr.next = prev; // 翻转当前节点的指针
    prev = curr; // 将prev指针移动到当前节点
    curr = next; // 将curr指针移动到下一个节点
  }

  return prev; // prev最终会指向新链表的头节点
}