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

刷题记录帖子

🔗
 楼主| Myron2017 2026-9-14 09:48:51 | 只看该作者
全局:
LC. 4052. Cyclically Shift Rows and Columns
Solved
Easy
Hint
You are given an integer n, a 2D integer array grid of size n x n, and two integer arrays rowShift and colShift, each of length n, where:

rowShift[i] represents the number of positions to cyclically shift the ith row of grid to the left.
colShift[j] represents the number of positions to cyclically shift the jth column of grid upward.
First, cyclically shift each row according to rowShift, then cyclically shift each column of the resulting grid according to colShift.

Return the resulting grid after performing all the shifts.

A cyclic left shift of a row by k positions moves the element at column j to column (j - k + n) % n. All other rows remain unchanged.

A cyclic upward shift of a column by k positions moves the element at row i to row (i - k + n) % n. All other columns remain unchanged.



Example 1:

Input: n = 2, grid = [[1,2],[3,4]], rowShift = [1,0], colShift = [0,1]

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

Explanation:

The grid changes as follows:



Example 2:

Input: n = 3, grid = [[1,2,3],[4,5,6],[7,8,9]], rowShift = [1,2,0], colShift = [2,2,1]

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

Explanation:

The grid changes as follows:





Constraints:

1 <= n == grid.length == grid[i].length <= 10
1 <= grid[i][j] <= 100
rowShift.length == colShift.length == n
0 <= rowShift[i], colShift[i] < n

我的解法 simulation
  1. class Solution:
  2.     def cyclicShift(self, n: int, grid: list[list[int]], rowShift: list[int], colShift: list[int]) -> list[list[int]]:
  3.         # row shift
  4.         for i in range(n):
  5.             m = rowShift[i] % n
  6.             grid[i] = grid[i][m:] + grid[i][:m]

  7.         # col shift
  8.         for j in range(n):
  9.             p = colShift[j] % n
  10.             tmpCol = []
  11.             for k in range(p, n):
  12.                 tmpCol.append(grid[k][j])
  13.             for q in range(p):
  14.                 tmpCol.append(grid[q][j])
  15.             
  16.             for r in range(n):
  17.                 grid[r][j] = tmpCol[r]
  18.             
  19.         return grid
复制代码
改进,列移位代码稍微长了一点,用了三个 for 循环来拼接和写回。我们可以借助 Python 的列表推导式(List Comprehension),用和行移位同样的“切片思想”来简化列操作。

先提取本列的所有元素,然后按照 行来操作
  1. class Solution:
  2.     def cyclicShift(self, n: int, grid: list[list[int]], rowShift: list[int], colShift: list[int]) -> list[list[int]]:
  3.         # 1. 模拟行移位 (保留你原本优雅的写法)
  4.         for i in range(n):
  5.             m = rowShift[i] % n
  6.             grid[i] = grid[i][m:] + grid[i][:m]

  7.         # 2. 模拟列移位 (利用列表推导式提取列,切片后再写回)
  8.         for j in range(n):
  9.             p = colShift[j] % n
  10.             # 提取当前列的所有元素
  11.             col = [grid[i][j] for i in range(n)]
  12.             # 同样使用切片进行循环上移
  13.             shifted_col = col[p:] + col[:p]
  14.             # 写回网格
  15.             for i in range(n):
  16.                 grid[i][j] = shifted_col[i]
  17.             
  18.         return grid
复制代码
更进一步的写法

从“正向模拟”到“逆向找规律”的进阶。我们可以思考:对于最终结果中的任意一个格子 ans[i][j],它的值是从原矩阵的哪个位置跑过来的?

最终结果 ans[i][j] 经历了列上移。说明在列上移前,它处在原来的哪一行?
原行号 r = (i + colShift[j]) % n。

此时锁定在了原矩阵的第 r 行,最终的列号是 j。它经历了行左移。说明在行左移前,它处在原来的哪一列?
原列号 c = (j + rowShift[r]) % n。

得出一个惊艳的结论:最终答案矩阵的 ans[i][j] 就是原矩阵 grid[r][c] 的值!


一定要反过来先做列操作,再做行操作。

本质上

这是求矩阵操作的逆操作, “逆映射”(Inverse Mapping)
  1. class Solution:
  2.     def cyclicShift(self, n: int, grid: list[list[int]], rowShift: list[int], colShift: list[int]) -> list[list[int]]:
  3.         # 创建一个全为0的新 N x N 矩阵
  4.         ans = [[0] * n for _ in range(n)]
  5.         
  6.         for i in range(n):
  7.             for j in range(n):
  8.                 # 逆推 1: 因为列上移了 colShift[j],所以当前行 i 的元素原来在 r 行
  9.                 r = (i + colShift[j]) % n
  10.                 # 逆推 2: 因为第 r 行左移了 rowShift[r],所以当前列 j 的元素原来在 c 列
  11.                 c = (j + rowShift[r]) % n
  12.                
  13.                 # 直接赋值
  14.                 ans[i][j] = grid[r][c]
  15.                
  16.         return ans
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-16 10:29:18 | 只看该作者
全局:
LC. 4048. Count Values With Equally Spaced Occurrences I
You are given an integer array nums.

An integer x is called special if:

x appears exactly three times in nums.
All three occurrences of x are equally spaced in nums. In other words, if all occurrences of x are at indices i1 < i2 < i3, then i2 - i1 = i3 - i2.
Return the number of distinct special integers in nums.



Example 1:

Input: nums = [1,8,1,5,1,5,8,5]

Output: 2

Explanation:

1 is special because it occurs exactly three times at equally spaced indices 0, 2, and 4.
5 is special because it occurs exactly three times at equally spaced indices 3, 5, and 7.
8 is not special because it occurs only twice.
Therefore, the answer is 2.

Example 2:

Input: nums = [8,8,8,8]

Output: 0

Explanation:

8 is not special because it does not occur exactly three times. Therefore, the answer is 0.

Example 3:

Input: nums = [8,6,6,8,8]

Output: 0

Explanation:

8 occurs at indices 0, 3, and 4, which are not equally spaced. 6 occurs only twice. Therefore, no integer is special.



Constraints:

3 <= nums.length <= 100
1 <= nums[i] <= 100


我的解法
  1. class Solution:
  2.     def countSpecialIntegers(self, nums: list[int]) -> int:
  3.         n = len(nums)
  4.         freq = defaultdict(int)
  5.         position = defaultdict(list)

  6.         for ind,val in enumerate(nums):
  7.             freq[val] += 1
  8.             position[val].append(ind)

  9.         ans = 0
  10.         for k in freq.keys():
  11.             if freq[k] == 3 and (position[k][1] - position[k][0]) == (position[k][2] - position[k][1]):
  12.                 ans += 1

  13.         return ans
复制代码
优化


省去冗余哈希表:position[val] 记录了列表,len(position[val]) 本身就是频次,因此不需要额外的 freq 字典
  1. from collections import defaultdict


  2. class Solution:

  3.     def countSpecialIntegers(self, nums: list[int]) -> int:
  4.         # pos 记录每个数字出现的所有下标
  5.         pos = defaultdict(list)
  6.         for idx, val in enumerate(nums):
  7.             pos[val].append(idx)

  8.         ans = 0
  9.         for indices in pos.values():
  10.             # 1. 频次恰好为 3
  11.             # 2. 三个下标满足等差数列
  12.             if len(indices) == 3 and ( indices[1] - indices[0] == indices[2] - indices[1]):
  13.                 ans += 1

  14.         return ans
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-16 10:38:02 | 只看该作者
全局:
LC. 4049. Count Values With Equally Spaced Occurrences II

You are given an integer array nums.

An integer x is called special if:

x appears at least three times in nums.
All occurrences of x are equally spaced in nums. In other words, if all occurrences of x are at indices i1 < i2 < ... < im, then i2 - i1 = i3 - i2 = ... = im - im-1.
Return the number of distinct special integers in nums.



Example 1:

Input: nums = [1,8,1,5,1,5,8,5]

Output: 2

Explanation:

1 is special because it occurs at equally spaced indices 0, 2, and 4.
5 is special because it occurs at equally spaced indices 3, 5, and 7.
8 is not special because it occurs only twice.
Therefore, the answer is 2.

Example 2:

Input: nums = [8,8,8,8]

Output: 1

Explanation:

8 is special because it occurs at equally spaced indices 0, 1, 2, and 3. Therefore, the answer is 1.

Example 3:

Input: nums = [8,6,6,8,8]

Output: 0

Explanation:

8 occurs at indices 0, 3, and 4, which are not equally spaced. 6 occurs only twice. Therefore, no integer is special.



Constraints:

3 <= nums.length <= 105
1 <= nums[i] <= 109

我的解法
  1. class Solution:
  2.     def countSpecialIntegers(self, nums: list[int]) -> int:
  3.         n = len(nums)
  4.         freq = defaultdict(int)
  5.         position = defaultdict(list)
  6.         removed_values = set()

  7.         for ind,val in enumerate(nums):
  8.             if val not in removed_values:
  9.                 freq[val] += 1
  10.                 position[val].append(ind)
  11.                 if len(position[val]) >= 3:
  12.                     if (position[val][-1] - position[val][-2]) != (position[val][-2] - position[val][-3]):
  13.                         removed_values.add(val)
  14.                         del freq[val]

  15.         ans = 0

  16.         for k in freq.keys():
  17.             if freq[k] >= 3:
  18.                 ans += 1

  19.         return ans
复制代码
优化

冗余清理:

freq 哈希表完全冗余:len(position[val]) 本身就是频次,不需要单独维护一个 freq 字典和频繁进行 del freq[val] 操作。

状态管理偏复杂:同时维护了 freq、position、removed_values 三个集合,增加了理解成本。

面试首选:离线分组(Offline Grouping)
在面试中,这类题目更推荐使用“先收集所有下标,再统一校验”的离线写法。逻辑更加清晰,且不容易写出状态同步的 Bug。
  1. from collections import defaultdict


  2. class Solution:

  3.     def countSpecialIntegers(self, nums: list[int]) -> int:
  4.         # 1. 收集每个数字出现的所有下标
  5.         pos = defaultdict(list)
  6.         for idx, val in enumerate(nums):
  7.             pos[val].append(idx)

  8.         ans = 0

  9.         # 2. 遍历每个数字的下标列表进行校验
  10.         for indices in pos.values():
  11.             n_obs = len(indices)
  12.             # 出现次数不足 3 次,直接跳过
  13.             if n_obs < 3:
  14.                 continue

  15.             # 公差 (步长)
  16.             diff = indices[1] - indices[0]

  17.             # 校验后续所有相邻下标的差值是否均等于 diff
  18.             if all(
  19.                 indices[i] - indices[i - 1] == diff for i in range(2, n_obs)
  20.             ):
  21.                 ans += 1

  22.         return ans
复制代码
我的代码的优化

你原来的代码逻辑没问题,但在“状态维护”上有两个明显的冗余:

多余的计数器:freq 字典是多余的,因为 len(position[val]) 本身就是频次。

列表占用空间过大:你用 position[val].append(ind) 保存了该数字出现的所有历史下标。但仔细想一想,为了判断等差数列,我们真的需要所有的历史下标吗?

破局点:状态压缩 (O(1) 辅助空间)判断等差数列,只要知道数字的“上一次出现的下标”和“确立的公差(步长)”就够了!每来一个新元素,只需用它和上一个下标做差,比对公差即可。

面试级优化:流式状态机写法我们把每个数字的状态压缩为一个元组:(出现次数, 上一次的下标, 公差)。这样针对每个唯一数字,空间复杂度从 $O(N)$ 直接降到了 $O(1)$!
  1. class Solution:
  2.     def countSpecialIntegers(self, nums: list[int]) -> int:
  3.         # state 记录候选数字的状态 -> 格式: val: (count, last_idx, diff)
  4.         state = {}
  5.         # 记录已经破坏了等差规则的废弃数字
  6.         removed_values = set()

  7.         for ind, val in enumerate(nums):
  8.             # 1. 如果这个数已经被淘汰,直接无视
  9.             if val in removed_values:
  10.                 continue
  11.             
  12.             # 2. 状态转移
  13.             if val not in state:
  14.                 # 第一次出现:记录次数 1,当前下标,公差暂定 0
  15.                 state[val] = (1, ind, 0)
  16.             
  17.             else:
  18.                 count, last_idx, diff = state[val]
  19.                
  20.                 if count == 1:
  21.                     # 第二次出现:确立公差 diff,并更新最新下标
  22.                     state[val] = (2, ind, ind - last_idx)
  23.                
  24.                 else:
  25.                     # 第三次及以上出现:严格校验公差
  26.                     if ind - last_idx != diff:
  27.                         # 发现不符合等差,打入冷宫,并从候选集中清理以节省内存
  28.                         removed_values.add(val)
  29.                         del state[val]
  30.                     else:
  31.                         # 符合等差,更新次数和最新下标
  32.                         state[val] = (count + 1, ind, diff)

  33.         # 3. 统计最终符合要求(留在 state 里且频次 >= 3)的数字
  34.         ans = 0
  35.         for count, _, _ in state.values():
  36.             if count >= 3:
  37.                 ans += 1

  38.         return ans
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-20 11:56:50 | 只看该作者
全局:
2824. Count Pairs Whose Sum is Less than Target

Given a 0-indexed integer array nums of length n and an integer target, return the number of pairs (i, j) where 0 <= i < j < n and nums[i] + nums[j] < target.


Example 1:

Input: nums = [-1,1,2,3,1], target = 2
Output: 3
Explanation: There are 3 pairs of indices that satisfy the conditions in the statement:
- (0, 1) since 0 < 1 and nums[0] + nums[1] = 0 < target
- (0, 2) since 0 < 2 and nums[0] + nums[2] = 1 < target
- (0, 4) since 0 < 4 and nums[0] + nums[4] = 0 < target
Note that (0, 3) is not counted since nums[0] + nums[3] is not strictly less than the target.
Example 2:

Input: nums = [-6,2,5,-2,-7,-1,3], target = -2
Output: 10
Explanation: There are 10 pairs of indices that satisfy the conditions in the statement:
- (0, 1) since 0 < 1 and nums[0] + nums[1] = -4 < target
- (0, 3) since 0 < 3 and nums[0] + nums[3] = -8 < target
- (0, 4) since 0 < 4 and nums[0] + nums[4] = -13 < target
- (0, 5) since 0 < 5 and nums[0] + nums[5] = -7 < target
- (0, 6) since 0 < 6 and nums[0] + nums[6] = -3 < target
- (1, 4) since 1 < 4 and nums[1] + nums[4] = -5 < target
- (3, 4) since 3 < 4 and nums[3] + nums[4] = -9 < target
- (3, 5) since 3 < 5 and nums[3] + nums[5] = -3 < target
- (4, 5) since 4 < 5 and nums[4] + nums[5] = -8 < target
- (4, 6) since 4 < 6 and nums[4] + nums[6] = -4 < target


Constraints:

1 <= nums.length == n <= 50
-50 <= nums[i], target <= 50

当然可以暴力做,但是更好的方法是排序!

我们可以先对数组进行升序排序,然后使用左右双指针来高效统计:

排序: 将数组从小到大排序。

初始化双指针: left 指向数组最左端(最小值),right 指向数组最右端(最大值)。

向内收缩:

如果 nums[left] + nums[right] < target:
因为数组已经排序,nums[right] 是当前能取到的最大值。如果最小值和当前最大值的和都小于 target,那么 nums[left] 和 left 到 right 之间的任何一个数相加,必定也都小于 target。
所以,包含 nums[left] 的有效对数直接就是 right - left 个。统计完之后,让 left 向右移动一位(left++)。

如果 nums[left] + nums[right] >= target:
说明两数之和太大了。因为 nums[left] 已经是当前能拿出的最小值,说明是 nums[right] 太大了,它不可能和区间内的任何其他数字组合出小于 target 的结果,因此直接淘汰,将 right 向左移动一位(right--)。
  1. function countPairs(nums: number[], target: number): number {
  2.     // 1. 将数组升序排序
  3.     nums.sort((a, b) => a - b);
  4.    
  5.     let left = 0;
  6.     let right = nums.length - 1;
  7.     let count = 0;
  8.    
  9.     // 2. 双指针相向而行
  10.     while (left < right) {
  11.         if (nums[left] + nums[right] < target) {
  12.             // 如果满足条件,left 和它右边直到 right 的所有元素配对都符合要求
  13.             count += (right - left);
  14.             // 统计完 left 之后,left 就可以去匹配下一个数了
  15.             left++;
  16.         } else {
  17.             // 如果大于等于 target,说明 right 这个数太大了,淘汰它
  18.             right--;
  19.         }
  20.     }
  21.    
  22.     return count;
  23. }
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-20 12:01:21 | 只看该作者
全局:
2825. Make String a Subsequence Using Cyclic Increments
You are given two 0-indexed strings str1 and str2.

In an operation, you select a set of indices in str1, and for each index i in the set, increment str1[i] to the next character cyclically. That is 'a' becomes 'b', 'b' becomes 'c', and so on, and 'z' becomes 'a'.

Return true if it is possible to make str2 a subsequence of str1 by performing the operation at most once, and false otherwise.

Note: A subsequence of a string is a new string that is formed from the original string by deleting some (possibly none) of the characters without disturbing the relative positions of the remaining characters.



Example 1:

Input: str1 = "abc", str2 = "ad"
Output: true
Explanation: Select index 2 in str1.
Increment str1[2] to become 'd'.
Hence, str1 becomes "abd" and str2 is now a subsequence. Therefore, true is returned.
Example 2:

Input: str1 = "zc", str2 = "ad"
Output: true
Explanation: Select indices 0 and 1 in str1.
Increment str1[0] to become 'a'.
Increment str1[1] to become 'd'.
Hence, str1 becomes "ad" and str2 is now a subsequence. Therefore, true is returned.
Example 3:

Input: str1 = "ab", str2 = "d"
Output: false
Explanation: In this example, it can be shown that it is impossible to make str2 a subsequence of str1 using the operation at most once.
Therefore, false is returned.


Constraints:

1 <= str1.length <= 105
1 <= str2.length <= 105
str1 and str2 consist of only lowercase English letters.

题目说“最多执行一次操作,选择一些索引并将字符进行循环递增”。这听起来很绕,但翻译成大白话其实就是:str1 中的每一个字符,你要么保持它不变,要么把它变成字母表中的下一个字母(其中 'z' 会变成 'a')。

在这些条件下,我们要判断能不能把 str1 变成 str2 的父序列(即 str2 是它的子序列)。

核心思路拆解
这道题用贪心策略是最优解:对于 str2 中的每个字符,我们只需在 str1 中从左到右找到第一个能匹配上它的字符即可。能早匹配就早匹配,这样能给后面的字符留下更多的空间。

双指针:

指针 i 遍历原字符串 str1。

指针 j 遍历目标子序列 str2。

匹配条件: 对于当前的 str1[i] 和 str2[j],如果满足以下三种情况之一,就算匹配成功:

两个字符一模一样:str1[i] === str2[j]

str1[i] 按照字母表递增一位后等于 str2[j]:比如 'a' 变成 'b'。

循环递增的边界情况:str1[i] 是 'z',且 str2[j] 是 'a'。

推进指针:

如果匹配成功,j 向后移动一位,准备匹配 str2 的下一个字符。

无论是否匹配成功,i 都要向后移动一位,继续考察 str1 的下一个字符。

结束条件:

如果 j 走完了 str2(即 j === str2.length),说明 str2 里的字符全都按顺序匹配上了,返回 true。

如果 i 走完了 str1 但 j 还没走完,返回 false。
  1. function canMakeSubsequence(str1: string, str2: string): boolean {
  2.     let i = 0; // 遍历 str1 的指针
  3.     let j = 0; // 遍历 str2 的指针
  4.    
  5.     // 当两个指针都没有越界时进行循环
  6.     while (i < str1.length && j < str2.length) {
  7.         // 获取当前字符的 ASCII 码值,提高比较效率
  8.         const code1 = str1.charCodeAt(i);
  9.         const code2 = str2.charCodeAt(j);
  10.         
  11.         // 匹配条件:
  12.         // 1. 字符完全相等 (code1 === code2)
  13.         // 2. str1 递增一位后等于 str2 (code1 + 1 === code2)
  14.         // 3. str1 是 'z' (122) 且 str2 是 'a' (97)
  15.         if (
  16.             code1 === code2 ||
  17.             code1 + 1 === code2 ||
  18.             (code1 === 122 && code2 === 97)
  19.         ) {
  20.             j++; // 匹配成功,str2 指针后移
  21.         }
  22.         
  23.         i++; // str1 的指针永远后移
  24.     }
  25.    
  26.     // 如果 j 走到了 str2 的末尾,说明 str2 的所有字符都成功在 str1 中找到了匹配
  27.     return j === str2.length;
  28. }
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-21 08:27:27 | 只看该作者
全局:
function createCounter(n: number): () => number {
   
    return function() {
        
    }
}


/**
* const counter = createCounter(10)
* counter() // 10
* counter() // 11
* counter() // 12
*/
  1. function createCounter(n: number): () => number {
  2.     return function() {
  3.         return n++;
  4.     };
  5. }

  6. /**
  7. * const counter = createCounter(10)
  8. * counter() // 10
  9. * counter() // 11
  10. * counter() // 12
  11. */
复制代码
这个解法巧妙地使用了 JavaScript/TypeScript 中的闭包(Closure)特性。

以下是具体的步骤解释:

闭包的作用:
在 createCounter 函数内部,我们返回了一个新的匿名函数。这个内部函数“记住”了外部函数的变量 n。即使 createCounter 执行完毕并返回后,这个内部函数依然保存在内存中,并且能够持续访问和修改这个私有的 n 变量。

后缀递增(n++)的特性:
代码中使用了 n++,这是一个后缀递增运算符。它的执行顺序是:先返回变量当前的值,然后再将变量的值加 1。

执行流程举例(假设 n = 10):

第一次调用 counter():执行 return n++,先返回当前的 10,然后 n 在后台变成了 11。

第二次调用 counter():由于闭包的作用,它记得现在的 n 是 11。执行 return n++,返回 11,然后 n 变成了 12。

第三次调用 counter():返回 12,n 变成 13。以此类推。

这样只需短短一行代码就能完美满足题目要求,而且不需要声明额外的内部变量。
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-21 08:30:00 | 只看该作者
全局:
2619. Array Prototype Last
Easy
conpanies icon
Companies
Hint
Write code that enhances all arrays such that you can call the array.last() method on any array and it will return the last element. If there are no elements in the array, it should return -1.

You may assume the array is the output of JSON.parse.



Example 1:

Input: nums = [null, {}, 3]
Output: 3
Explanation: Calling nums.last() should return the last element: 3.
Example 2:

Input: nums = []
Output: -1
Explanation: Because there are no elements, return -1.


Constraints:

arr is a valid JSON array
0 <= arr.length <= 1000
  1. /**
  2. * [url=home.php?mod=space&uid=160137]@return[/url] {null|boolean|number|string|Array|Object}
  3. */
  4. Array.prototype.last = function() {
  5.     if (this.length === 0) {
  6.         return -1;
  7.     }
  8.     return this[this.length - 1];
  9. };

  10. /**
  11. * const arr = [1, 2, 3];
  12. * arr.last(); // 3
  13. */
复制代码
这道题要求我们在所有数组的原型(Array.prototype)上添加一个自定义的方法 last()。

关于 this 关键字:
当我们在 Array.prototype 上定义方法时,函数内部的 this 会指向调用该方法的那个具体数组对象。例如,当执行 [1, 2, 3].last() 时,this 指代的就是 [1, 2, 3] 这个数组。

逻辑判断:

首先,我们通过 this.length === 0 来检查数组是否为空。如果为空,根据题意直接返回 -1。

如果数组不为空,我们就通过 this[this.length - 1] 获取数组的最后一个元素并返回。

提示:在 TypeScript 的环境中使用这个方法时,通常还需要在全局命名空间中声明合并(Declaration Merging)来告诉 TypeScript Array 接口现在多了一个 last 方法,但力扣(LeetCode)的代码框中直接用如上 JavaScript 写法即可通过。
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-21 08:33:15 | 只看该作者
全局:
2618. Check if Object Instance of Class
Medium
conpanies icon
Companies
Hint
Write a function that checks if a given value is an instance of a given class or superclass. For this problem, an object is considered an instance of a given class if that object has access to that class's methods.

There are no constraints on the data types that can be passed to the function. For example, the value or the class could be undefined.



Example 1:

Input: func = () => checkIfInstanceOf(new Date(), Date)
Output: true
Explanation: The object returned by the Date constructor is, by definition, an instance of Date.
Example 2:

Input: func = () => { class Animal {}; class Dog extends Animal {}; return checkIfInstanceOf(new Dog(), Animal); }
Output: true
Explanation:
class Animal {};
class Dog extends Animal {};
checkIfInstanceOf(new Dog(), Animal); // true

Dog is a subclass of Animal. Therefore, a Dog object is an instance of both Dog and Animal.
Example 3:

Input: func = () => checkIfInstanceOf(Date, Date)
Output: false
Explanation: A date constructor cannot logically be an instance of itself.
Example 4:

Input: func = () => checkIfInstanceOf(5, Number)
Output: true
Explanation: 5 is a Number. Note that the "instanceof" keyword would return false. However, it is still considered an instance of Number because it accesses the Number methods. For example "toFixed()".

这道题让我们手动实现类似 JavaScript 中 instanceof 操作符的功能,但有一个关键的拓展:它必须支持基本数据类型(Primitives)。

在原生的 JavaScript 中,如果我们检查 5 instanceof Number,会返回 false,因为 5 是一个基本数据类型,而不是对象。但题目要求(如例4所示),我们应该认为 5 是 Number 的实例,因为它能调用 Number 的方法(比如 5.toFixed())。

为了实现这一点,逻辑步骤如下:

边界条件处理:

null 和 undefined 在 JavaScript 中没有任何方法,也没有原型链,所以直接排除。

classFunction 必须是一个函数。如果传入的是一个普通对象或者基本数据类型,它不可能作为构造函数,直接返回 false。

遍历原型链查找:

在 JavaScript 中,方法是通过“原型链(Prototype Chain)”继承的。一个对象之所以能调用某个类的方法,是因为那个类的 prototype 存在于该对象的原型链上。

即使是基本数据类型(如数字 5),当我们调用 Object.getPrototypeOf(5) 时,JavaScript 引擎会在底层将其包装为对象,并正确返回 Number.prototype。

我们使用一个 while 循环,不断通过 Object.getPrototypeOf() 往上层追溯原型,直到原型链的尽头(null)。

在追溯的过程中,只要发现 currentProto === classFunction.prototype,就说明该对象继承了该类的方法,返回 true。

(注:如果你想追求极简,这道题也可以写成 return Object(obj) instanceof classFunction;,但在某些极端边缘测试用例中,手动遍历原型链的方法能提供最稳定的保障。)
  1. function checkIfInstanceOf(obj: any, classFunction: any): boolean {
  2.     // 排除 obj 为 null 或 undefined 的情况,因为它们没有原型
  3.     // 同时也需要确保 classFunction 是一个函数(构造函数)
  4.     if (obj === null || obj === undefined || typeof classFunction !== 'function') {
  5.         return false;
  6.     }

  7.     // 获取当前对象的原型
  8.     let currentProto = Object.getPrototypeOf(obj);

  9.     // 顺着原型链向上查找
  10.     while (currentProto !== null) {
  11.         // 如果原型链上的某一层和给定的类的原型相等,说明是它的实例
  12.         if (currentProto === classFunction.prototype) {
  13.             return true;
  14.         }
  15.         // 继续向上层原型查找
  16.         currentProto = Object.getPrototypeOf(currentProto);
  17.     }

  18.     return false;
  19. }

  20. /**
  21. * checkIfInstanceOf(new Date(), Date); // true
  22. */
复制代码
回复

使用道具 举报

🔗
 楼主| 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. */
复制代码
回复

使用道具 举报

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

本版积分规则

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