返回

令人头疼的十道大厂面试题

前端

又到了跳槽季,程序员们摩拳擦掌,准备迎接新的挑战。面试是跳槽过程中必不可少的一环,而大厂的面试更是出了名的难。为了帮助大家顺利通过面试,我们总结了十道经典的大厂面试题,让你充分了解面试官的套路,助你顺利拿到offer!

  1. 算术交换

算术交换是一种通过算术运算过程中的技巧,可以巧妙地将两个值进行互换。但是,这个方法有个缺点就是变量数据溢出。因为JavaScript能存储数字的精度范围是 -2^53 到 2^53。所以,加法或减法运算可能会导致数字溢出。

let a = 1;
let b = 2;

a = a + b;
b = a - b;
a = a - b;

console.log(a); // 2
console.log(b); // 1
  1. 数组去重

数组去重是面试中经常被问到的一个问题。有多种方法可以实现数组去重,最常见的方法是使用Set数据结构。Set是一种无序集合,它可以自动消除重复的元素。

const arr = [1, 2, 3, 4, 5, 1, 2, 3];

const uniqueArr = [...new Set(arr)];

console.log(uniqueArr); // [1, 2, 3, 4, 5]
  1. 二叉树遍历

二叉树遍历是一种遍历二叉树中所有节点的方法。有三种常见的二叉树遍历方式:前序遍历、中序遍历和后序遍历。

前序遍历: 先访问根节点,然后递归访问左子树,最后递归访问右子树。

中序遍历: 先递归访问左子树,然后访问根节点,最后递归访问右子树。

后序遍历: 先递归访问左子树,然后递归访问右子树,最后访问根节点。

  1. 链表反转

链表反转也是面试中经常被问到的一个问题。链表反转是指将链表中节点的顺序颠倒过来。

class Node {
  constructor(val) {
    this.val = val;
    this.next = null;
  }
}

const head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);

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

  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }

  return prev;
}

const reversedHead = reverseList(head);
console.log(reversedHead); // 5 -> 4 -> 3 -> 2 -> 1
  1. 快速排序

快速排序是一种高效的排序算法,它利用分治法将数组分成较小的子数组,然后递归地对子数组进行排序。

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }

  const pivot = arr[0];
  const left = [];
  const right = [];

  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return [...quickSort(left), pivot, ...quickSort(right)];
}

const arr = [5, 2, 8, 3, 1, 9, 4, 7, 6];

console.log(quickSort(arr)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. 最长公共子序列

最长公共子序列(Longest Common Subsequence,LCS)是指两个字符串中最长的公共子序列。公共子序列是指两个字符串中出现顺序相同的字符序列,但不一定是连续的。

function lcs(str1, str2) {
  const m = str1.length;
  const n = str2.length;
  const dp = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (str1[i - 1] === str2[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
      }
    }
  }

  return dp[m][n];
}

const str1 = 'ABCDGH';
const str2 = 'AEDFHR';

console.log(lcs(str1, str2)); // 3
  1. 0-1背包问题

0-1背包问题是一个经典的动态规划问题。问题如下:

有一个背包,容量为m。有n件物品,每件物品有自己的重量和价值。每件物品只能放入背包一次。求放入背包中的物品的最大总价值。

function knapsack01(items, capacity) {
  const n = items.length;
  const dp = new Array(n + 1).fill(0).map(() => new Array(capacity + 1).fill(0));

  for (let i = 1; i <= n; i++) {
    const weight = items[i - 1].weight;
    const value = items[i - 1].value;

    for (let j = 1; j <= capacity; j++) {
      if (weight > j) {
        dp[i][j] = dp[i - 1][j];
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight] + value);
      }
    }
  }

  return dp[n][capacity];
}

const items = [
  { weight: 1, value: 1 },
  { weight: 2, value: 6 },
  { weight: 3, value: 10 },
  { weight: 5, value: 16 },
];
const capacity = 7;

console.log(knapsack01(items, capacity)); // 22
  1. 字符串匹配

字符串匹配是一个经典的算法问题。问题如下:

给定一个字符串s和一个模式串p,求p在s中出现的所有位置。

function kmp(s, p) {
  const m = s.length;
  const n = p.length;
  const next = new Array(n).fill(-1);

  for (let i = 1; i < n; i++) {
    let j = next[i - 1];

    while (j >= 0 && p[j + 1] !== p[i]) {
      j = next[j];
    }

    if (p[j + 1] === p[i]) {
      next[i] = j + 1;
    }
  }

  const matches = [];
  let j = -1;

  for (let i = 0; i < m; i++) {
    while (j >= 0 && p[j + 1] !== s[i]) {
      j = next[j];
    }

    if (p[j + 1] === s[i]) {
      j++;
    }

    if (j === n - 1) {
      matches.push(i - n + 1);
      j = next[j];
    }
  }

  return matches;
}

const s = 'ABCABCDABABCABAB';
const p = 'ABCAB';

console.log(kmp(s, p)); // [0, 10]
  1. 贪心算法

贪心算法是一种简单的优化算法,它