楼主: Myron2017
跳转到指定楼层
上一主题 下一主题
收起左侧

刷题记录帖子

🔗
 楼主| Myron2017 2026-9-21 08:37:11 | 只看该作者
全局:
2774. Array Upper Bound
Easy
Hint
Write code that enhances all arrays such that you can call the upperBound() method on any array and it will return the last index of a given target number. nums is a sorted ascending array of numbers that may contain duplicates. If the target number is not found in the array, return -1.



Example 1:

Input: nums = [3,4,5], target = 5
Output: 2
Explanation: Last index of target value is 2
Example 2:

Input: nums = [1,4,5], target = 2
Output: -1
Explanation: Because there is no digit 2 in the array, return -1.
Example 3:

Input: nums = [3,4,6,6,6,6,7], target = 6
Output: 5
Explanation: Last index of target value is 5


Constraints:

1 <= nums.length <= 104
-104 <= nums[i], target <= 104
nums is sorted in ascending order.


Follow up: Can you write an algorithm with O(log n) runtime complexity?

  1. interface Array<T> {
  2.     upperBound(target: number): number;
  3. }

  4. Array.prototype.upperBound = function(target: number): number {
  5.     let left = 0;
  6.     let right = this.length - 1;
  7.     let result = -1;

  8.     while (left <= right) {
  9.         const mid = Math.floor((left + right) / 2);
  10.         
  11.         if (this[mid] === target) {
  12.             result = mid;       // 记录当前找到的目标索引
  13.             left = mid + 1;     // 继续向右侧搜索,寻找是否还有更靠后的目标
  14.         } else if (this[mid] < target) {
  15.             left = mid + 1;     // 目标在右半部分
  16.         } else {
  17.             right = mid - 1;    // 目标在左半部分
  18.         }
  19.     }

  20.     return result;
  21. };

  22. // [3,4,5].upperBound(5); // 2
  23. // [1,4,5].upperBound(2); // -1
  24. // [3,4,6,6,6,6,7].upperBound(6) // 5
复制代码
题目要求我们在所有数组的原型上添加一个 upperBound() 方法,用于找到有序数组中目标值 target 的最后一次出现的索引。如果没有找到,则返回 -1。题目进阶(Follow up)要求时间复杂度为 O(logn)。

既然数组已经是升序排序的,并且要求 O(logn) 的时间复杂度,这正是典型的二分查找(Binary Search)的应用场景。

具体逻辑如下:

初始化指针:定义双指针 left 指向数组头部,right 指向数组尾部。定义 result = -1 用来存储最终结果。

二分循环:当 left <= right 时不断循环。计算中间位置 mid。

关键判断(this[mid] === target):

在普通的二分查找中,只要找到目标值就会直接返回 mid。

但在这里,因为我们需要找的是最后一个索引。所以当我们发现 this[mid] === target 时,我们先用 result 把当前的 mid 记录下来,然后强行把 left 指针移到 mid + 1。这样做的目的是在数组的右半部分继续搜索,看是否还有位置更靠后的目标值。

常规判断:

如果 this[mid] < target,说明目标值在更右边,将 left 移至 mid + 1。

如果 this[mid] > target,说明目标值在左边,将 right 移至 mid - 1。

返回结果:循环结束后,result 中保存的就是目标值最后一次出现的索引。如果整个过程中都没碰到目标值,result 会保持初始的 -1 并返回。
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-21 08:45:26 | 只看该作者
全局:
2775. Undefined to Null
Medium
Given a deeply nested object or array obj, return the object obj with any undefined values replaced by null.

undefined values are handled differently than null values when objects are converted to a JSON string using JSON.stringify(). This function helps ensure serialized data is free of unexpected errors.



Example 1:

Input: obj = {"a": undefined, "b": 3}
Output: {"a": null, "b": 3}
Explanation: The value for obj.a has been changed from undefined to null
Example 2:

Input: obj = {"a": undefined, "b": ["a", undefined]}
Output: {"a": null,"b": ["a", null]}
Explanation: The values for obj.a and obj.b[1] have been changed from undefined to null


Constraints:

obj is a valid JSON object or array
2 <= JSON.stringify(obj).length <= 105

这道题要求我们将一个深度嵌套(Deeply Nested)的对象或数组中的所有 undefined 替换为 null。因为 JSON 的标准规范里不包含 undefined,在执行 JSON.stringify() 时,含有 undefined 的键会被忽略或转换为 null,为了消除这种不可预见性,我们需要手动进行一次清理。

遇到深度嵌套的数据结构,最直接有效的方法就是深度优先遍历(DFS) / 递归(Recursion)。

具体逻辑如下:

for...in 遍历:
在 JavaScript/TypeScript 中,for...in 循环非常强大。它不仅可以遍历对象的“键(keys)”,也可以遍历数组的“索引(indices)”。所以无论是对象还是数组,我们都可以用这一套逻辑统一处理。

条件判断:

如果当前键对应的值是 undefined:我们直接将其赋值(原地修改)为 null。

如果当前键对应的值是一个嵌套的对象或数组(在 JS 中 typeof [] 和 typeof {} 都是 'object'),并且它不是 null(因为 typeof null 也是 'object'):我们就对这个子对象/子数组递归调用 undefinedToNull() 进行下一层的深度检查和替换。

如果是其他基本数据类型(如 string, number, boolean 等),则忽略,什么也不做。

类型断言与返回:
由于我们直接在原数据 obj 上进行了原地修改(In-place modification),这在空间复杂度上是最优的(O(1) 额外空间,不计算调用栈)。最后只需将修改好的 obj 强制类型转换为题目要求的 Obj2 并返回即可。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Value = undefined | null | boolean | number | string | Value[] | { [key: string]: Value };

  3. type Obj1 = Record<string, Value> | Array<Value>
  4. type Obj2 = Record<string, JSONValue> | Array<JSONValue>

  5. function undefinedToNull(obj: Obj1): Obj2 {
  6.     // 遍历对象的所有键或数组的所有索引
  7.     for (const key in obj) {
  8.         if (obj[key] === undefined) {
  9.             // 如果值为 undefined,直接替换为 null
  10.             obj[key] = null;
  11.         } else if (typeof obj[key] === 'object' && obj[key] !== null) {
  12.             // 如果值是对象或数组(且不是 null),递归调用
  13.             undefinedToNull(obj[key] as Obj1);
  14.         }
  15.     }
  16.    
  17.     // 原地修改完毕后,断言为 Obj2 类型并返回
  18.     return obj as unknown as Obj2;
  19. }

  20. /**
  21. * undefinedToNull({"a": undefined, "b": 3}) // {"a": null, "b": 3}
  22. * undefinedToNull([undefined, undefined]) // [null, null]
  23. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:22:18 来自APP | 只看该作者
全局:
2797. Partial Function with Placeholders
Easy
Given a function fn and an array args, return a function partialFn.

Placeholders "_" in the args should be replaced with values from restArgs starting from index 0. Any remaining values in the restArgs should be added at the end of the args.

partialFn should return a result of fn. fn should be called with the elements of the modified args passed as separate arguments.



Example 1:

Input: fn = (...args) => args, args = [2,4,6], restArgs = [8,10]
Output: [2,4,6,8,10]
Explanation:
const partialFn = partial(fn, args)
const result = partialFn(...restArgs)
console.log(result) // [2,4,6,8,10]

There are no placeholders "_" in args therefore restArgs is just added at the end of args. Then the elements of the args are passed as separate arguments to fn, which returns passed arguments as an array.
Example 2:
Input: fn = (...args) => args, args = [1,2,"_",4,"_",6], restArgs = [3,5]
Output: [1,2,3,4,5,6]
Explanation:
const partialFn = partial(fn, args)
const result = partialFn(...restArgs)
console.log(result) // [1,2,3,4,5,6]

Placeholders "_" are replaced with values from the restArgs. Then the elements of the args are passed as separate arguments to fn, which returns passed arguments as an array.
Example 3:

Input: fn = (a, b, c) => b + a - c, args = ["_", 5], restArgs = [5, 20]
Output: -10
Explanation:
const partialFn = partial(fn, args)
const result = partialFn(...restArgs)
console.log(result) // -10

Placeholder "_" is replaced with 5 and 20 is added at the end of args. Then the elements of the args are passed as separate arguments to fn, which returns -10 (5 + 5 - 20).


Constraints:

fn is a function
args and restArgs are valid JSON arrays
1 <= args.length <= 5 * 104
1 <= restArgs.length <= 5 * 104
0 <= number of placeholders <= restArgs.length
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Fn = (...args: JSONValue[]) => JSONValue

  3. function partial(fn: Fn, args: JSONValue[]): Fn {
  4.     return function(...restArgs: JSONValue[]) {
  5.         const combinedArgs: JSONValue[] = [];
  6.         let restIndex = 0; // 用于追踪 restArgs 数组的当前索引
  7.         
  8.         // 1. 遍历初始参数数组
  9.         for (let i = 0; i < args.length; i++) {
  10.             if (args[i] === "_") {
  11.                 // 遇到占位符,从 restArgs 中取出一个值进行替换
  12.                 combinedArgs.push(restArgs[restIndex]);
  13.                 restIndex++;
  14.             } else {
  15.                 // 不是占位符,直接保留原来的值
  16.                 combinedArgs.push(args[i]);
  17.             }
  18.         }
  19.         
  20.         // 2. 将 restArgs 中剩余的未使用的参数追加到末尾
  21.         if (restIndex < restArgs.length) {
  22.             combinedArgs.push(...restArgs.slice(restIndex));
  23.         }
  24.         
  25.         // 3. 将合并后的参数传递给原函数并返回结果
  26.         return fn(...combinedArgs);
  27.     }
  28. };
复制代码

补充内容 (2026-09-22 09:23 +08:00):
这道题的核心逻辑可以分为以下几个步骤:
  • 闭包 (Closure):
    1. partial
    复制代码
    函数返回一个新的函数。这个新函数可以访问到外层
    1. partial
    复制代码
    接收的
    1. fn
    复制代码
    和
    1. args
    复制代码
    (这就是闭包的特性)。新函数接收剩余参数
    1. ...restArgs
    复制代码
    。
  • 遍历与替换: 我们需要将
    1. args
    复制代码
    和
    1. restArgs
    复制代码
    合并成一个新的参数数组
    1. combinedArgs
    复制代码
    。
    • 我们定义一个变量
      1. restIndex = 0
      复制代码
      ,用来记录当前
      1. restArgs
      复制代码
      用到了第几个元素。
    • 遍历原始的
      1. args
      复制代码
      数组:
      • 如果当前元素是占位符
        1. "_"
        复制代码
        ,我们就从
        1. restArgs
        复制代码
        中取出第
        1. restIndex
        复制代码
        个元素放入
        1. combinedArgs
        复制代码
        中,并且让
        1. restIndex
        复制代码
        加 1。
      • 如果当前元素不是占位符,我们就原封不动地把它放入
        1. combinedArgs
        复制代码
        。
  • 处理多余的剩余参数: 当我们遍历完
    1. args
    复制代码
    时,
    1. restArgs
    复制代码
    可能还没有用完(比如
    1. args
    复制代码
    里只有 1 个占位符,但
    1. restArgs
    复制代码
    传了 3 个值)。 因此,我们检查
    1. restIndex < restArgs.length
    复制代码
    ,如果满足条件,说明还有剩余参数。我们使用
    1. slice(restIndex)
    复制代码
    截取剩下所有的参数,并用展开运算符
    1. ...
    复制代码
    将它们追加到
    1. combinedArgs
    复制代码
    的末尾。
  • 执行原函数: 现在
    1. combinedArgs
    复制代码
    已经是一个完美的、拼装好的参数数组。我们使用
    1. fn(...combinedArgs)
    复制代码
    将其展开并传给原函数,最后返回原函数的执行结果。
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:25:08 | 只看该作者
全局:
2803. Factorial Generator
Easy
Write a generator function that takes an integer n as an argument and returns a generator object which yields the factorial sequence.

The factorial sequence is defined by the relation n! = n * (n-1) * (n-2) * ... * 2 * 1​​​.

The factorial of 0 is defined as 1.



Example 1:

Input: n = 5
Output: [1,2,6,24,120]
Explanation:
const gen = factorial(5)
gen.next().value // 1
gen.next().value // 2
gen.next().value // 6
gen.next().value // 24
gen.next().value // 120
Example 2:

Input: n = 2
Output: [1,2]
Explanation:
const gen = factorial(2)
gen.next().value // 1
gen.next().value // 2
Example 3:

Input: n = 0
Output: [1]
Explanation:
const gen = factorial(0)
gen.next().value // 1


Constraints:

0 <= n <= 18

代码解释生成器函数语法:函数名带有 * (即 function*),表示这是一个生成器函数。它可以在执行过程中使用 yield 关键字“暂停”并返回一个值,下一次调用 .next() 时会从暂停的地方继续执行。处理边界条件 n = 0:根据题意,0! 是 1,此时我们只需要 yield 1 然后 return 结束生成器即可。计算序列:我们声明一个变量 currentFactorial 初始值为 1,用来保存当前的阶乘结果。使用一个 for 循环从 1 遍历到 n。在每一次迭代中,我们直接用上次保存的 currentFactorial 乘以当前的 i,这样就可以做到 $\mathcal{O}(1)$ 的状态转移,而不用每次都从头计算阶乘。接着使用 yield 将计算出的阶乘值提供给外部调用者。
  1. function* factorial(n: number): Generator<number> {
  2.     // 特殊情况:如果 n 为 0,0的阶乘是 1,只生成一个 1
  3.     if (n === 0) {
  4.         yield 1;
  5.         return;
  6.     }
  7.    
  8.     // 如果 n > 0,计算并生成从 1! 到 n! 的序列
  9.     let currentFactorial = 1;
  10.     for (let i = 1; i <= n; i++) {
  11.         currentFactorial *= i; // 不断累乘当前的 i
  12.         yield currentFactorial; // 产出当前的阶乘结果
  13.     }
  14. }

  15. /**
  16. * const gen = factorial(2);
  17. * gen.next().value; // 1
  18. * gen.next().value; // 2
  19. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:27:39 | 只看该作者
全局:
2804. Array Prototype ForEach
Easy
Write your version of method forEach that enhances all arrays such that you can call the array.forEach(callback, context) method on any array and it will execute callback on each element of the array. Method forEach should not return anything.

callback accepts the following arguments:

currentValue - represents the current element being processed in the array. It is the value of the element in the current iteration.
index - represents the index of the current element being processed in the array.
array - represents the array itself, allowing access to the entire array within the callback function.
The context is the object that should be passed as the function context parameter to the callback function, ensuring that the this keyword within the callback function refers to this context object.

Try to implement it without using the built-in array methods.



Example 1:

Input:
arr = [1,2,3],
callback = (val, i, arr) => arr[i] = val * 2,
context = {"context":true}
Output: [2,4,6]
Explanation:
arr.forEach(callback, context)  
console.log(arr) // [2,4,6]

The callback is executed on each element of the array.
Example 2:

Input:
arr = [true, true, false, false],
callback = (val, i, arr) => arr[i] = this,
context = {"context": false}
Output: [{"context":false},{"context":false},{"context":false},{"context":false}]
Explanation:
arr.forEach(callback, context)
console.log(arr) // [{"context":false},{"context":false},{"context":false},{"context":false}]

The callback is executed on each element of the array with the right context.
Example 3:

Input:
arr = [true, true, false, false],
callback = (val, i, arr) => arr[i] = !val,
context = {"context": 5}
Output: [false,false,true,true]


Constraints:

arr is a valid JSON array
context is a valid JSON object
fn is a function
0 <= arr.length <= 105

代码解释this 关键字的作用域:当我们把函数挂载到 Array.prototype 上时,函数内部的 this 就会指向调用该方法的数组实例本身。例如,当我们执行 arr.forEach(...) 时,forEach 内部的 this 就是 arr 数组。遍历数组:题目要求尽量不使用内置的数组方法(如 map, reduce 等)。因此,我们使用最基础的 for 循环,从 i = 0 遍历到 i < this.length。在循环中,this[i] 就是当前遍历到的元素。绑定 context 并执行回调:题目要求回调函数 callback 执行时,其内部的 this 必须指向传入的 context 对象。在 JavaScript 中,我们可以使用 Function.prototype.call(thisArg, arg1, arg2, ...) 方法来执行函数,并显式指定该函数内部的 this(即 thisArg)。因此,我们调用 callback.call(context, this[i], i, this):context 作为回调函数的 this。this[i] 作为第一个参数 currentValue。i 作为第二个参数 index。this(原数组)作为第三个参数 array。复杂度分析:时间复杂度: $\mathcal{O}(N)$,其中 $N$ 是数组的长度。我们需要遍历整个数组,并且为每个元素执行一次回调函数。空间复杂度: $\mathcal{O}(1)$,除了循环迭代变量外,没有使用额外的空间。当然,这不包括回调函数内部自身可能造成的空间消耗。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Callback = (currentValue: JSONValue, index: number, array: JSONValue[]) => any
  3. type Context = Record<string, JSONValue>

  4. // 扩展全局 Array 接口,防止 TypeScript 报错
  5. declare global {
  6.     interface Array<T> {
  7.         forEach(callback: Callback, context: Context): void;
  8.     }
  9. }

  10. Array.prototype.forEach = function(callback: Callback, context: Context): void {
  11.     // 在原型方法中,'this' 指向调用该方法的数组实例
  12.     for (let i = 0; i < this.length; i++) {
  13.         // 使用 Function.prototype.call() 来显式绑定 context 为 callback 的 this
  14.         // 并传入当前值、索引和原数组作为参数
  15.         callback.call(context, this[i], i, this);
  16.     }
  17. }

  18. /**
  19. *  const arr = [1,2,3];
  20. *  const callback = (val, i, arr) => arr[i] = val * 2;
  21. *  const context = {"context":true};
  22. *
  23. *  arr.forEach(callback, context)
  24. *
  25. *  console.log(arr) // [2,4,6]
  26. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:30:17 | 只看该作者
全局:
2758. Next Day
Easy
Write code that enhances all date objects such that you can call the date.nextDay() method on any date object and it will return the next day in the format YYYY-MM-DD as a string.



Example 1:

Input: date = "2014-06-20"
Output: "2014-06-21"
Explanation:
const date = new Date("2014-06-20");
date.nextDay(); // "2014-06-21"
Example 2:

Input: date = "2017-10-31"
Output: "2017-11-01"
Explanation: The day after 2017-10-31 is 2017-11-01.


Constraints:

new Date(date) is a valid date object

代码解释
避免副作用 (Avoid Side Effects):
我们使用 new Date(this.valueOf()) 创建了当前日期的一个副本 nextDate。直接修改 this (比如 this.setDate(...)) 会改变原始的日期对象,这在实际开发中通常是不好的实践。

日期的安全加法:
在 JavaScript 中,处理日期的加减最安全的方法是使用原生的 Date API,即 setDate(getDate() + 1)。

为什么不直接用 getTime() + 24 * 60 * 60 * 1000?因为如果某天刚好遇到夏令时 (DST) 的切换,一天可能只有 23 小时或者 25 小时,直接加 24 小时的毫秒数可能会导致计算出的日期不准确。而 setDate() 可以完美避开夏令时和闰年闰月的问题。

字符串格式化 (Zero Padding):
要返回标准的 YYYY-MM-DD 格式,必须保证月和日都是两位数。

getFullYear() 会返回 4 位数的年份(如 2014)。

getMonth() 的返回值是 0-11,所以需要 + 1。

我们将数字转化为字符串后,使用字符串自带的 .padStart(2, '0') 方法。它的意思是:如果字符串长度不足 2 位,就在前面用 '0' 补齐。
  1. interface Date {
  2.     nextDay(): string;
  3. }

  4. Date.prototype.nextDay = function(): string {
  5.     // 1. 创建一个新的 Date 实例,避免修改原始的 Date 对象
  6.     const nextDate = new Date(this.valueOf());
  7.    
  8.     // 2. 将日期设置为当前日期加 1
  9.     // JavaScript 的 setDate 会自动处理跨月、跨年的情况(例如 10月31日加1会变成11月1日)
  10.     nextDate.setDate(nextDate.getDate() + 1);
  11.    
  12.     // 3. 提取年、月、日并进行格式化
  13.     const year = nextDate.getFullYear();
  14.     // getMonth() 返回的月份是从 0 开始的 (0-11),所以需要加 1
  15.     // padStart(2, '0') 用于确保月份和日期是两位数,例如 '9' 会变成 '09'
  16.     const month = String(nextDate.getMonth() + 1).padStart(2, '0');
  17.     const day = String(nextDate.getDate()).padStart(2, '0');
  18.    
  19.     // 4. 拼接成 YYYY-MM-DD 格式的字符串并返回
  20.     return `${year}-${month}-${day}`;
  21. };

  22. /**
  23. * const date = new Date("2014-06-20");
  24. * console.log(date.nextDay()); // "2014-06-21"
  25. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:31:58 | 只看该作者
全局:
2727. Is Object Empty
Easy
Given an object or an array, return if it is empty.

An empty object contains no key-value pairs.
An empty array contains no elements.
You may assume the object or array is the output of JSON.parse.



Example 1:

Input: obj = {"x": 5, "y": 42}
Output: false
Explanation: The object has 2 key-value pairs so it is not empty.
Example 2:

Input: obj = {}
Output: true
Explanation: The object doesn't have any key-value pairs so it is empty.
Example 3:

Input: obj = [null, false, 0]
Output: false
Explanation: The array has 3 elements so it is not empty.


Constraints:

obj is a valid JSON object or array
2 <= JSON.stringify(obj).length <= 105


Can you solve it in O(1) time?

代码解释for...in 循环的特性:for...in 语法不仅可以用来遍历普通对象的键(keys),也可以用来遍历数组的索引(indices)。它的工作机制是获取可枚举属性。如果在对象或数组中找到了任何一个可枚举属性,它就会立刻进入循环体。为什么这是 $O(1)$ 时间复杂度?我们并没有遍历整个对象或数组,也没有像 Object.keys() 那样把所有的键收集成一个全新的数组。引擎在尝试提取第一个键时,只要提取到了,我们就在循环体内 return false 中断了函数的执行。无论这个对象有 $1$ 个属性还是 $100,000$ 个属性,代码都只执行第一步检查就结束了,因此时间复杂度严格控制在了 $O(1)$。空间复杂度:同样是 $O(1)$,因为我们没有开辟额外的内存空间(如生成键数组)。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Obj = Record<string, JSONValue> | JSONValue[]

  3. function isEmpty(obj: Obj): boolean {
  4.     // 遍历对象的键或数组的索引
  5.     for (const _ in obj) {
  6.         // 只要能进入循环,说明里面至少有一个属性/元素,直接返回 false
  7.         return false;
  8.     }
  9.     // 如果循环没有被执行,说明没有任何属性/元素,返回 true
  10.     return true;
  11. };
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:34:23 | 只看该作者
全局:
2726. Calculator with Method Chaining
Easy
conpanies icon
Companies
Design a Calculator class. The class should provide the mathematical operations of addition, subtraction, multiplication, division, and exponentiation. It should also allow consecutive operations to be performed using method chaining. The Calculator class constructor should accept a number which serves as the initial value of result.

Your Calculator class should have the following methods:

add - This method adds the given number value to the result and returns the updated Calculator.
subtract - This method subtracts the given number value from the result and returns the updated Calculator.
multiply - This method multiplies the result  by the given number value and returns the updated Calculator.
divide - This method divides the result by the given number value and returns the updated Calculator. If the passed value is 0, an error "Division by zero is not allowed" should be thrown.
power - This method raises the result to the power of the given number value and returns the updated Calculator.
getResult - This method returns the result.
Solutions within 10-5 of the actual result are considered correct.



Example 1:

Input:
actions = ["Calculator", "add", "subtract", "getResult"],
values = [10, 5, 7]
Output: 8
Explanation:
new Calculator(10).add(5).subtract(7).getResult() // 10 + 5 - 7 = 8
Example 2:

Input:
actions = ["Calculator", "multiply", "power", "getResult"],
values = [2, 5, 2]
Output: 100
Explanation:
new Calculator(2).multiply(5).power(2).getResult() // (2 * 5) ^ 2 = 100
Example 3:

Input:
actions = ["Calculator", "divide", "getResult"],
values = [20, 0]
Output: "Division by zero is not allowed"
Explanation:
new Calculator(20).divide(0).getResult() // 20 / 0

The error should be thrown because we cannot divide by zero.


Constraints:

actions is a valid JSON array of strings
values is a valid JSON array of numbers
2 <= actions.length <= 2 * 104
1 <= values.length <= 2 * 104 - 1
actions[i] is one of "Calculator", "add", "subtract", "multiply", "divide", "power", and "getResult"
First action is always "Calculator"
Last action is always "getResult"


这道题要求我们设计一个支持方法链式调用(Method Chaining)的 Calculator(计算器)类。

为了实现链式调用,我们需要在类的每一个计算方法(如 add, subtract 等)执行完毕后,返回实例本身 (this)。这样,下一个方法就可以直接挂在前面返回的实例上继续调用。

代码解释状态管理 (State Management):我们声明了一个私有属性 private result: number。在 constructor 初始化时,将传入的首个 value 赋值给它。之后所有的数学运算都将在这个 result 属性上进行累加或修改。方法链式调用 (Method Chaining):在 add、subtract、multiply、divide 和 power 这几个方法中,关键的一步是它们在修改完 this.result 之后,都执行了 return this;。这里的 this 指向的是当前被操作的 Calculator 实例对象。当我们执行 new Calculator(10).add(5) 时,add(5) 算完后返回的仍然是那个拥有 result = 15 的计算器对象,因此我们可以紧接着写 .subtract(7)。异常处理 (Error Handling):题目特别指出在除法运算中,如果被除数为 0,需要抛出错误消息 "Division by zero is not allowed"。我们在 divide 函数的第一行加入了 value === 0 的判断并主动 throw new Error(...) 拦截此操作。指数运算 (Exponentiation):使用 JavaScript/TypeScript 原生的指数运算符 **= 可以使代码变得十分简练,相当于 this.result = Math.pow(this.result, value)。复杂度分析:时间复杂度: 每一个单独的数学运算方法时间复杂度均为 $O(1)$。空间复杂度: 整个计算器对象只需维护一个浮点数 result 的状态,空间复杂度为 $O(1)$。
  1. class Calculator {
  2.     // 内部私有属性,用于保存当前的计算结果
  3.     private result: number;

  4.     constructor(value: number) {
  5.         this.result = value;
  6.     }
  7.    
  8.     add(value: number): Calculator {
  9.         this.result += value;
  10.         return this; // 返回实例本身,以支持链式调用
  11.     }
  12.    
  13.     subtract(value: number): Calculator {
  14.         this.result -= value;
  15.         return this;
  16.     }
  17.    
  18.     multiply(value: number): Calculator {
  19.         this.result *= value;
  20.         return this;
  21.     }
  22.    
  23.     divide(value: number): Calculator {
  24.         if (value === 0) {
  25.             // 按照题目要求,如果是除以 0,抛出指定内容的错误
  26.             throw new Error("Division by zero is not allowed");
  27.         }
  28.         this.result /= value;
  29.         return this;
  30.     }
  31.    
  32.     power(value: number): Calculator {
  33.         // 等同于 Math.pow(this.result, value)
  34.         this.result **= value;
  35.         return this;
  36.     }
  37.    
  38.     getResult(): number {
  39.         // 返回最终结果
  40.         return this.result;
  41.     }
  42. }
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:41:40 | 只看该作者
全局:
2725. Interval Cancellation
Easy
Given a function fn, an array of arguments args, and an interval time t, return a cancel function cancelFn.

After a delay of cancelTimeMs, the returned cancel function cancelFn will be invoked.

setTimeout(cancelFn, cancelTimeMs)
The function fn should be called with args immediately and then called again every t milliseconds until cancelFn is called at cancelTimeMs ms.



Example 1:

Input: fn = (x) => x * 2, args = [4], t = 35
Output:
[
   {"time": 0, "returned": 8},
   {"time": 35, "returned": 8},
   {"time": 70, "returned": 8},
   {"time": 105, "returned": 8},
   {"time": 140, "returned": 8},
   {"time": 175, "returned": 8}
]
Explanation:
const cancelTimeMs = 190;
const cancelFn = cancellable((x) => x * 2, [4], 35);
setTimeout(cancelFn, cancelTimeMs);

Every 35ms, fn(4) is called. Until t=190ms, then it is cancelled.
1st fn call is at 0ms. fn(4) returns 8.
2nd fn call is at 35ms. fn(4) returns 8.
3rd fn call is at 70ms. fn(4) returns 8.
4th fn call is at 105ms. fn(4) returns 8.
5th fn call is at 140ms. fn(4) returns 8.
6th fn call is at 175ms. fn(4) returns 8.
Cancelled at 190ms
Example 2:

Input: fn = (x1, x2) => (x1 * x2), args = [2, 5], t = 30
Output:
[
   {"time": 0, "returned": 10},
   {"time": 30, "returned": 10},
   {"time": 60, "returned": 10},
   {"time": 90, "returned": 10},
   {"time": 120, "returned": 10},
   {"time": 150, "returned": 10}
]
Explanation:
const cancelTimeMs = 165;
const cancelFn = cancellable((x1, x2) => (x1 * x2), [2, 5], 30)
setTimeout(cancelFn, cancelTimeMs)

Every 30ms, fn(2, 5) is called. Until t=165ms, then it is cancelled.
1st fn call is at 0ms
2nd fn call is at 30ms
3rd fn call is at 60ms
4th fn call is at 90ms
5th fn call is at 120ms
6th fn call is at 150ms
Cancelled at 165ms
Example 3:

Input: fn = (x1, x2, x3) => (x1 + x2 + x3), args = [5, 1, 3], t = 50
Output:
[
   {"time": 0, "returned": 9},
   {"time": 50, "returned": 9},
   {"time": 100, "returned": 9},
   {"time": 150, "returned": 9}
]
Explanation:
const cancelTimeMs = 180;
const cancelFn = cancellable((x1, x2, x3) => (x1 + x2 + x3), [5, 1, 3], 50)
setTimeout(cancelFn, cancelTimeMs)

Every 50ms, fn(5, 1, 3) is called. Until t=180ms, then it is cancelled.
1st fn call is at 0ms
2nd fn call is at 50ms
3rd fn call is at 100ms
4th fn call is at 150ms
Cancelled at 180ms


Constraints:

fn is a function
args is a valid JSON array
1 <= args.length <= 10
30 <= t <= 100
10 <= cancelTimeMs <= 500

代码解释   立即调用 ($0\text{ ms}$):原生的 setInterval(fn, t) 默认会在等待第一个 t 毫秒之后才开始第一次调用。为了满足题目要求的“立即执行”,我们直接在设置 setInterval 之前先手动调用一次 fn(...args)。   开启循环定时器:使用 setInterval 保存定时器句柄 timer。传入的回调函数每次触发时都会解构展开 args 数组并传递给 fn。   返回取消函数 (闭包):利用闭包特性,返回的函数能访问到 timer 变量。当外部调用 cancelFn() 时,执行 clearInterval(timer),就能精准终止后续的所有循环触发。   

要理解 clearInterval(timer) 为什么能精准终止后续的循环,需要搞清楚 JavaScript 底层的三个核心机制:Timer ID(定时器标识符)、事件循环(Event Loop)中的定时器管理,以及 闭包(Closure)。1. setInterval 到底做了什么?(注册与发牌)当你执行这行代码时:TypeScriptconst timer = setInterval(() => { fn(...args); }, t);
JavaScript 引擎(浏览器或 Node.js)在后台做了两件事:在后台“定时器注册表”中登记任务:引擎会启动一个后台计时器,记录着:“每隔 t 毫秒,就把回调函数推进任务队列里准备执行”。返回一个独一无二的编号(Timer ID):setInterval 并不是返回那个函数本身,而是返回一个整数(或对象,统称为 ID)。比如返回了数字 42。此时变量 timer = 42。这个 timer(42)就像是你去医院看病拿到的排号单号。2. 闭包(Closure)的作用(记住单号)在 cancellable 函数里,你返回了一个匿名函数:TypeScriptreturn () => {
    clearInterval(timer);
};
即使 cancellable 函数本身已经执行完毕退出了,但因为 闭包 的存在,返回给外部的那个 cancelFn 依然“死死记住”了外层作用域里的 timer 变量(比如 timer = 42)。3. clearInterval(timer) 做了什么?(销毁单号)当外部在 190ms 那一刻调用 cancelFn() 时,本质上执行的是:TypeScriptclearInterval(42);
JavaScript 引擎接收到指令后,会拿着 42 这个号去后台的“定时器注册表”里查找:引擎:“查到了,42 号定时器正在每隔 35ms 准备下一次触发。好,我现在立刻把 42 号从注册表里彻底删掉!”一旦被从注册表里注销,后台的计时器就立刻停止了。4. 结合 190ms 的具体过程看具体的时间轴:175 ms:第 6 次触发完成。此时后台注册表里的 42 号定时器开始倒计时,预订在 210 ms($175 + 35$)再次触发。190 ms:外部调用了 cancelFn() $\rightarrow$ 执行 clearInterval(42) $\rightarrow$ 42 号定时器被立即销毁。210 ms:时间到了,但引擎的定时器列表里已经没有 42 号了,所以什么都不会发生,循环彻底终止。总结clearInterval(timer) 之所以能精准终止,是因为:setInterval 给了我们一个精准的操控句柄(Timer ID);闭包把这个句柄完整地留交给了 cancelFn;clearInterval 直接从 JavaScript 引擎底层撕毁了这个句柄对应的任务。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Fn = (...args: JSONValue[]) => void

  3. function cancellable(fn: Fn, args: JSONValue[], t: number): Function {
  4.     // 1. 按照题目要求,在 0ms 时立即调用一次 fn
  5.     fn(...args);
  6.    
  7.     // 2. 设置定时器,之后每隔 t 毫秒调用一次 fn
  8.     const timer = setInterval(() => {
  9.         fn(...args);
  10.     }, t);
  11.    
  12.     // 3. 返回一个取消函数,调用时清除定时器即可
  13.     return () => {
  14.         clearInterval(timer);
  15.     };
  16. };

  17. /**
  18. *  const result = [];
  19. *
  20. *  const fn = (x) => x * 2;
  21. *  const args = [4], t = 35, cancelTimeMs = 190;
  22. *
  23. *  const start = performance.now();
  24. *
  25. *  const log = (...argsArr) => {
  26. *      const diff = Math.floor(performance.now() - start);
  27. *      result.push({"time": diff, "returned": fn(...argsArr)});
  28. *  }
  29. *      
  30. *  const cancel = cancellable(log, args, t);
  31. *
  32. *  setTimeout(cancel, cancelTimeMs);
  33. *   
  34. *  setTimeout(() => {
  35. *      console.log(result); // [
  36. *                           //     {"time":0,"returned":8},
  37. *                           //     {"time":35,"returned":8},
  38. *                           //     {"time":70,"returned":8},
  39. *                           //     {"time":105,"returned":8},
  40. *                           //     {"time":140,"returned":8},
  41. *                           //     {"time":175,"returned":8}
  42. *                           // ]
  43. *  }, cancelTimeMs + t + 15)   
  44. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-22 09:43:49 | 只看该作者
全局:
2724. Sort By
Easy
conpanies icon
Companies
Given an array arr and a function fn, return a sorted array sortedArr. You can assume fn only returns numbers and those numbers determine the sort order of sortedArr. sortedArr must be sorted in ascending order by fn output.

You may assume that fn will never duplicate numbers for a given array.



Example 1:

Input: arr = [5, 4, 1, 2, 3], fn = (x) => x
Output: [1, 2, 3, 4, 5]
Explanation: fn simply returns the number passed to it so the array is sorted in ascending order.
Example 2:

Input: arr = [{"x": 1}, {"x": 0}, {"x": -1}], fn = (d) => d.x
Output: [{"x": -1}, {"x": 0}, {"x": 1}]
Explanation: fn returns the value for the "x" key. So the array is sorted based on that value.
Example 3:

Input: arr = [[3, 4], [5, 2], [10, 1]], fn = (x) => x[1]
Output: [[10, 1], [5, 2], [3, 4]]
Explanation: arr is sorted in ascending order by number at index=1.


Constraints:

arr is a valid JSON array
fn is a function that returns a number
1 <= arr.length <= 5 * 105
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Fn = (value: JSONValue) => number

  3. function sortBy(arr: JSONValue[], fn: Fn): JSONValue[] {
  4.     // 使用原生的 Array.prototype.sort 方法
  5.     // 通过比较 fn(a) 和 fn(b) 的差值来实现升序排序
  6.     return arr.sort((a, b) => fn(a) - fn(b));
  7. };
复制代码
代码解释Array.prototype.sort() 比较函数机制:JavaScript/TypeScript 原生的 sort 方法接收一个比较函数 (a, b) => number:若返回值 小于 0:a 会被排在 b 的前面。若返回值 大于 0:b 会被排在 a 的前面。若返回值 等于 0:a 和 b 的相对位置保持不变。按 fn 返回值升序:计算 fn(a) - fn(b):如果 fn(a) < fn(b),差值为负数,a 排在 b 前面,符合升序需求。如果 fn(a) > fn(b),差值为正数,b 排在 a 前面。原地排序与返回值:arr.sort() 会修改原数组并返回该数组的引用,完全符合题目的返回值要求。复杂度分析时间复杂度:$\mathcal{O}(N \log N)$,其中 $N$ 是数组 arr 的长度。JavaScript 引擎(如 V8)内部使用的排序算法(Timsort / pdqsort)在平均和最坏情况下的时间复杂度均为 $\mathcal{O}(N \log N)$。空间复杂度:$\mathcal{O}(1)$ 或 $\mathcal{O}(\log N)$,因为 sort 是原地排序算法,仅需额外的栈空间用于递归排序。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表