Leetcode JavaScript 问题

2025年4月19日 | 阅读 15 分钟

引言

编码面试和算法竞赛的领域充满了需要你全力以赴的问题。LeetCode 绝对是一个开发者磨练技术能力和解决问题技巧的重要平台。在 LeetCode 上,一个庞大的开发者群体可以找到从最简单到最复杂的各种编码任务。它就像一个熔炉,可以让你锻造算法技能并学习如何使用数据结构。

在众多可用的编程语言中,选择一种具有广泛采用且在 Web 开发过程中很常见的灵活语言。本文致力于 LeetCode JavaScript 问题选集,为中级或高级开发人员提供解决问题技巧发展的有用技术、策略和资源。

无论您是希望成为算法问题大师的资深 JavaScript 程序员,还是只是一个刚开始踏上 LeetCode 平台解决问题之旅的新手,本指南都旨在为您提供成功所需的知识和技能。利用 JavaScript 的力量,我们将踏上揭示代码难题为何如此棘手的旅程,并逐一揭开算法的奥秘,走向万维网。

理解 LeetCode 问题

最起码的步骤之一是彻底了解 LeetCode 问题本身。在这里,这些问题旨在多方面地挑战您的编码技能,例如算法效率、数据结构操作和问题分析。每个问题通常都有一个定义、限制和示例,以演示预期的输入-输出。在尝试解决这些问题之前,您应该花足够的时间来理解它们。

成功策略

  1. 从简单问题开始:如果您是 LeetCode 或 JavaScript 的新手,请从简单的问题开始练习和掌握该领域。首先研究基本算法结构,解决一些简单的实体,然后更新系统,再继续进行更高级的操作。
  2. 使用内置数据结构和方法:JavaScript 包含大量内置数据结构和函数,可以使数据流更快、更方便。熟悉数组、对象、字符串以及提供的 map、filter 和 reduce 等基本方法。经常依赖它们不仅有助于保持解决方案简短,还能提高效率。
  3. 练习算法技术:虽然 LeetCode 上有很多解决问题的方法,但其中许多都可以使用简单的算法技术来解决,例如递归、动态规划、贪婪算法等。请花一些时间找出这些方法并将其应用到您练习的各种问题中。
  4. 优化时间和空间复杂度:LeetCode 不仅验证您解决方案的逻辑,还对其进行加速。注意算法的时间和空间复杂度;您还需要尝试提高它们的性能。充分利用 JavaScript 应用不同优化模式(例如,记忆化、双指针和位运算)的能力。
  5. 测试您的解决方案:LeetCode 是一个方便的工具,可以尽可能地验证您的解决方案是否能在测试数据集中正常工作。探索这些技巧来检查您的工作并发现任何错误。在关注这些场景时,还要确保处理边缘情况和特殊情况,这样您就永远不会出现系统中的任何错误。

学习和练习资源

  1. 官方 LeetCode 文档:LeetCode 甚至提供了完整的文档,包括解释数据结构、算法和编码模式的开发者手册。查阅这些资源以获得对不同冲突解决策略的见解。
  2. 在线课程和教程:数百个网站为有兴趣的学习者提供 LeetCode 课程和 JavaScript 教程。互联网极大地影响了人们准备与技术相关的面试的方式。Udemy、Coursera 和 freeCodeCamp 等平台是开发人员获取涵盖广泛编码面试问题的学习路径的示例。
  3. 社区论坛和讨论:在 LeetCode 讨论区、Stack Overflow 和 Reddit 等平台上与活跃的开发者社区互动。参与讨论和意见交流、同行评审以及咨询在该领域经验更丰富的人员,无疑将极大地帮助您的学习。

问题 1

给定一个整数 n,如果 n 是 2 的幂,则返回 true;否则,返回 false。

如果存在整数 x,使得 n == 2^x,则整数 n 是 2 的幂。

示例 1

输入: n = 1

输出: true

解释: 2^0 = 1

示例 2

输入: n = 16

输出: true

解释: 2^4 = 16

示例 3

输入: n = 3

输出: false

约束

-231 <= n <= 231 - 1

JavaScript

此过程通过检查 n 是否只有一个位设置为 1 来工作。如果 n 是 2 的幂,则只有第一个位会设置,而在第一个位设置为 1 之后,其余位都会翻转。因此,n & n-1 将为 2 的幂产生零的按位 AND 值。

问题 2

给定一个正整数 millis,编写一个异步函数,使其睡眠 millis 毫秒。它可以解析任何值。

示例 1

输入: millis = 100

输出: 100

解释: 应返回一个在 100ms 后解析的 promise。

let t = Date.now();

sleep(100).then(() => {

console.log(Date.now() - t); // 100

});

示例 2

输入: millis = 200

输出: 200

解释: 应返回一个在 200ms 后解析的 promise。

约束

1 <= millis <= 1000

JavaScript

输出

Output: 100
Elapsed time: 100
Output: 200
Elapsed time: 200

此代码返回一个 Promise,该 Promise 在指示的毫秒延迟后解析。然后,将设计一种方法来处理 promise 的已完成值——在我们的例子中,它就是 millis 参数本身。

问题 3

给定一个整数数组 nums,一个 reducer 函数 fn,以及一个初始值 init,通过按顺序将前一个计算的返回值传递给 fn 函数来计算数组中的每个元素,并返回最终结果。

结果通过以下操作获得:val = fn(init, nums[0]), val = fn(val, nums[1]), val = fn(val, nums[2]), ... 直到处理完数组中的每个元素。然后返回 val 的最终值。

如果数组的长度为 0,则函数应返回 init。

请在不使用内置 Array.reduce 方法的情况下解决。

示例 1

输入

nums = [1,2,3,4]

fn = function sum(accum, curr) { return accum + curr; }

init = 0

输出: 10

说明

最初,值为 init=0。

(0) + nums[0] = 1

(1) + nums[1] = 3

(3) + nums[2] = 6

(6) + nums[3] = 10

最终答案是 10。

示例 2

输入

nums = [1,2,3,4]

fn = function sum(accum, curr) { return accum + curr * curr; }

init = 100

输出: 130

说明

最初,值为 init=100。

(100) + nums[0] * nums[0] = 101

(101) + nums[1] * nums[1] = 105

(105) + nums[2] * nums[2] = 114

(114) + nums[3] * nums[3] = 130

最终答案是 130。

示例 3

输入

nums = []

fn = function sum(accum, curr) { return 0; }

init = 25

输出: 25

解释: 对于空数组,答案始终是 init。

约束

0 <= nums.length <= 1000

0 <= nums[i] <= 1000

0 <= init <= 1000

JavaScript

输出

Output: 10
Output: 130
Output: 25

问题 4

给定一个函数数组 [f1, f2, f3, ..., fn],返回一个新函数 fn,它是函数数组的复合函数。

[f(x), g(x), h(x)] 的函数复合是 fn(x) = f(g(h(x)))。

空函数列表的函数复合是恒等函数 f(x) = x。

数组中的每个函数都接受一个整数作为输入并返回一个整数作为输出。

示例 1

输入: functions = [x => x + 1, x => x * x, x => 2 * x], x = 4

输出: 65

说明

从右到左求值...

从 x = 4 开始。

2 * (4) = 8

(8) * (8) = 64

(64) + 1 = 65

示例 2

输入: functions = [x => 10 * x, x => 10 * x, x => 10 * x], x = 1

输出: 1000

说明

从右到左求值...

10 * (1) = 10

10 * (10) = 100

10 * (100) = 1000

示例 3

输入: functions = [], x = 42

输出: 42

说明

零个函数的复合是恒等函数

约束

-1000 <= x <= 1000

0 <= functions.length <= 1000

所有函数都接受并返回单个整数

JavaScript

输出

Output: 65
Output: 1000
Output: 42

此代码 composeFunctions 函数反向遍历函数数组,通过将一个函数应用于另一个函数评估的结果来形成最终结果。如果函数数组为空,则返回 x 本身。

问题 5

给定一个整数数组 arr 和一个映射函数 fn,返回一个应用了转换的新数组。

返回的数组应创建为 returnedArray[i] = fn(arr[i], i)。

请在不使用内置 Array.map 方法的情况下解决。

示例 1

输入: arr = [1,2,3], fn = function plusone(n) { return n + 1; }

输出: [2,3,4]

说明

const newArray = map(arr, plusone); // [2,3,4]

该函数将数组中的每个值增加一。

示例 2

输入: arr = [1,2,3], fn = function plusI(n, i) { return n + i; }

输出: [1,3,5]

解释: 该函数将每个值增加其所在的索引。

示例 3

输入: arr = [10,20,30], fn = function constant() { return 42; }

输出: [42,42,42]

解释: 该函数始终返回 42。

约束

0 <= arr.length <= 1000

-109 <= arr[i] <= 109

fn 返回一个数字

JavaScript

输出

Output 1: [2, 3, 4]
Output 2: [1, 3, 5]
Output 3: [42, 42, 42]

此代码指定了一个 map 函数,该函数将创建一个名为 mappedArr 的新数组,并迭代输入数组 arr 的每个元素。将相应的映射函数 fn 应用于每个元素,最后将结果推送到 mappedArr。最后,元素从原始数组 res 映射,从而产生一个包含转换后元素的新数组 mappedArr。

问题 6

给定一个数组 arr 和一个块大小,返回一个分块数组。分块数组包含 arr 中的原始元素,但由长度为 size 的子数组组成。如果 arr.length 不能被 size 整除,则最后一个子数组的长度可能小于 size。

您可以假定数组是 JSON.parse 的输出。换句话说,它是有效的 JSON。

请在不使用 lodash 的 _.chunk 函数的情况下解决。

示例 1

输入: arr = [1,2,3,4,5], size = 1

输出: [[1],[2],[3],[4],[5]]

解释: arr 已被拆分成长度为 1 的子数组。

示例 2

输入: arr = [1,9,6,3,2], size = 3

输出: [[1,9,6],[3,2]]

解释: arr 已被拆分成长度为 3 的子数组。但是,第二个子数组只剩下两个元素。

示例 3

输入: arr = [8,5,3,2,6], size = 6

输出: [[8,5,3,2,6]]

解释: Size 大于 arr.length;因此,所有元素都在第一个子数组中。

示例 4

输入: arr = [], size = 1

输出: []

解释: 没有要分块的元素,因此返回一个空数组。

约束

arr 是一个有效的 JSON 数组

2 <= JSON.stringify(arr).length <= 105

1 <= size <= arr.length + 1

JavaScript

输出

Output 1: [[1],[2],[3],[4],[5]]
Output 2: [[1,9,6],[3,2]]
Output 3: [[8,5,3,2,6]]
Output 4: []

此代码是一个示例,使用 chunkArray 作为函数名,它接受数组 arr 和块大小作为参数。它遍历数组,将其拆分成所需长度的子数组,并将每个子数组放入一个名为 chunkedArray 的新数组中。最后,它为您提供 chunkedArray。

问题 7

创建一个 ArrayWrapper 类,该类在其构造函数中接受一个整数数组。此类应具有两个功能

当两个类的实例与 + 运算符相加时,结果值是两个数组中所有元素的总和。

当对实例调用 String() 函数时,它返回一个以逗号分隔并用方括号括起来的字符串,例如 [1,2,3]。

示例 1

输入: nums = [[1,2],[3,4]], operation = "Add"

输出: 10

说明

const obj1 = new ArrayWrapper([1,2]);

const obj2 = new ArrayWrapper([3,4]);

obj1 + obj2; // 10

示例 2

输入: nums = [[23,98,42,70]], operation = "String"

输出: "[23,98,42,70]"

说明

const obj = new ArrayWrapper([23,98,42,70]);

String(obj); // "[23,98,42,70]"

示例 3

输入: nums = [[],[]], operation = "Add"

输出: 0

说明

const obj1 = new ArrayWrapper([]);

const obj2 = new ArrayWrapper([]);

obj1 + obj2; // 0

约束

0 <= nums.length <= 1000

0 <= nums[i] <= 1000

注意: nums 是传递给构造函数的数组

JavaScript

输出

Output 1: 10
Output 2: [23,98,42,70]
Output 3: 0

在此代码中,forward 类有一个构造函数,它接受一个整数数组作为参数。它还有两个方法:add() 是替换加法运算符的函数,结果是两个数组中所有元素的总和,toString() 是重写 toString 方法的函数,因此该函数返回一个用方括号括起来的字符串,中间有一个逗号分隔符。

问题 8

编写一个函数 createHelloWorld。它应该返回一个新函数,该函数始终返回“Hello World”。

示例 1

输入: args = []

输出: "Hello World"

说明

const f = createHelloWorld();

f(); // "Hello World"

createHelloWorld 返回的函数应始终返回“Hello World”。

示例 2

输入: args = [{},null,42]

输出: "Hello World"

说明

const f = createHelloWorld();

f({}, null, 42); // "Hello World"

可以向函数传递任何参数,但它仍然应该始终返回“Hello World”。

约束

0 <= args.length <= 10

JavaScript

输出

Output 1: Hello World
Output 2: Hello World

在此代码中,createHelloWorld() 返回一个新函数,该函数在被调用时,无论传递什么参数,始终返回“Hello World”。我们生成定义为 createHelloWorld() 的此函数的实例,并通过提供不同的参数集来调用它们,以表明该函数“Hello World”返回“Hello World”,而不管传递的参数如何。

问题 9

给定一个函数 fn,一个参数数组 args,以及一个以毫秒为单位的超时时间 t,返回一个取消函数 cancelFn。

在 cancelTimeMs 延迟后,将调用返回的取消函数 cancelFn。

setTimeout(cancelFn, cancelTimeMs)

最初,函数 fn 的执行应延迟 t 毫秒。

如果在 t 毫秒的延迟之前调用了函数 cancelFn,它将取消 fn 的延迟执行。否则,如果 cancelFn 未在指定的延迟 t 内调用,则将使用提供的参数执行 fn。

示例 1

输入: fn = (x) => x * 5, args = [2], t = 20

输出: [{"time": 20, "returned": 10}]

说明

const cancelTimeMs = 50;

const cancelFn = cancellable((x) => x * 5, [2], 20);

setTimeout(cancelFn, cancelTimeMs);

取消操作计划在 cancelTimeMs (50ms) 延迟后发生,该延迟发生在 20ms 时 fn(2) 执行之后。

示例 2

输入: fn = (x) => x**2, args = [2], t = 100

输出: []

说明

const cancelTimeMs = 50;

const cancelFn = cancellable((x) => x**2, [2], 100);

setTimeout(cancelFn, cancelTimeMs);

取消操作计划在 cancelTimeMs (50ms) 延迟后发生,该延迟发生在 100ms 时 fn(2) 执行之前,导致 fn(2) 永远不会被调用。

示例 3

输入: fn = (x1, x2) => x1 * x2, args = [2,4], t = 30

输出: [{"time": 30, "returned": 8}]

说明

const cancelTimeMs = 100;

const cancelFn = cancellable((x1, x2) => x1 * x2, [2,4], 30);

setTimeout(cancelFn, cancelTimeMs);

取消操作计划在 cancelTimeMs (100ms) 延迟后发生,该延迟发生在 30ms 时 fn(2,4) 执行之后。

约束

fn 是一个函数

args 是一个有效的 JSON 数组

1 <= args.length <= 10

20 <= t <= 1000

10 <= cancelTimeMs <= 1000

JavaScript

输出

For Example 1: [{"time": 20, "returned": 10}]
For Example 2: []
For Example 3: [{"time": 30, "returned": 8}]

此代码中的 cancellable 函数接受主函数 fn、其参数以及延迟时间 t。它构造一个在 t 毫秒后解析的 promise,并在尚未被取消的情况下使用提供的参数执行函数 fn。另一方面,如果在超时之前调用了取消函数,则将 cancelled 标志设置为 true。最后,该图被包装在一个可能仍需取消的结束部分。


下一个主题Javascript-append