故事
发哥在360实习,没有转正机会。红衣教主100台车送的霸气,可自己人转正却不那么洒脱。最近收到了腾讯面试,期待好消息。
建议大家先拿着题目自己做下,再看题解。
总体分析
这是腾讯一面的面试题,比较注重全面考察基础能力和素质。
- 基础语法和逻辑能力 字符串操作、数组排序等题目。
- 正则表达式
- 异步编程 考察手写!
- 算法实现 基础算法,一面收官
相对来说中规中矩,没有特别难的题目。但是越是基础题,其实腾讯面试官会追问的比较深。
解题
- 有字符串 var = 'abc345efgabcab',请写出 3 条 JS 语句分别实现如下 3 个功能(使用正则):
- 去掉字符串中的a、b、c 字符,形成结果:'345efg'
js
const var1 = 'abc345efgabcab'.replace(/[abc]/g, '');
console.log(var1); // 输出: '345efg'
- 将字符串中的数字用中括号括起来,形成结果:'abc[345]efgabcab'
js
const var2 = 'abc345efgabcab'.replace(/\d+/g, match => `[${match}]`);
console.log(var2); // 输出: 'abc[345]efgabcab'
- 将字符串中的每个数字的值分别乘以 2,形成结果:'abc6810efgabcab'
javascript
const var3 = 'abc345efgabcab'.replace(/\d/g, match => String(parseInt(match, 10) * 2));
console.log(var3); // 输出: 'abc6810efgabcab'
- 写出以下程序的输出
js
const arr = [3, 1, 4, 1, 5, 9];
const sortedArr = arr.sort((a, b) => a - b);
console.log(sortedArr); // [1, 1, 3, 4, 5, 9]
console.log(arr); // [1, 1, 3, 4, 5, 9]
- 主要考察sort的排序规则,可以以冒泡排序来理解,两两比较,交换位置。 a-b 是升序,b-a 是降序。 a比b小,不用交互。相反,a比b大,交换位置。
- sort方法会直接修改原数组 比如 reverse , splice , push , pop , shift , unshift 等方法都会直接修改原数组。 而 slice , concat , join , map , filter 等方法不会修改原数组。
-
多种方式实现多层嵌套数组扁平化
- 递归 + reduce
jsfunction flattenArrayRecursive(arr) { return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenArrayRecursive(val)) : acc.concat(val), [] ); } console.log(flattenArrayRecursive([1, [2, [3, 4], 5], 6])); // 输出: [1, 2, 3, 4, 5, 6]
- 使用栈
jsfunction flattenArrayStack(arr) { const stack = [...arr]; const result = []; while (stack.length) { const next = stack.pop(); if (Array.isArray(next)) { stack.push(...next); } else { result.push(next); } } return result.reverse(); // 因为是从栈中弹出元素,所以需要反转结果 } console.log(flattenArrayStack([1, [2, [3, 4], 5], 6])); // 输出: [1, 2, 3, 4, 5, 6]
- ES6 的 flat() 方法 ES6 提供了一个内置的方法 flat(),可以非常简洁地实现数组扁平化。你可以指定一个深度参数,默认值为 1,如果想要完全扁平化未知深度的数组,可以传入 Infinity。
scssconst arr = [1, [2, [3, 4], 5], 6]; // 默认深度为1 arr.flat() // [1, 2, [3, 4], 5, 6] // 指定深度为2 arr.flat(2) // [1, 2, 3, 4, 5, 6] // 完全扁平化(处理任意深度) arr.flat(Infinity) // [1, 2, 3, 4, 5, 6] // 更复杂的例子 const deepArr = [1, [2, [3, [4, 5], 6], 7], 8]; deepArr.flat(1) // [1, 2, [3, [4, 5], 6], 7, 8] deepArr.flat(2) // [1, 2, 3, [4, 5], 6, 7, 8] deepArr.flat(3) // [1, 2, 3, 4, 5, 6, 7, 8]
- 使用 toString 方法(仅适用于数值型数组) 这种方法利用了数组转换成字符串时会自动扁平化的特性,但需要注意它只适用于数值型数组或其它可以直接安全转换为字符串再转回原始类型的数组。
js[1, [2, [3, 4], 5], 6].toString() '1,2,3,4,5,6' function flattenArrayToString(arr) { return arr.toString().split(',').map(Number); } console.log(flattenArrayToString([1, [2, [3, 4], 5], 6])); // 输出: [1, 2, 3, 4, 5, 6]
-
实现promise.all 函数
Promise.all接收一个Promise数组作为参数,返回一个新的Promise,只有当所有Promise都成功时才成功返回所有结果数组,只要有一个失败就立即失败。
Promise 除了 all 方法外,还有以下类似的静态方法:
-
Promise.race(iterable)
- 接收一组 Promise 实例
- 返回一个新的 Promise
- 只要有一个 Promise 率先改变状态(无论成功或失败),就采用它的结果
-
Promise.allSettled(iterable)
- 接收一组 Promise 实例
- 返回一个新的 Promise
- 等待所有 Promise 都完成(无论成功或失败)
- 返回所有 Promise 的结果数组,包含状态和值
-
Promise.any(iterable)
- 接收一组 Promise 实例
- 返回一个新的 Promise
- 只要有一个 Promise 成功,就返回那个已经成功的 Promise
- 如果所有 Promise 都失败,则返回一个失败的 Promise
这些方法各有特点和使用场景:
- all: 需要等所有任务都完成
- race: 只需要最快的任务结果
- allSettled: 不关心成功失败,需要所有结果
- any: 只要有一个成功即可
手写实现
js
// 自定义实现 Promise.all 方法
Promise.MyAll = function (promises) {
// 存储所有 Promise 的结果
let arr = [],
// 计数器,记录已完成的 Promise 数量
count = 0
// 返回一个新的 Promise
return new Promise((resolve, reject) => {
// 遍历传入的 promises 数组
promises.forEach((item, i) => {
// 将每个项转为 Promise 对象并执行
Promise.resolve(item).then(res => {
// 按照原始顺序存储结果
arr[i] = res
// 完成计数加1
count += 1
// 当所有 Promise 都完成时,返回结果数组
if (count === promises.length) resolve(arr)
},
// 任何一个 Promise 失败时直接 reject
reject)
})
})
}
更详细的讲解可以参考字节飞书面试------请实现promise.all
-
js 实现大数相加 原题是leetcode 415. 字符串相加。腾讯老喜欢考这道题了,简单题,好搞定。
js 的 Number 类型有精度限制,对于超大数字es6提供了BigInt新类型。但接发不能用。
大数相加的主要思路是:
- 将两个大数字符串转为数组,并对齐位数
-
可以用 padStart 补 0
-
或者在前面追加 0
- 模拟人工加法运算
-
从个位开始按位相加
-
需要记录进位值
-
当前位结果为 (a + b + carry) % 10
-
新的进位值为 Math.floor((a + b + carry) / 10)
- 最后处理可能的进位,并将结果数组转回字符串
代码实现:
js
/**
* 实现两个大数字符串相加
* @param {string} num1 第一个大数字符串
* @param {string} num2 第二个大数字符串
* @returns {string} 相加结果的字符串
*/
function addStrings(num1, num2) {
// 用数组存储最终计算结果的每一位
const res = []
// carry 表示进位值,初始为0
let carry = 0
// i, j 分别指向 num1 和 num2 的末位(个位)
let i = num1.length - 1
let j = num2.length - 1
// 只要下标未越界或还有进位值,就继续循环
// i>=0 表示 num1 还有数字未处理
// j>=0 表示 num2 还有数字未处理
// carry 表示还有进位需要处理
while (i >= 0 || j >= 0 || carry) {
// 获取当前位的两个数字
// 如果下标已经小于0,则补0
// Number()将字符转为数字
const n1 = i >= 0 ? Number(num1[i]) : 0
const n2 = j >= 0 ? Number(num2[j]) : 0
// 当前位相加,需要加上进位值
const sum = n1 + n2 + carry
// sum % 10 得到当前位的结果
// % 是求余(取模)运算符
// unshift 将结果插入到数组头部,因为我们是从个位开始计算的
res.unshift(sum % 10)
// 计算新的进位值
// 比如 sum = 15, carry = 1
carry = Math.floor(sum / 10)
// 移动指针,处理下一位
i--
j--
}
// 将结果数组转为字符串并返回
return res.join('')
}
- 编写二叉树的前序遍历函数
太简单了,直接上代码。面试官说,你这个递归的,我看看你能不能用迭代实现。还好A出来了....
js
function preorder(root) {
// 退出条件
// 空树
if (!root) {
return
}
// 递归式
console.log(root.val);
preorder(root.left);
preorder(root.right);
}
// 迭代方式实现前序遍历
function preorderTraversalIterative(root) {
const result = []
// 如果根节点为空,直接返回空数组
if (!root) {
return result
}
// 使用栈来模拟递归过程
const stack = [root]
while (stack.length) {
// 弹出栈顶节点并访问
const node = stack.pop()
result.push(node.val)
// 由于栈是后进先出,所以先压入右子节点
// 这样可以保证左子节点先被访问
if (node.right) {
stack.push(node.right)
}
if (node.left) {
stack.push(node.left)
}
}
return result
}
总结
发哥的腾讯面试官是女的,一直在追问,压力有点大。期待二面。
接下来我会记录身边朋友的各个大厂面试经历,欢迎点赞,收藏。