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

刷题记录帖子

🔗
 楼主| Myron2017 2026-9-28 08:54:45 | 只看该作者
全局:
2757. Generate Circular Array Values
Medium
Given a circular array arr and an integer startIndex, return a generator object gen that yields values from arr.

The first time gen.next() is called on the generator, it should should yield arr[startIndex].

Each subsequent time gen.next() is called, an integer jump will be passed into the function (Ex: gen.next(-3)).

If jump is positive, the index should increase by that value, however if the current index is the last index, it should instead jump to the first index.
If jump is negative, the index should decrease by the magnitude of that value, however if the current index is the first index, it should instead jump to the last index.


Example 1:

Input: arr = [1,2,3,4,5], steps = [1,2,6], startIndex = 0
Output: [1,2,4,5]
Explanation:  
const gen = cycleGenerator(arr, startIndex);
gen.next().value;  // 1, index = startIndex = 0
gen.next(1).value; // 2, index = 1, 0 -> 1
gen.next(2).value; // 4, index = 3, 1 -> 2 -> 3
gen.next(6).value; // 5, index = 4, 3 -> 4 -> 0 -> 1 -> 2 -> 3 -> 4
Example 2:

Input: arr = [10,11,12,13,14,15], steps = [1,4,0,-1,-3], startIndex = 1
Output: [11,12,10,10,15,12]
Explanation:
const gen = cycleGenerator(arr, startIndex);
gen.next().value;   // 11, index = 1
gen.next(1).value;  // 12, index = 2
gen.next(4).value;  // 10, index = 0
gen.next(0).value;  // 10, index = 0
gen.next(-1).value; // 15, index = 5
gen.next(-3).value; // 12, index = 2
Example 3:

Input: arr = [2,4,6,7,8,10], steps = [-4,5,-3,10], startIndex = 3
Output: [7,10,8,4,10]
Explanation:  
const gen = cycleGenerator(arr, startIndex);
gen.next().value   // 7,  index = 3
gen.next(-4).value // 10, index = 5
gen.next(5).value  // 8,  index = 4
gen.next(-3).value // 4,  index = 1  
gen.next(10).value // 10, index = 5


Constraints:

1 <= arr.length <= 104
1 <= steps.length <= 100
-104 <= steps[i], arr[i] <= 104
0 <= startIndex < arr.length


题目理解

给定一个环形数组 arr 和起始下标 startIndex,要返回一个生成器对象 gen:

第一次调用 gen.next() 时,产出(yield)arr[startIndex]。
之后每次调用 gen.next(jump) 时,会传入一个整数 jump,当前下标要移动 jump 步,然后产出新位置的值:
jump 为正:下标向后移动;走到最后一个元素后,下一步回到第一个元素。
jump 为负:下标向前移动;走到第一个元素后,再往前一步会到最后一个元素。
jump 为 0:原地不动,再次产出当前值。

说白了就是在环上移动,本质是对数组长度取模。例如例 1 中,arr 长度为 5,从下标 3 跳 6 步:3 + 6 = 9,9 % 5 = 4,落在下标 4,值为 5。

先复习两个关键知识点

1. 生成器的 next(value) 传参机制

typescript
const jump = yield arr[index];

这行代码做了两件事:

yield arr[index]:把 arr[index] 作为 next() 的返回值产出,然后暂停在这里。
下一次调用 gen.next(x) 时,暂停处的 yield 表达式会求值为 x,于是 jump 就拿到了 x,然后继续往下执行。

注意:第一次调用 gen.next() 传入的参数会被忽略,因为此时生成器还没有执行到任何 yield。这正好符合题意:第一次调用不带参数,只产出起始位置的值。

2. JavaScript 中负数取模

JS 的 % 运算结果符号和被除数一致,例如 -1 % 6 === -1,而不是 5。所以要处理负数下标,需要写成:

typescript
((x % n) + n) % n

先取模得到 (-n, n) 范围内的值,加 n 让它变成正数,再取模一次收敛到 [0, n)。
  1. function* cycleGenerator(arr: number[], startIndex: number): Generator<number, void, number> {
  2.     const n = arr.length;
  3.     let index = startIndex;

  4.     while (true) {
  5.         // 产出当前位置的值,并暂停;下一次 next(jump) 传入的值会赋给 jump
  6.         const jump = yield arr[index];

  7.         // 在环形数组上移动,同时兼容负数和超过数组长度的跳跃
  8.         index = (((index + jump) % n) + n) % n;
  9.     }
  10. }

  11. /**
  12. *  const gen = cycleGenerator([1,2,3,4,5], 0);
  13. *  gen.next().value  // 1
  14. *  gen.next(1).value // 2
  15. *  gen.next(2).value // 4
  16. *  gen.next(6).value // 5
  17. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 08:57:24 | 只看该作者
全局:
2756. Query Batching
Hard
Batching multiple small queries into a single large query can be a useful optimization. Write a class QueryBatcher that implements this functionality.

The constructor should accept two parameters:

An asynchronous function queryMultiple which accepts an array of string keys input. It will resolve with an array of values that is the same length as the input array. Each index corresponds to the value associated with input[i]. You can assume the promise will never reject.
A throttle time in milliseconds t.
The class has a single method.

async getValue(key). Accepts a single string key and resolves with a single string value. The keys passed to this function should eventually get passed to the queryMultiple function. queryMultiple should never be called consecutively within t milliseconds. The first time getValue is called, queryMultiple should immediately be called with that single key. If after t milliseconds, getValue had been called again, all the passed keys should be passed to queryMultiple and ultimately returned. You can assume every key passed to this method is unique.
The following diagram illustrates how the throttling algorithm works. Each rectangle represents 100ms. The throttle time is 400ms.

Throttle info



Example 1:

Input:
queryMultiple = async function(keys) {
  return keys.map(key => key + '!');
}
t = 100
calls = [
{"key": "a", "time": 10},
{"key": "b", "time": 20},
{"key": "c", "time": 30}
]
Output: [
{"resolved": "a!", "time": 10},
{"resolved": "b!", "time": 110},
{"resolved": "c!", "time": 110}
]
Explanation:
const batcher = new QueryBatcher(queryMultiple, 100);
setTimeout(() => batcher.getValue('a'), 10); // "a!" at t=10ms
setTimeout(() => batcher.getValue('b'), 20); // "b!" at t=110ms
setTimeout(() => batcher.getValue('c'), 30); // "c!" at t=110ms

queryMultiple simply adds an "!" to the key
At t=10ms, getValue('a') is called, queryMultiple(['a']) is immediately called and the result is immediately returned.
At t=20ms, getValue('b') is called but the query is queued
At t=30ms, getValue('c') is called but the query is queued.
At t=110ms, queryMultiple(['a', 'b']) is called and the results are immediately returned.
Example 2:

Input:
queryMultiple = async function(keys) {
  await new Promise(res => setTimeout(res, 100));
  return keys.map(key => key + '!');
}
t = 100
calls = [
{"key": "a", "time": 10},
{"key": "b", "time": 20},
{"key": "c", "time": 30}
]
Output: [
  {"resolved": "a!", "time": 110},
  {"resolved": "b!", "time": 210},
  {"resolved": "c!", "time": 210}
]
Explanation:
This example is the same as example 1 except there is a 100ms delay in queryMultiple. The results are the same except the promises resolve 100ms later.
Example 3:

Input:
queryMultiple = async function(keys) {
  await new Promise(res => setTimeout(res, keys.length * 100));
  return keys.map(key => key + '!');
}
t = 100
calls = [
  {"key": "a", "time": 10},
  {"key": "b", "time": 20},
  {"key": "c", "time": 30},
  {"key": "d", "time": 40},
  {"key": "e", "time": 250}
  {"key": "f", "time": 300}
]
Output: [
  {"resolved":"a!","time":110},
  {"resolved":"e!","time":350},
  {"resolved":"b!","time":410},
  {"resolved":"c!","time":410},
  {"resolved":"d!","time":410},
  {"resolved":"f!","time":450}
]
Explanation:
queryMultiple(['a']) is called at t=10ms, it is resolved at t=110ms
queryMultiple(['b', 'c', 'd']) is called at t=110ms, it is resolved at 410ms
queryMultiple(['e']) is called at t=250ms, it is resolved at 350ms
queryMultiple(['f']) is called at t=350ms, it is resolved at 450ms


Constraints:

0 <= t <= 1000
0 <= calls.length <= 10
1 <= key.length <= 100
All keys are unique


题目理解

要实现一个 QueryBatcher 类,把多次零散的单个查询合并成一次批量查询,同时对批量查询的调用频率做节流:

构造函数接收批量查询函数 queryMultiple 和节流时间 t。
getValue(key) 接收单个 key,返回一个 Promise,最终 resolve 成这个 key 对应的值。
第一次调用 getValue 时,立即用这一个 key 调用 queryMultiple。
之后两次 queryMultiple 调用之间,至少间隔 t 毫秒。在节流期间进来的 key 先排队,等节流结束后,一次性把队列里所有 key 传给 queryMultiple。

几个容易忽略的关键点,都能从例 3 看出来:

节流时间从「调用 queryMultiple 的时刻」开始算,而不是从它 resolve 的时刻算。 例 3 中 ['a'] 在 t=10 调用,t=110 才 resolve,但 ['b','c','d'] 在 t=110 就被调用了,与 a 是否完成无关(这里恰好相等,看后面的 e 更明显)。
多个 queryMultiple 可以同时在途。 ['b','c','d'] 在 t=110 调用,要到 t=410 才返回;而 ['e'] 在 t=250 就被调用了,并没有等前一个返回。
节流结束时如果队列为空,就回到「空闲」状态,之后再来的 key 可以立即触发查询(例如 t=250 的 e)。
思路

维护三样东西:

queue:等待查询的项,每项包含 key 和这个 key 对应 Promise 的 resolve 函数。
throttled:布尔值,表示当前是否处于节流期。
flush():发起一次批量查询的方法。

流程:

getValue(key):创建一个 Promise,把 {key, resolve} 放入队列;如果当前不在节流期,立刻 flush()。

flush():

取出队列里的所有项,清空队列。
把 throttled 设为 true,进入节流期。
调用 queryMultiple(所有 key),结果返回后,按下标依次 resolve 每一项(返回数组的第 i 个值对应第 i 个 key)。
同时启动一个 t 毫秒的定时器:定时器触发时结束节流期,如果队列里有新排队的项,就再次 flush();如果队列为空,就什么也不做,安静地回到空闲状态。

定时器只在有 flush 发生时才会被创建,队列空了就不会继续设置,所以不会出现永远不退出的定时器。
  1.   


  2. type QueryMultiple = (keys: string[]) => Promise<string[]>

  3. class QueryBatcher {
  4.     private queryMultiple: QueryMultiple;
  5.     private t: number;
  6.     private queue: { key: string; resolve: (value: string) => void }[] = [];
  7.     private throttled = false;

  8.     constructor(queryMultiple: QueryMultiple, t: number) {
  9.         this.queryMultiple = queryMultiple;
  10.         this.t = t;
  11.     }

  12.     private flush(): void {
  13.         // 取出当前队列里的全部请求,并清空队列
  14.         const batch = this.queue;
  15.         this.queue = [];

  16.         // 进入节流期:从"调用 queryMultiple"这一刻开始计时
  17.         this.throttled = true;

  18.         // 发起批量查询,返回后按下标把结果分发给各自的 Promise
  19.         this.queryMultiple(batch.map(item => item.key)).then(values => {
  20.             batch.forEach((item, i) => item.resolve(values[i]));
  21.         });

  22.         // t 毫秒后结束节流期;若期间有新请求排队,则立即发起下一批
  23.         setTimeout(() => {
  24.             this.throttled = false;
  25.             if (this.queue.length > 0) {
  26.                 this.flush();
  27.             }
  28.         }, this.t);
  29.     }

  30.     async getValue(key: string): Promise<string> {
  31.         return new Promise<string>(resolve => {
  32.             this.queue.push({ key, resolve });
  33.             // 不在节流期就立即查询;否则留在队列里等定时器触发
  34.             if (!this.throttled) {
  35.                 this.flush();
  36.             }
  37.         });
  38.     }
  39. };

  40. /**
  41. * async function queryMultiple(keys) {
  42.  *   return keys.map(key => key + '!');
  43. * }
  44. *
  45. * const batcher = new QueryBatcher(queryMultiple, 100);
  46. * batcher.getValue('a').then(console.log); // resolves "a!" at t=0ms
  47. * batcher.getValue('b').then(console.log); // resolves "b!" at t=100ms
  48. * batcher.getValue('c').then(console.log); // resolves "c!" at t=100ms
  49. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 08:59:21 | 只看该作者
全局:
2755. Deep Merge of Two Objects
Medium
conpanies icon
Companies
Given two values obj1 and obj2, return a deepmerged value.

Values should be deepmerged according to these rules:

If the two values are objects, the resulting object should have all the keys that exist on either object. If a key belongs to both objects, deepmerge the two associated values. Otherwise, add the key-value pair to the resulting object.
If the two values are arrays, the resulting array should be the same length as the longer array. Apply the same logic as you would with objects, but treat the indices as keys.
Otherwise the resulting value is obj2.
You can assume obj1 and obj2 are the output of JSON.parse().



Example 1:

Input: obj1 = {"a": 1, "c": 3}, obj2 = {"a": 2, "b": 2}
Output: {"a": 2, "c": 3, "b": 2}
Explanation: The value of obj1["a"] changed to 2 because if both objects have the same key and their value is not an array or object then we change the obj1 value to the obj2 value. Key "b" with value was added to obj1 as it doesn't exist in obj1.
Example 2:

Input: obj1 = [{}, 2, 3], obj2 = [[], 5]
Output: [[], 5, 3]
Explanation: result[0] = obj2[0] because obj1[0] and obj2[0] have different types. result[2] = obj1[2] because obj2[2] does not exist.
Example 3:

Input:
obj1 = {"a": 1, "b": {"c": [1 , [2, 7], 5], "d": 2}},
obj2 = {"a": 1, "b": {"c": [6, [6], [9]], "e": 3}}
Output: {"a": 1, "b": {"c": [6, [6, 7], [9]], "d": 2, "e": 3}}
Explanation:
Arrays obj1["b"]["c"] and obj2["b"]["c"] have been merged in way that obj2 values overwrite obj1 values deeply only if they are not arrays or objects.
obj2["b"]["c"] has key "e" that obj1 doesn't have so it's added to obj1.
Example 4:

Input: obj1 = true, obj2 = null
Output: null


Constraints:

obj1 and obj2 are valid JSON values
1 <= JSON.stringify(obj1).length <= 5 * 105
1 <= JSON.stringify(obj2).length <= 5 * 105


题目理解

给定两个 JSON 值 obj1 和 obj2,返回它们深度合并后的结果,规则分三种情况:

两个都是对象:结果包含两个对象的所有键。某个键只在一边出现,就直接取那一边的值;两边都有,则对这两个值递归深度合并。
两个都是数组:结果长度等于较长那个数组的长度,处理方式和对象一样,只是把下标当作键。
其他所有情况(类型不同、或者是基本类型):直接取 obj2。

几个容易踩坑的地方,都能从例子中看出来:

数组和对象是不同类型。 例 2 中 obj1[0] = {}、obj2[0] = [],一个是对象一个是数组,类型不同,所以直接取 obj2[0],结果是 []。
null 不是对象。 JS 中 typeof null === 'object',所以判断对象时必须额外排除 null。例 4:true 和 null 合并,走「其他情况」,结果是 null。
数组合并时,用长度判断某个下标是否存在,而不是判断值是否为 undefined,因为 null 也是合法的 JSON 值。
思路

写一个递归函数 deepMerge(obj1, obj2):

数组 + 数组:

取 len = max(obj1.length, obj2.length),遍历每个下标 i:
i >= obj2.length:obj2 没有这个位置,取 obj1[i]
i >= obj1.length:obj1 没有这个位置,取 obj2[i]
两边都有:递归 deepMerge(obj1[i], obj2[i])

对象 + 对象:

先把 obj1 浅拷贝一份作为结果,保留只在 obj1 中出现的键。
遍历 obj2 的每个键:如果 obj1 里自身也有这个键,递归合并;否则直接放入 obj2 的值。

其他情况:返回 obj2。

这里判断键是否存在用的是 hasOwnProperty,而不是 in。因为 in 会沿着原型链查找,"toString" in {} 是 true,如果 obj2 恰好有个叫 toString 或 constructor 的键,就会误判。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };

  2. function deepMerge(obj1: JSONValue, obj2: JSONValue): JSONValue {
  3.     // 类型守卫:是"普通对象"(排除 null 和数组)
  4.     const isPlainObject = (v: JSONValue): v is { [key: string]: JSONValue } =>
  5.         typeof v === 'object' && v !== null && !Array.isArray(v);

  6.     // 情况 1:两个都是数组,把下标当作键来合并
  7.     if (Array.isArray(obj1) && Array.isArray(obj2)) {
  8.         const len = Math.max(obj1.length, obj2.length);
  9.         const result: JSONValue[] = [];
  10.         for (let i = 0; i < len; i++) {
  11.             if (i >= obj2.length) {
  12.                 result.push(obj1[i]);                       // 只有 obj1 有
  13.             } else if (i >= obj1.length) {
  14.                 result.push(obj2[i]);                       // 只有 obj2 有
  15.             } else {
  16.                 result.push(deepMerge(obj1[i], obj2[i]));   // 两边都有,递归
  17.             }
  18.         }
  19.         return result;
  20.     }

  21.     // 情况 2:两个都是对象,按键合并
  22.     if (isPlainObject(obj1) && isPlainObject(obj2)) {
  23.         const result: { [key: string]: JSONValue } = { ...obj1 };
  24.         for (const key of Object.keys(obj2)) {
  25.             result[key] = Object.prototype.hasOwnProperty.call(obj1, key)
  26.                 ? deepMerge(obj1[key], obj2[key])           // 两边都有,递归
  27.                 : obj2[key];                                // 只有 obj2 有
  28.         }
  29.         return result;
  30.     }

  31.     // 情况 3:类型不同或者是基本类型,直接取 obj2
  32.     return obj2;
  33. }

  34. /**
  35. * let obj1 = {"a": 1, "c": 3}, obj2 = {"a": 2, "b": 2};
  36. * deepMerge(obj1, obj2); // {"a": 2, "c": 3, "b": 2}
  37. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 09:02:34 | 只看该作者
全局:
2754. Bind Function to Context
Medium
Hint
Enhance all functions to have the bindPolyfill method. When bindPolyfill is called with a passed object obj, that object becomes the this context for the function.

For example, if you had the code:

function f() {
  console.log('My context is ' + this.ctx);
}
f();
The output would be "My context is undefined". However, if you bound the function:

function f() {
  console.log('My context is ' + this.ctx);
}
const boundFunc = f.boundPolyfill({ "ctx": "My Object" })
boundFunc();
The output should be "My context is My Object".

You may assume that a single non-null object will be passed to the bindPolyfill method.

Please solve it without the built-in Function.bind method.



Example 1:

Input:
fn = function f(multiplier) {
  return this.x * multiplier;
}
obj = {"x": 10}
inputs = [5]
Output: 50
Explanation:
const boundFunc = f.bindPolyfill({"x": 10});
boundFunc(5); // 50
A multiplier of 5 is passed as a parameter.
The context is set to {"x": 10}.
Multiplying those two numbers yields 50.
Example 2:

Input:
fn = function speak() {
  return "My name is " + this.name;
}
obj = {"name": "Kathy"}
inputs = []
Output: "My name is Kathy"
Explanation:
const boundFunc = f.bindPolyfill({"name": "Kathy"});
boundFunc(); // "My name is Kathy"


Constraints:

obj is a non-null object
0 <= inputs.length <= 100


Can you solve it without using any built-in methods?


题目理解

要给所有函数(也就是 Function.prototype)添加一个 bindPolyfill 方法,效果类似内置的 bind:

调用 fn.bindPolyfill(obj) 时,返回一个新函数。
调用这个新函数时,原函数 fn 内部的 this 指向 obj,并且新函数收到的所有参数都要原样传给 fn,返回值也要原样返回。
不能使用内置的 Function.bind。进阶要求:不使用任何内置方法(也就是连 call、apply 也不用)。

例 1:f 里用到 this.x * multiplier,绑定 {x: 10} 后调用 boundFunc(5),this.x 是 10,结果是 50。

(题面示例代码里写的 f.boundPolyfill 是笔误,实际方法名是 bindPolyfill。)

核心知识:this 是怎么确定的

普通函数的 this 取决于调用方式。其中一条规则是:以对象的方法形式调用时,this 就是点号前面的那个对象。

typescript
obj.method();   // method 内部的 this === obj

这就是不用 call/apply 的关键:临时把函数挂到 obj 上,作为它的方法来调用,this 自然指向 obj,调用完再把它删掉。

思路
在 bindPolyfill 中先保存原函数:const fn = this(此时 this 就是调用 bindPolyfill 的那个函数)。
返回一个新函数,每次被调用时:
创建一个唯一的 Symbol 作为临时属性名;
obj[key] = fn,把原函数挂到 obj 上;
用 obj[key](...args) 以方法形式调用,此时 fn 里的 this === obj;
无论成功还是抛出异常,最后都 delete obj[key],不留痕迹。

为什么用 Symbol 而不是普通字符串?Symbol() 每次返回的值都是全局唯一的,不会和 obj 上已有的属性冲突,也不会被 Object.keys 等枚举到。并且每次调用都生成新的 Symbol,所以递归调用或嵌套调用时互不干扰。
  1. type Fn = (...args) => any

  2. interface Function {
  3.     bindPolyfill(obj: Record<any, any>): Fn;
  4. }

  5. Function.prototype.bindPolyfill = function(obj): Fn {
  6.     const fn = this;                // 保存原函数
  7.     const context: any = obj;       // 声明为 any,避免 symbol 索引报错

  8.     return function(...args) {
  9.         const key = Symbol();       // 唯一的临时属性名
  10.         context[key] = fn;          // 把函数临时挂到 obj 上

  11.         try {
  12.             // 以 obj 的方法形式调用,fn 内部的 this 就是 obj
  13.             return context[key](...args);
  14.         } finally {
  15.             delete context[key];    // 无论是否抛异常都清理
  16.         }
  17.     };
  18. }
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 09:34:37 | 只看该作者
全局:
2694. Event Emitter
Medium
conpanies icon
Companies
Design an EventEmitter class. This interface is similar (but with some differences) to the one found in Node.js or the Event Target interface of the DOM. The EventEmitter should allow for subscribing to events and emitting them.

Your EventEmitter class should have the following two methods:

subscribe - This method takes in two arguments: the name of an event as a string and a callback function. This callback function will later be called when the event is emitted.
An event should be able to have multiple listeners for the same event. When emitting an event with multiple callbacks, each should be called in the order in which they were subscribed. An array of results should be returned. You can assume no callbacks passed to subscribe are referentially identical.
The subscribe method should also return an object with an unsubscribe method that enables the user to unsubscribe. When it is called, the callback should be removed from the list of subscriptions and undefined should be returned.
emit - This method takes in two arguments: the name of an event as a string and an optional array of arguments that will be passed to the callback(s). If there are no callbacks subscribed to the given event, return an empty array. Otherwise, return an array of the results of all callback calls in the order they were subscribed.


Example 1:

Input:
actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"],
values = [[], ["firstEvent"], ["firstEvent", "function cb1() { return 5; }"],  ["firstEvent", "function cb1() { return 6; }"], ["firstEvent"]]
Output: [[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
Explanation:
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], no callback are subscribed yet
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], returns the output of cb1 and cb2
Example 2:

Input:
actions = ["EventEmitter", "subscribe", "emit", "emit"],
values = [[], ["firstEvent", "function cb1(...args) { return args.join(','); }"], ["firstEvent", [1,2,3]], ["firstEvent", [3,4,6]]]
Output: [[],["subscribed"],["emitted",["1,2,3"]],["emitted",["3,4,6"]]]
Explanation: Note that the emit method should be able to accept an OPTIONAL array of arguments.

const emitter = new EventEmitter();
emitter.subscribe("firstEvent, function cb1(...args) { return args.join(','); });
emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"]
emitter.emit("firstEvent", [3, 4, 6]); // ["3,4,6"]
Example 3:

Input:
actions = ["EventEmitter", "subscribe", "emit", "unsubscribe", "emit"],
values = [[], ["firstEvent", "(...args) => args.join(',')"], ["firstEvent", [1,2,3]], [0], ["firstEvent", [4,5,6]]]
Output: [[],["subscribed"],["emitted",["1,2,3"]],["unsubscribed",0],["emitted",[]]]
Explanation:
const emitter = new EventEmitter();
const sub = emitter.subscribe("firstEvent", (...args) => args.join(','));
emitter.emit("firstEvent", [1, 2, 3]); // ["1,2,3"]
sub.unsubscribe(); // undefined
emitter.emit("firstEvent", [4, 5, 6]); // [], there are no subscriptions
Example 4:

Input:
actions = ["EventEmitter", "subscribe", "subscribe", "unsubscribe", "emit"],
values = [[], ["firstEvent", "x => x + 1"], ["firstEvent", "x => x + 2"], [0], ["firstEvent", [5]]]
Output: [[],["subscribed"],["subscribed"],["unsubscribed",0],["emitted",[7]]]
Explanation:
const emitter = new EventEmitter();
const sub1 = emitter.subscribe("firstEvent", x => x + 1);
const sub2 = emitter.subscribe("firstEvent", x => x + 2);
sub1.unsubscribe(); // undefined
emitter.emit("firstEvent", [5]); // [7]


Constraints:

1 <= actions.length <= 10
values.length === actions.length
All test cases are valid, e.g. you don't need to handle scenarios when unsubscribing from a non-existing subscription.
There are only 4 different actions: EventEmitter, emit, subscribe, and unsubscribe.
The EventEmitter action doesn't take any arguments.
The emit action takes between either 1 or 2 arguments. The first argument is the name of the event we want to emit, and the 2nd argument is passed to the callback functions.
The subscribe action takes 2 arguments, where the first one is the event name and the second is the callback function.
The unsubscribe action takes one argument, which is the 0-indexed order of the subscription made before.


题目解读

要实现一个简化版的事件发射器 EventEmitter,类似 Node.js 的 EventEmitter,有两个方法:

subscribe(eventName, callback):订阅事件。
同一个事件可以有多个回调,触发时按订阅顺序依次执行。
返回一个对象,里面有 unsubscribe 方法。调用它会把这个回调从订阅列表里删掉,并返回 undefined。
emit(eventName, args = []):触发事件。
args 是可选的参数数组,会展开后传给每个回调。
如果该事件没有任何订阅者,返回 []。
否则返回所有回调返回值组成的数组,顺序与订阅顺序一致。

题目还说明不会出现两个引用完全相同的回调,所以可以直接用回调函数本身来定位要删除的订阅。示例里的 [0]、"subscribed" 等只是测试框架的输出格式,不用自己实现。

解题思路

用一个 Map<string, Callback[]> 保存数据:key 是事件名,value 是该事件的回调数组,数组顺序就是订阅顺序。

subscribe:如果这个事件还没有数组就先创建,然后把回调 push 进去。返回的 unsubscribe 通过闭包记住 eventName 和 callback,调用时用 indexOf 找到位置,再 splice 删除。
emit:取出该事件的回调数组(没有就用空数组),用 map 依次执行 cb(...args),map 的结果就是返回值数组。事件不存在时,空数组的 map 结果正好是 []。
  1. type Callback = (...args: any[]) => any;
  2. type Subscription = {
  3.     unsubscribe: () => void
  4. }

  5. class EventEmitter {
  6.     // 事件名 -> 回调数组(按订阅顺序存放)
  7.     private events = new Map<string, Callback[]>();

  8.     subscribe(eventName: string, callback: Callback): Subscription {
  9.         if (!this.events.has(eventName)) {
  10.             this.events.set(eventName, []);
  11.         }
  12.         this.events.get(eventName)!.push(callback);

  13.         return {
  14.             unsubscribe: () => {
  15.                 const list = this.events.get(eventName)!;
  16.                 const idx = list.indexOf(callback); // 回调互不相同,可直接定位
  17.                 if (idx !== -1) list.splice(idx, 1);
  18.             }
  19.         };
  20.     }

  21.     emit(eventName: string, args: any[] = []): any[] {
  22.         const list = this.events.get(eventName) ?? [];
  23.         return list.map(cb => cb(...args)); // 按订阅顺序执行并收集返回值
  24.     }
  25. }

  26. /**
  27. * const emitter = new EventEmitter();
  28. *
  29. * // Subscribe to the onClick event with onClickCallback
  30. * function onClickCallback() { return 99 }
  31. * const sub = emitter.subscribe('onClick', onClickCallback);
  32. *
  33. * emitter.emit('onClick'); // [99]
  34. * sub.unsubscribe(); // undefined
  35. * emitter.emit('onClick'); // []
  36. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 09:37:23 | 只看该作者
全局:
2693. Call Function with Custom Context
Medium
Enhance all functions to have the callPolyfill method. The method accepts an object obj as its first parameter and any number of additional arguments. The obj becomes the this context for the function. The additional arguments are passed to the function (that the callPolyfill method belongs on).

For example if you had the function:

function tax(price, taxRate) {
  const totalCost = price * (1 + taxRate);
  console.log(`The cost of ${this.item} is ${totalCost}`);
}
Calling this function like tax(10, 0.1) will log "The cost of undefined is 11". This is because the this context was not defined.

However, calling the function like tax.callPolyfill({item: "salad"}, 10, 0.1) will log "The cost of salad is 11". The this context was appropriately set, and the function logged an appropriate output.

Please solve this without using the built-in Function.call method.



Example 1:

Input:
fn = function add(b) {
  return this.a + b;
}
args = [{"a": 5}, 7]
Output: 12
Explanation:
fn.callPolyfill({"a": 5}, 7); // 12
callPolyfill sets the "this" context to {"a": 5}. 7 is passed as an argument.
Example 2:

Input:
fn = function tax(price, taxRate) {
return `The cost of the ${this.item} is ${price * taxRate}`;
}
args = [{"item": "burger"}, 10, 1.1]
Output: "The cost of the burger is 11"
Explanation: callPolyfill sets the "this" context to {"item": "burger"}. 10 and 1.1 are passed as additional arguments.


Constraints:

typeof args[0] == 'object' and args[0] != null
1 <= args.length <= 100
2 <= JSON.stringify(args[0]).length <= 105


题目解读

给所有函数加上一个 callPolyfill 方法,效果等同于原生的 Function.prototype.call,但不能直接使用 call。

第一个参数 context 是一个对象,调用时它会成为函数内部的 this。
后面的参数(任意个)原样传给函数。
返回函数执行的结果。

例如 add.callPolyfill({a: 5}, 7):函数 add 里的 this 就是 {a: 5},参数 b = 7,所以返回 this.a + b = 12。

解题思路

JavaScript 里有一条规则:用 obj.method() 的形式调用函数时,函数内部的 this 就是 obj。

所以不用 call,只要临时把函数挂到 context 上,再通过 context.fn(...args) 调用,this 就自然指向 context 了。步骤如下:

生成一个不会和 context 已有属性冲突的 key,用 Symbol() 最合适。
把当前函数(this,也就是调用 callPolyfill 的那个函数)赋给 context[key]。
通过 context[key](...args) 调用,拿到返回值。
删除这个临时属性,还原 context。
返回结果。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };

  2. interface Function {
  3.     callPolyfill(context: Record<string, JSONValue>, ...args: JSONValue[]): JSONValue;
  4. }


  5. Function.prototype.callPolyfill = function(context, ...args): JSONValue {
  6.     const key = Symbol();               // 唯一 key,不会覆盖 context 上已有的属性
  7.     const ctx = context as any;
  8.     ctx[key] = this;                    // this 是调用 callPolyfill 的那个函数

  9.     try {
  10.         return ctx[key](...args);       // 以 ctx.fn() 的形式调用,函数内的 this 就是 ctx
  11.     } finally {
  12.         delete ctx[key];                // 无论是否抛错,都把临时属性清理掉
  13.     }

  14. }

  15. /**
  16. * function increment() { this.count++; return this.count; }
  17. * increment.callPolyfill({count: 1}); // 2
  18. */
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 09:39:32 | 只看该作者
全局:
2692. Make Object Immutable
Medium
Hint
Write a function that takes an object obj and returns a new immutable version of this object.

An immutable object is an object that can't be altered and will throw an error if any attempt is made to alter it.

There are three types of error messages that can be produced from this new object.

Attempting to modify a key on the object will result in this error message: `Error Modifying: ${key}`.
Attempting to modify an index on an array will result in this error message: `Error Modifying Index: ${index}`.
Attempting to call a method that mutates an array will result in this error message: `Error Calling Method: ${methodName}`. You may assume the only methods that can mutate an array are ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse'].
obj is a valid JSON object or array, meaning it is the output of JSON.parse().

Note that a string literal should be thrown, not an Error.



Example 1:

Input:
obj = {
  "x": 5
}
fn = (obj) => {
  obj.x = 5;
  return obj.x;
}
Output: {"value": null, "error": "Error Modifying: x"}
Explanation: Attempting to modify a key on an object resuts in a thrown error. Note that it doesn't matter that the value was set to the same value as it was before.
Example 2:

Input:
obj = [1, 2, 3]
fn = (arr) => {
  arr[1] = {};
  return arr[2];
}
Output: {"value": null, "error": "Error Modifying Index: 1"}
Explanation: Attempting to modify an array results in a thrown error.
Example 3:

Input:
obj = {
  "arr": [1, 2, 3]
}
fn = (obj) => {
  obj.arr.push(4);
  return 42;
}
Output: { "value": null, "error": "Error Calling Method: push"}
Explanation: Calling a method that can result in a mutation results in a thrown error.
Example 4:

Input:
obj = {
  "x": 2,
  "y": 2
}
fn = (obj) => {
  return Object.keys(obj);
}
Output: {"value": ["x", "y"], "error": null}
Explanation: No mutations were attempted so the function returns as normal.


Constraints:

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


题目解读

实现 makeImmutable(obj),返回 obj 的"不可变版本"。对这个返回值(包括它内部嵌套的对象和数组)做任何修改都要抛出错误,并且抛出的是字符串,不是 Error 对象。共有三种报错:

操作        抛出的字符串
修改对象的某个 key        Error Modifying: ${key}
修改数组的某个下标        Error Modifying Index: ${index}
调用数组的修改类方法(pop、push、shift、unshift、splice、sort、reverse)        Error Calling Method: ${methodName}

不涉及修改的操作(读取属性、Object.keys、map 等)要正常工作。就算把属性设置成和原来相同的值,也算修改,同样要抛错(示例 1)。

解题思路

这题的关键是用 Proxy 拦截对对象的操作。

拦截 set:任何赋值都直接抛错。目标是数组时抛 Error Modifying Index,是对象时抛 Error Modifying。
拦截 get,处理两件事:
如果目标是数组,并且访问的属性是七个修改类方法之一,就返回一个会抛错的函数。这样调用 arr.push(4) 时才会抛 Error Calling Method: push。
如果取到的值是对象或数组(嵌套结构),就对它再调用一次 makeImmutable,保证深层也不可变。这是懒包装:用到哪一层才包装哪一层,不用一开始就递归遍历整棵树。
其余情况原样返回属性值。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type Obj = Array<JSONValue> | Record<string, JSONValue>;

  3. const MUTATING_METHODS = ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

  4. function makeImmutable(obj: Obj): Obj {
  5.     // 修改属性(赋值/删除)时统一抛错,注意抛的是字符串
  6.     const reject = (target: Obj, prop: string | symbol): never => {
  7.         if (Array.isArray(target)) throw `Error Modifying Index: ${String(prop)}`;
  8.         throw `Error Modifying: ${String(prop)}`;
  9.     };

  10.     return new Proxy(obj, {
  11.         set: reject,
  12.         deleteProperty: reject,

  13.         get(target, prop, receiver) {
  14.             // 数组的修改类方法:被"调用"时才抛错,只是访问不报错
  15.             if (Array.isArray(target) && typeof prop === 'string' && MUTATING_METHODS.includes(prop)) {
  16.                 return () => { throw `Error Calling Method: ${prop}`; };
  17.             }
  18.             const value = Reflect.get(target, prop, receiver);
  19.             // 嵌套的对象/数组也要包装成不可变的
  20.             return typeof value === 'object' && value !== null ? makeImmutable(value) : value;
  21.         }
  22.     });
  23. };
复制代码
代码要点
为什么用 Proxy? 它可以拦截读取、赋值、删除等底层操作,正好用来"监控"有没有人试图修改对象。
set: reject:set 拦截器的参数是 (target, prop, value, receiver),reject 只用前两个,多余参数会被忽略。函数体一定会抛错,所以返回类型是 never。
deleteProperty:题目没有明说,但 delete obj.x 也是修改,顺手拦截更完整,报错格式与 set 一致。
修改类方法为什么在 get 里处理? arr.push(4) 其实分两步:先读取属性 push,再调用它。所以在 get 里返回一个"调用时才抛错"的函数,就能在调用时抛出 Error Calling Method: push。这样单纯读取 arr.push 不会误报。
只在数组上拦截方法:如果 JSON 对象本身有一个叫 "push" 的 key,它不应该被当作方法处理,所以先判断 Array.isArray(target)。
map、filter 等非修改方法:调用时 this 是代理对象,方法内部读取元素会再次走 get,因此元素同样被包装为不可变。
抛字符串而不是 Error:题目明确要求,所以写成 throw \...`,不用 new Error(...)`。
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-28 09:44:07 | 只看该作者
全局:
2691. Immutability Helper
Hard
Hint
Creating clones of immutable objects with minor alterations can be a tedious process. Write a class ImmutableHelper that serves as a tool to help with this requirement. The constructor accepts an immutable object obj which will be a JSON object or array.

The class has a single method produce which accepts a function mutator. The function returns a new object which is similar to the original except it has those mutations applied.

mutator accepts a proxied version of obj. A user of this function can (appear to) mutate this object, but the original object obj should not actually be effected.

For example, a user could write code like this:

const originalObj = {"x": 5};
const helper = new ImmutableHelper(originalObj);
const newObj = helper.produce((proxy) => {
  proxy.x = proxy.x + 1;
});
console.log(originalObj); // {"x": 5}
console.log(newObj); // {"x": 6}
Properties of the mutator function:

It will always return undefined.
It will never access keys that don't exist.
It will never delete keys (delete obj.key)
It will never call methods on a proxied object (push, shift, etc).
It will never set keys to objects (proxy.x = {})
Note on how the solution will be tested: the solution validator will only analyze differences between what was returned and the original obj. Doing a full comparison would be too computationally expensive. Also, any mutations to the original object will result in a wrong answer.



Example 1:

Input:
obj = {"val": 10},
mutators = [
  proxy => { proxy.val += 1; },
  proxy => { proxy.val -= 1; }
]
Output:
[
  {"val": 11},
  {"val": 9}
]
Explanation:
const helper = new ImmutableHelper({val: 10});
helper.produce(proxy => { proxy.val += 1; }); // { "val": 11 }
helper.produce(proxy => { proxy.val -= 1; }); // { "val": 9 }
Example 2:

Input:
obj = {"arr": [1, 2, 3]}
mutators = [
proxy => {
   proxy.arr[0] = 5;
   proxy.newVal = proxy.arr[0] + proxy.arr[1];
}
]
Output:
[
  {"arr": [5, 2, 3], "newVal": 7 }
]
Explanation: Two edits were made to the original array. The first element in the array was to set 5. Then a new key was added with a value of 7.
Example 3:

Input:
obj = {"obj": {"val": {"x": 10, "y": 20}}}
mutators = [
  proxy => {
    let data = proxy.obj.val;
    let temp = data.x;
    data.x = data.y;
    data.y = temp;
  }
]
Output:
[
  {"obj": {"val": {"x": 20, "y": 10}}}
]
Explanation: The values of "x" and "y" were swapped.


Constraints:

2 <= JSON.stringify(obj).length <= 4 * 105
mutators is an array of functions
total calls to produce() < 105

题目解读

实现 ImmutableHelper 类。构造函数接收一个 JSON 对象或数组 obj,produce(mutator) 的行为如下:

把 obj 的一个**代理(proxy)**交给 mutator,mutator 可以在代理上"随意修改"。
修改不能影响原始的 obj。
produce 最终返回一个新对象,内容是应用了这些修改后的结果。

mutator 有几个保证:只做赋值(读取已有的 key、写入 key,新增 key 也可以),不会删除 key,不会调用 push 等方法,也不会把 key 设为对象。

难点在性能:JSON 最长 4×10^5 个字符,produce 最多调用近 10^5 次。如果每次都深拷贝整个对象,肯定超时。

解题思路

这就是 Immer 库的核心原理:写时复制(copy-on-write)+ 结构共享。

不预先拷贝任何东西,mutator 读取到哪一层,才为那一层创建代理。
第一次写入某个节点时,才对这个节点做一次浅拷贝,之后的写入都写到这份拷贝上。
写入某个节点时,它的所有祖先节点也必须有自己的拷贝,并让父拷贝指向子拷贝。这样从根到被修改节点的整条路径都是新对象。
没被碰过的子树直接和原对象共享,不拷贝。
produce 结束时,返回根节点的拷贝;如果什么都没改,返回原对象即可。

原始对象始终只读,不会被修改。
  1. type JSONValue = null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };
  2. type InputObj = Record<string, JSONValue> | Array<JSONValue>;

  3. // 每个"被代理的节点"对应一个状态
  4. interface DraftState {
  5.     base: any;                                  // 原始节点(只读,绝不修改)
  6.     copy: any | null;                           // 第一次写入时才创建的浅拷贝
  7.     parent: DraftState | null;                  // 父节点状态
  8.     key: string | symbol | null;                // 自己在父节点中的 key
  9.     children: Map<string | symbol, any>;        // 缓存已创建的子代理
  10. }

  11. function newState(base: any, parent: DraftState | null, key: string | symbol | null): DraftState {
  12.     return { base, copy: null, parent, key, children: new Map() };
  13. }

  14. // 确保该节点及其所有祖先都已有自己的拷贝,并返回该节点的拷贝
  15. function ensureCopy(state: DraftState): any {
  16.     if (state.copy === null) {
  17.         state.copy = Array.isArray(state.base) ? state.base.slice() : { ...state.base };
  18.         if (state.parent !== null) {
  19.             ensureCopy(state.parent)[state.key!] = state.copy;  // 把自己的拷贝挂回父拷贝上
  20.         }
  21.     }
  22.     return state.copy;
  23. }

  24. function createProxy(state: DraftState): any {
  25.     return new Proxy(state.base, {
  26.         get(_, prop) {
  27.             const source = state.copy ?? state.base;   // 有拷贝就读拷贝,否则读原对象
  28.             const value = source[prop];
  29.             if (typeof value !== 'object' || value === null) return value;

  30.             // 嵌套对象/数组:返回(并缓存)子代理
  31.             let child = state.children.get(prop);
  32.             if (!child) {
  33.                 child = createProxy(newState(state.base[prop], state, prop));
  34.                 state.children.set(prop, child);
  35.             }
  36.             return child;
  37.         },
  38.         set(_, prop, value) {
  39.             ensureCopy(state)[prop] = value;           // 写到拷贝上,原对象不受影响
  40.             return true;
  41.         }
  42.     });
  43. }

  44. class ImmutableHelper {
  45.     private obj: InputObj;

  46.     constructor(obj: InputObj) {
  47.         this.obj = obj;
  48.     }

  49.     produce(mutator: (obj: InputObj) => void): InputObj {
  50.         const root = newState(this.obj, null, null);
  51.         mutator(createProxy(root));
  52.         return root.copy ?? root.base;                 // 没有修改就直接返回原对象
  53.     }
  54. }
复制代码
代码要点
DraftState:每个被访问的节点都有一个状态,记录原对象 base、写入后才出现的 copy、父节点和自己的 key。有了 parent 和 key,写入时就能沿着路径往上"补齐"祖先的拷贝。
ensureCopy:先给自己做浅拷贝(数组用 slice(),对象用展开 {...}),再递归确保父节点有拷贝,最后把自己的拷贝挂回父拷贝的对应 key 上。这一步保证了"根到叶子整条路径都是新对象"。
get 读哪里? 有拷贝时必须读拷贝,否则读不到刚写入的值。例如示例 2 里 proxy.arr[0] = 5 之后再读 proxy.arr[0],要得到 5。
子代理为什么要缓存? 同一个节点的状态(尤其是 copy)必须唯一。如果每次访问 proxy.arr 都新建状态,之前的修改就丢了,所以用 children 缓存。
子代理的 base 用 state.base[prop]:也就是原始的子节点,而不是从拷贝里取。因为子节点拷贝要由子状态自己管理。
set 不用 Reflect.set:代理的 target 是原对象,如果转发给 target,就真的修改原对象了。这里只写到 copy 上,所以原对象不会被改动。
proxy.val += 1:等价于先 get val(得到 10),再 set val = 11,两个 trap 配合即可。
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-29 10:41:39 | 只看该作者
全局:
580. Count Student Number in Departments
Medium
Topics
Hint
SQL Schema
Pandas Schema
Table: Student

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| student_id   | int     |
| student_name | varchar |
| gender       | varchar |
| dept_id      | int     |
+--------------+---------+
student_id is the primary key (column with unique values) for this table.
dept_id is a foreign key (reference column) to dept_id in the Department tables.
Each row of this table indicates the name of a student, their gender, and the id of their department.


Table: Department

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| dept_id     | int     |
| dept_name   | varchar |
+-------------+---------+
dept_id is the primary key (column with unique values) for this table.
Each row of this table contains the id and the name of a department.


Write a solution to report the respective department name and number of students majoring in each department for all departments in the Department table (even ones with no current students).

Return the result table ordered by student_number in descending order. In case of a tie, order them by dept_name alphabetically.

The result format is in the following example.



Example 1:

Input:
Student table:
+------------+--------------+--------+---------+
| student_id | student_name | gender | dept_id |
+------------+--------------+--------+---------+
| 1          | Jack         | M      | 1       |
| 2          | Jane         | F      | 1       |
| 3          | Mark         | M      | 2       |
+------------+--------------+--------+---------+
Department table:
+---------+-------------+
| dept_id | dept_name   |
+---------+-------------+
| 1       | Engineering |
| 2       | Science     |
| 3       | Law         |
+---------+-------------+
Output:
+-------------+----------------+
| dept_name   | student_number |
+-------------+----------------+
| Engineering | 2              |
| Science     | 1              |
| Law         | 0              |
+-------------+----------------+
  1. SELECT
  2.     d.dept_name,
  3.     COUNT(s.student_id) AS student_number
  4. FROM Department d
  5. LEFT JOIN Student s
  6.     ON d.dept_id = s.dept_id
  7. GROUP BY d.dept_id, d.dept_name
  8. ORDER BY student_number DESC, d.dept_name ASC;
复制代码
回复

使用道具 举报

🔗
 楼主| Myron2017 2026-9-30 10:30:23 | 只看该作者
全局:
584. Find Customer Referee
Solved
Easy
Topics
conpanies icon
Companies
Hint
SQL Schema
Pandas Schema
Table: Customer

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| name        | varchar |
| referee_id  | int     |
+-------------+---------+
In SQL, id is the primary key column for this table.
Each row of this table indicates the id of a customer, their name, and the id of the customer who referred them.


Find the names of the customer that are either:

referred by any customer with id != 2.
not referred by any customer.
Return the result table in any order.

The result format is in the following example.



Example 1:

Input:
Customer table:
+----+------+------------+
| id | name | referee_id |
+----+------+------------+
| 1  | Will | null       |
| 2  | Jane | null       |
| 3  | Alex | 2          |
| 4  | Bill | null       |
| 5  | Zack | 1          |
| 6  | Mark | 2          |
+----+------+------------+
Output:
+------+
| name |
+------+
| Will |
| Jane |
| Bill |
| Zack |
+------+
  1. SELECT name
  2. FROM Customer
  3. WHERE referee_id != 2
  4.    OR referee_id IS NULL;
复制代码
回复

使用道具 举报

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

本版积分规则

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