令人头疼的十道大厂面试题
2023-12-10 06:23:23
又到了跳槽季,程序员们摩拳擦掌,准备迎接新的挑战。面试是跳槽过程中必不可少的一环,而大厂的面试更是出了名的难。为了帮助大家顺利通过面试,我们总结了十道经典的大厂面试题,让你充分了解面试官的套路,助你顺利拿到offer!
- 算术交换
算术交换是一种通过算术运算过程中的技巧,可以巧妙地将两个值进行互换。但是,这个方法有个缺点就是变量数据溢出。因为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
- 数组去重
数组去重是面试中经常被问到的一个问题。有多种方法可以实现数组去重,最常见的方法是使用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]
- 二叉树遍历
二叉树遍历是一种遍历二叉树中所有节点的方法。有三种常见的二叉树遍历方式:前序遍历、中序遍历和后序遍历。
前序遍历: 先访问根节点,然后递归访问左子树,最后递归访问右子树。
中序遍历: 先递归访问左子树,然后访问根节点,最后递归访问右子树。
后序遍历: 先递归访问左子树,然后递归访问右子树,最后访问根节点。
- 链表反转
链表反转也是面试中经常被问到的一个问题。链表反转是指将链表中节点的顺序颠倒过来。
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
- 快速排序
快速排序是一种高效的排序算法,它利用分治法将数组分成较小的子数组,然后递归地对子数组进行排序。
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]
- 最长公共子序列
最长公共子序列(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
- 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
- 字符串匹配
字符串匹配是一个经典的算法问题。问题如下:
给定一个字符串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]
- 贪心算法
贪心算法是一种简单的优化算法,它