diff --git a/solution/2300-2399/2349.Design a Number Container System/README.md b/solution/2300-2399/2349.Design a Number Container System/README.md index 75e89b38e3e0a..0de3381c84616 100644 --- a/solution/2300-2399/2349.Design a Number Container System/README.md +++ b/solution/2300-2399/2349.Design a Number Container System/README.md @@ -74,7 +74,15 @@ nc.find(10); // 数字 10 所在下标为 2 ,3 和 5 。最小下标为 2 , -### 方法一:哈希表 +### 方法一:哈希表 + 有序集合 + +我们用一个哈希表 $d$ 记录下标和数字的映射关系,用一个哈希表 $g$ 记录每个数字对应的下标集合,这里我们可以使用有序集合来存储下标,这样我们就可以方便地找到最小下标。 + +调用 `change` 方法时,我们先判断下标是否已经存在,如果存在,我们就将原来的数字从对应的下标集合中删除,然后将新的数字添加到对应的下标集合中。时间复杂度 $O(\log n)$。 + +调用 `find` 方法时,我们直接返回对应数字的下标集合的第一个元素即可。时间复杂度 $O(1)$。 + +空间复杂度 $O(n)$。其中 $n$ 为数字的个数。 @@ -85,20 +93,21 @@ from sortedcontainers import SortedSet class NumberContainers: + def __init__(self): - self.mp = {} - self.t = defaultdict(SortedSet) + self.d = {} + self.g = defaultdict(SortedSet) def change(self, index: int, number: int) -> None: - if index in self.mp: - v = self.mp[index] - self.t[v].remove(index) - self.mp[index] = number - self.t[number].add(index) + if index in self.d: + old_number = self.d[index] + self.g[old_number].remove(index) + self.d[index] = number + self.g[number].add(index) def find(self, number: int) -> int: - s = self.t[number] - return s[0] if s else -1 + ids = self.g[number] + return ids[0] if ids else -1 # Your NumberContainers object will be instantiated and called as such: @@ -111,26 +120,24 @@ class NumberContainers: ```java class NumberContainers { - private Map mp = new HashMap<>(); - private Map> t = new HashMap<>(); + private Map d = new HashMap<>(); + private Map> g = new HashMap<>(); public NumberContainers() { } public void change(int index, int number) { - if (mp.containsKey(index)) { - int v = mp.get(index); - t.get(v).remove(index); - if (t.get(v).isEmpty()) { - t.remove(v); - } + if (d.containsKey(index)) { + int oldNumber = d.get(index); + g.get(oldNumber).remove(index); } - mp.put(index, number); - t.computeIfAbsent(number, k -> new TreeSet<>()).add(index); + d.put(index, number); + g.computeIfAbsent(number, k -> new TreeSet<>()).add(index); } public int find(int number) { - return t.containsKey(number) ? t.get(number).first() : -1; + var ids = g.get(number); + return ids == null || ids.isEmpty() ? -1 : ids.first(); } } @@ -147,26 +154,28 @@ class NumberContainers { ```cpp class NumberContainers { public: - map mp; - map> t; - NumberContainers() { } void change(int index, int number) { - auto it = mp.find(index); - if (it != mp.end()) { - t[it->second].erase(index); - it->second = number; - } else - mp[index] = number; - t[number].insert(index); + if (d.contains(index)) { + int oldNumber = d[index]; + g[oldNumber].erase(index); + if (g[oldNumber].empty()) { + g.erase(oldNumber); + } + } + d[index] = number; + g[number].insert(index); } int find(int number) { - auto it = t.find(number); - return it == t.end() || it->second.empty() ? -1 : *it->second.begin(); + return g.contains(number) ? *g[number].begin() : -1; } + +private: + unordered_map d; + unordered_map> g; }; /** @@ -181,8 +190,8 @@ public: ```go type NumberContainers struct { - mp map[int]int - t map[int]*redblacktree.Tree + d map[int]int + g map[int]*redblacktree.Tree } func Constructor() NumberContainers { @@ -190,22 +199,21 @@ func Constructor() NumberContainers { } func (this *NumberContainers) Change(index int, number int) { - if num, ok := this.mp[index]; ok { - this.t[num].Remove(index) + if oldNumber, ok := this.d[index]; ok { + this.g[oldNumber].Remove(index) } - this.mp[index] = number - if this.t[number] == nil { - this.t[number] = redblacktree.NewWithIntComparator() + this.d[index] = number + if _, ok := this.g[number]; !ok { + this.g[number] = redblacktree.NewWithIntComparator() } - this.t[number].Put(index, nil) + this.g[number].Put(index, nil) } func (this *NumberContainers) Find(number int) int { - s, ok := this.t[number] - if !ok || s.Size() == 0 { - return -1 + if ids, ok := this.g[number]; ok && ids.Size() > 0 { + return ids.Left().Key.(int) } - return s.Left().Key.(int) + return -1 } /** @@ -216,6 +224,532 @@ func (this *NumberContainers) Find(number int) int { */ ``` +#### TypeScript + +```ts +class NumberContainers { + private d = new Map(); + private g = new Map>(); + constructor() {} + + change(index: number, number: number): void { + if (this.d.has(index)) { + const oldNumber = this.d.get(index)!; + this.g.get(oldNumber)!.delete(index); + if (!this.g.get(oldNumber)!.size()) { + this.g.delete(oldNumber); + } + } + this.d.set(index, number); + if (!this.g.has(number)) { + this.g.set(number, new TreeSet()); + } + this.g.get(number)!.add(index); + } + + find(number: number): number { + return this.g.has(number) ? this.g.get(number)!.first()! : -1; + } +} + +type Compare = (lhs: T, rhs: T) => number; + +class RBTreeNode { + data: T; + count: number; + left: RBTreeNode | null; + right: RBTreeNode | null; + parent: RBTreeNode | null; + color: number; + constructor(data: T) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + + sibling(): RBTreeNode | null { + if (!this.parent) return null; // sibling null if no parent + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + + isOnLeft(): boolean { + return this === this.parent!.left; + } + + hasRedChild(): boolean { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +} + +class RBTree { + root: RBTreeNode | null; + lt: (l: T, r: T) => boolean; + constructor(compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0)) { + this.root = null; + this.lt = (l: T, r: T) => compare(l, r) < 0; + } + + rotateLeft(pt: RBTreeNode): void { + const right = pt.right!; + pt.right = right.left; + + if (pt.right) pt.right.parent = pt; + right.parent = pt.parent; + + if (!pt.parent) this.root = right; + else if (pt === pt.parent.left) pt.parent.left = right; + else pt.parent.right = right; + + right.left = pt; + pt.parent = right; + } + + rotateRight(pt: RBTreeNode): void { + const left = pt.left!; + pt.left = left.right; + + if (pt.left) pt.left.parent = pt; + left.parent = pt.parent; + + if (!pt.parent) this.root = left; + else if (pt === pt.parent.left) pt.parent.left = left; + else pt.parent.right = left; + + left.right = pt; + pt.parent = left; + } + + swapColor(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + + swapData(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + + fixAfterInsert(pt: RBTreeNode): void { + let parent = null; + let grandParent = null; + + while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) { + parent = pt.parent; + grandParent = pt.parent.parent; + + /* Case : A + Parent of pt is left child of Grand-parent of pt */ + if (parent === grandParent?.left) { + const uncle = grandParent.right; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + /* Case : 2 + pt is right child of its parent + Left-rotation required */ + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is left child of its parent + Right-rotation required */ + this.rotateRight(grandParent); + this.swapColor(parent!, grandParent); + pt = parent!; + } + } else { + /* Case : B + Parent of pt is right child of Grand-parent of pt */ + const uncle = grandParent!.left; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle != null && uncle.color === 0) { + grandParent!.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent!; + } else { + /* Case : 2 + pt is left child of its parent + Right-rotation required */ + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is right child of its parent + Left-rotation required */ + this.rotateLeft(grandParent!); + this.swapColor(parent!, grandParent!); + pt = parent!; + } + } + } + this.root!.color = 1; + } + + delete(val: T): boolean { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + + deleteAll(val: T): boolean { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + + deleteNode(v: RBTreeNode): void { + const u = BSTreplace(v); + + // True when u and v are both black + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent!; + + if (!u) { + // u is null therefore v is leaf + if (v === this.root) this.root = null; + // v is root, making root null + else { + if (uvBlack) { + // u and v both black + // v is leaf, fix double black at v + this.fixDoubleBlack(v); + } else { + // u or v is red + if (v.sibling()) { + // sibling is not null, make it red" + v.sibling()!.color = 0; + } + } + // delete v from the tree + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + + if (!v.left || !v.right) { + // v has 1 child + if (v === this.root) { + // v is root, assign the value of u to v, and delete u + v.data = u.data; + v.left = v.right = null; + } else { + // Detach v from tree and move u up + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + // u and v both black, fix double black at u + else u.color = 1; // u or v red, color u black + } + return; + } + + // v has 2 children, swap data with successor and recurse + this.swapData(u, v); + this.deleteNode(u); + + // find node that replaces a deleted node in BST + function BSTreplace(x: RBTreeNode): RBTreeNode | null { + // when node have 2 children + if (x.left && x.right) return successor(x.right); + // when leaf + if (!x.left && !x.right) return null; + // when single child + return x.left ?? x.right; + } + // find node that do not have a left child + // in the subtree of the given node + function successor(x: RBTreeNode): RBTreeNode { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + + fixDoubleBlack(x: RBTreeNode): void { + if (x === this.root) return; // Reached root + + const sibling = x.sibling(); + const parent = x.parent!; + if (!sibling) { + // No sibiling, double black pushed up + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + // Sibling red + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + // left case + else this.rotateLeft(parent); // right case + this.fixDoubleBlack(x); + } else { + // Sibling black + if (sibling.hasRedChild()) { + // at least 1 red children + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + // left left + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + // right left + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + // left right + sibling.right!.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + // right right + sibling.right!.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + // 2 black children + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + + insert(data: T): boolean { + // search for a position to insert + let parent = this.root; + while (parent) { + if (this.lt(data, parent.data)) { + if (!parent.left) break; + else parent = parent.left; + } else if (this.lt(parent.data, data)) { + if (!parent.right) break; + else parent = parent.right; + } else break; + } + + // insert node into parent + const node = new RBTreeNode(data); + if (!parent) this.root = node; + else if (this.lt(node.data, parent.data)) parent.left = node; + else if (this.lt(parent.data, node.data)) parent.right = node; + else { + parent.count++; + return false; + } + node.parent = parent; + this.fixAfterInsert(node); + return true; + } + + find(data: T): RBTreeNode | null { + let p = this.root; + while (p) { + if (this.lt(data, p.data)) { + p = p.left; + } else if (this.lt(p.data, data)) { + p = p.right; + } else break; + } + return p ?? null; + } + + *inOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.inOrder(root.left!)) yield v; + yield root.data; + for (const v of this.inOrder(root.right!)) yield v; + } + + *reverseInOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.reverseInOrder(root.right!)) yield v; + yield root.data; + for (const v of this.reverseInOrder(root.left!)) yield v; + } +} + +class TreeSet { + _size: number; + tree: RBTree; + compare: Compare; + constructor( + collection: T[] | Compare = [], + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const val of collection) this.add(val); + } + + size(): number { + return this._size; + } + + has(val: T): boolean { + return !!this.tree.find(val); + } + + add(val: T): boolean { + const successful = this.tree.insert(val); + this._size += successful ? 1 : 0; + return successful; + } + + delete(val: T): boolean { + const deleted = this.tree.deleteAll(val); + this._size -= deleted ? 1 : 0; + return deleted; + } + + ceil(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(p.data, val) >= 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + floor(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(val, p.data) >= 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + higher(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(val, p.data) < 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + lower(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(p.data, val) < 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + first(): T | undefined { + return this.tree.inOrder().next().value; + } + + last(): T | undefined { + return this.tree.reverseInOrder().next().value; + } + + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + pop(): T | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + *[Symbol.iterator](): Generator { + for (const val of this.values()) yield val; + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) yield val; + return undefined; + } + + /** + * Return a generator for reverse order traversing the set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) yield val; + return undefined; + } +} + +/** + * Your NumberContainers object will be instantiated and called as such: + * var obj = new NumberContainers() + * obj.change(index,number) + * var param_2 = obj.find(number) + */ +``` + diff --git a/solution/2300-2399/2349.Design a Number Container System/README_EN.md b/solution/2300-2399/2349.Design a Number Container System/README_EN.md index 2b5447bae3f35..8b0950666fad4 100644 --- a/solution/2300-2399/2349.Design a Number Container System/README_EN.md +++ b/solution/2300-2399/2349.Design a Number Container System/README_EN.md @@ -72,7 +72,15 @@ nc.find(10); // Number 10 is at the indices 2, 3, and 5. The smallest index that -### Solution 1 +### Solution 1: Hash Table + Ordered Set + +We use a hash table $d$ to record the mapping relationship between indices and numbers, and another hash table $g$ to record the set of indices corresponding to each number. Here, we can use an ordered set to store the indices, which allows us to conveniently find the smallest index. + +When calling the `change` method, we first check if the index already exists. If it does, we remove the original number from its corresponding index set and then add the new number to the corresponding index set. The time complexity is $O(\log n)$. + +When calling the `find` method, we simply return the first element of the index set corresponding to the number. The time complexity is $O(1)$. + +The space complexity is $O(n)$, where $n$ is the number of numbers. @@ -83,20 +91,21 @@ from sortedcontainers import SortedSet class NumberContainers: + def __init__(self): - self.mp = {} - self.t = defaultdict(SortedSet) + self.d = {} + self.g = defaultdict(SortedSet) def change(self, index: int, number: int) -> None: - if index in self.mp: - v = self.mp[index] - self.t[v].remove(index) - self.mp[index] = number - self.t[number].add(index) + if index in self.d: + old_number = self.d[index] + self.g[old_number].remove(index) + self.d[index] = number + self.g[number].add(index) def find(self, number: int) -> int: - s = self.t[number] - return s[0] if s else -1 + ids = self.g[number] + return ids[0] if ids else -1 # Your NumberContainers object will be instantiated and called as such: @@ -109,26 +118,24 @@ class NumberContainers: ```java class NumberContainers { - private Map mp = new HashMap<>(); - private Map> t = new HashMap<>(); + private Map d = new HashMap<>(); + private Map> g = new HashMap<>(); public NumberContainers() { } public void change(int index, int number) { - if (mp.containsKey(index)) { - int v = mp.get(index); - t.get(v).remove(index); - if (t.get(v).isEmpty()) { - t.remove(v); - } + if (d.containsKey(index)) { + int oldNumber = d.get(index); + g.get(oldNumber).remove(index); } - mp.put(index, number); - t.computeIfAbsent(number, k -> new TreeSet<>()).add(index); + d.put(index, number); + g.computeIfAbsent(number, k -> new TreeSet<>()).add(index); } public int find(int number) { - return t.containsKey(number) ? t.get(number).first() : -1; + var ids = g.get(number); + return ids == null || ids.isEmpty() ? -1 : ids.first(); } } @@ -145,26 +152,28 @@ class NumberContainers { ```cpp class NumberContainers { public: - map mp; - map> t; - NumberContainers() { } void change(int index, int number) { - auto it = mp.find(index); - if (it != mp.end()) { - t[it->second].erase(index); - it->second = number; - } else - mp[index] = number; - t[number].insert(index); + if (d.contains(index)) { + int oldNumber = d[index]; + g[oldNumber].erase(index); + if (g[oldNumber].empty()) { + g.erase(oldNumber); + } + } + d[index] = number; + g[number].insert(index); } int find(int number) { - auto it = t.find(number); - return it == t.end() || it->second.empty() ? -1 : *it->second.begin(); + return g.contains(number) ? *g[number].begin() : -1; } + +private: + unordered_map d; + unordered_map> g; }; /** @@ -179,8 +188,8 @@ public: ```go type NumberContainers struct { - mp map[int]int - t map[int]*redblacktree.Tree + d map[int]int + g map[int]*redblacktree.Tree } func Constructor() NumberContainers { @@ -188,22 +197,21 @@ func Constructor() NumberContainers { } func (this *NumberContainers) Change(index int, number int) { - if num, ok := this.mp[index]; ok { - this.t[num].Remove(index) + if oldNumber, ok := this.d[index]; ok { + this.g[oldNumber].Remove(index) } - this.mp[index] = number - if this.t[number] == nil { - this.t[number] = redblacktree.NewWithIntComparator() + this.d[index] = number + if _, ok := this.g[number]; !ok { + this.g[number] = redblacktree.NewWithIntComparator() } - this.t[number].Put(index, nil) + this.g[number].Put(index, nil) } func (this *NumberContainers) Find(number int) int { - s, ok := this.t[number] - if !ok || s.Size() == 0 { - return -1 + if ids, ok := this.g[number]; ok && ids.Size() > 0 { + return ids.Left().Key.(int) } - return s.Left().Key.(int) + return -1 } /** @@ -214,6 +222,532 @@ func (this *NumberContainers) Find(number int) int { */ ``` +#### TypeScript + +```ts +class NumberContainers { + private d = new Map(); + private g = new Map>(); + constructor() {} + + change(index: number, number: number): void { + if (this.d.has(index)) { + const oldNumber = this.d.get(index)!; + this.g.get(oldNumber)!.delete(index); + if (!this.g.get(oldNumber)!.size()) { + this.g.delete(oldNumber); + } + } + this.d.set(index, number); + if (!this.g.has(number)) { + this.g.set(number, new TreeSet()); + } + this.g.get(number)!.add(index); + } + + find(number: number): number { + return this.g.has(number) ? this.g.get(number)!.first()! : -1; + } +} + +type Compare = (lhs: T, rhs: T) => number; + +class RBTreeNode { + data: T; + count: number; + left: RBTreeNode | null; + right: RBTreeNode | null; + parent: RBTreeNode | null; + color: number; + constructor(data: T) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + + sibling(): RBTreeNode | null { + if (!this.parent) return null; // sibling null if no parent + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + + isOnLeft(): boolean { + return this === this.parent!.left; + } + + hasRedChild(): boolean { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +} + +class RBTree { + root: RBTreeNode | null; + lt: (l: T, r: T) => boolean; + constructor(compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0)) { + this.root = null; + this.lt = (l: T, r: T) => compare(l, r) < 0; + } + + rotateLeft(pt: RBTreeNode): void { + const right = pt.right!; + pt.right = right.left; + + if (pt.right) pt.right.parent = pt; + right.parent = pt.parent; + + if (!pt.parent) this.root = right; + else if (pt === pt.parent.left) pt.parent.left = right; + else pt.parent.right = right; + + right.left = pt; + pt.parent = right; + } + + rotateRight(pt: RBTreeNode): void { + const left = pt.left!; + pt.left = left.right; + + if (pt.left) pt.left.parent = pt; + left.parent = pt.parent; + + if (!pt.parent) this.root = left; + else if (pt === pt.parent.left) pt.parent.left = left; + else pt.parent.right = left; + + left.right = pt; + pt.parent = left; + } + + swapColor(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + + swapData(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + + fixAfterInsert(pt: RBTreeNode): void { + let parent = null; + let grandParent = null; + + while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) { + parent = pt.parent; + grandParent = pt.parent.parent; + + /* Case : A + Parent of pt is left child of Grand-parent of pt */ + if (parent === grandParent?.left) { + const uncle = grandParent.right; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + /* Case : 2 + pt is right child of its parent + Left-rotation required */ + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is left child of its parent + Right-rotation required */ + this.rotateRight(grandParent); + this.swapColor(parent!, grandParent); + pt = parent!; + } + } else { + /* Case : B + Parent of pt is right child of Grand-parent of pt */ + const uncle = grandParent!.left; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle != null && uncle.color === 0) { + grandParent!.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent!; + } else { + /* Case : 2 + pt is left child of its parent + Right-rotation required */ + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is right child of its parent + Left-rotation required */ + this.rotateLeft(grandParent!); + this.swapColor(parent!, grandParent!); + pt = parent!; + } + } + } + this.root!.color = 1; + } + + delete(val: T): boolean { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + + deleteAll(val: T): boolean { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + + deleteNode(v: RBTreeNode): void { + const u = BSTreplace(v); + + // True when u and v are both black + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent!; + + if (!u) { + // u is null therefore v is leaf + if (v === this.root) this.root = null; + // v is root, making root null + else { + if (uvBlack) { + // u and v both black + // v is leaf, fix double black at v + this.fixDoubleBlack(v); + } else { + // u or v is red + if (v.sibling()) { + // sibling is not null, make it red" + v.sibling()!.color = 0; + } + } + // delete v from the tree + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + + if (!v.left || !v.right) { + // v has 1 child + if (v === this.root) { + // v is root, assign the value of u to v, and delete u + v.data = u.data; + v.left = v.right = null; + } else { + // Detach v from tree and move u up + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + // u and v both black, fix double black at u + else u.color = 1; // u or v red, color u black + } + return; + } + + // v has 2 children, swap data with successor and recurse + this.swapData(u, v); + this.deleteNode(u); + + // find node that replaces a deleted node in BST + function BSTreplace(x: RBTreeNode): RBTreeNode | null { + // when node have 2 children + if (x.left && x.right) return successor(x.right); + // when leaf + if (!x.left && !x.right) return null; + // when single child + return x.left ?? x.right; + } + // find node that do not have a left child + // in the subtree of the given node + function successor(x: RBTreeNode): RBTreeNode { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + + fixDoubleBlack(x: RBTreeNode): void { + if (x === this.root) return; // Reached root + + const sibling = x.sibling(); + const parent = x.parent!; + if (!sibling) { + // No sibiling, double black pushed up + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + // Sibling red + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + // left case + else this.rotateLeft(parent); // right case + this.fixDoubleBlack(x); + } else { + // Sibling black + if (sibling.hasRedChild()) { + // at least 1 red children + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + // left left + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + // right left + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + // left right + sibling.right!.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + // right right + sibling.right!.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + // 2 black children + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + + insert(data: T): boolean { + // search for a position to insert + let parent = this.root; + while (parent) { + if (this.lt(data, parent.data)) { + if (!parent.left) break; + else parent = parent.left; + } else if (this.lt(parent.data, data)) { + if (!parent.right) break; + else parent = parent.right; + } else break; + } + + // insert node into parent + const node = new RBTreeNode(data); + if (!parent) this.root = node; + else if (this.lt(node.data, parent.data)) parent.left = node; + else if (this.lt(parent.data, node.data)) parent.right = node; + else { + parent.count++; + return false; + } + node.parent = parent; + this.fixAfterInsert(node); + return true; + } + + find(data: T): RBTreeNode | null { + let p = this.root; + while (p) { + if (this.lt(data, p.data)) { + p = p.left; + } else if (this.lt(p.data, data)) { + p = p.right; + } else break; + } + return p ?? null; + } + + *inOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.inOrder(root.left!)) yield v; + yield root.data; + for (const v of this.inOrder(root.right!)) yield v; + } + + *reverseInOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.reverseInOrder(root.right!)) yield v; + yield root.data; + for (const v of this.reverseInOrder(root.left!)) yield v; + } +} + +class TreeSet { + _size: number; + tree: RBTree; + compare: Compare; + constructor( + collection: T[] | Compare = [], + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const val of collection) this.add(val); + } + + size(): number { + return this._size; + } + + has(val: T): boolean { + return !!this.tree.find(val); + } + + add(val: T): boolean { + const successful = this.tree.insert(val); + this._size += successful ? 1 : 0; + return successful; + } + + delete(val: T): boolean { + const deleted = this.tree.deleteAll(val); + this._size -= deleted ? 1 : 0; + return deleted; + } + + ceil(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(p.data, val) >= 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + floor(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(val, p.data) >= 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + higher(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(val, p.data) < 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + lower(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(p.data, val) < 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + first(): T | undefined { + return this.tree.inOrder().next().value; + } + + last(): T | undefined { + return this.tree.reverseInOrder().next().value; + } + + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + pop(): T | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + *[Symbol.iterator](): Generator { + for (const val of this.values()) yield val; + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) yield val; + return undefined; + } + + /** + * Return a generator for reverse order traversing the set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) yield val; + return undefined; + } +} + +/** + * Your NumberContainers object will be instantiated and called as such: + * var obj = new NumberContainers() + * obj.change(index,number) + * var param_2 = obj.find(number) + */ +``` + diff --git a/solution/2300-2399/2349.Design a Number Container System/Solution.cpp b/solution/2300-2399/2349.Design a Number Container System/Solution.cpp index 812ebd94c9301..c96d6bfc577f4 100644 --- a/solution/2300-2399/2349.Design a Number Container System/Solution.cpp +++ b/solution/2300-2399/2349.Design a Number Container System/Solution.cpp @@ -1,25 +1,27 @@ class NumberContainers { public: - map mp; - map> t; - NumberContainers() { } void change(int index, int number) { - auto it = mp.find(index); - if (it != mp.end()) { - t[it->second].erase(index); - it->second = number; - } else - mp[index] = number; - t[number].insert(index); + if (d.contains(index)) { + int oldNumber = d[index]; + g[oldNumber].erase(index); + if (g[oldNumber].empty()) { + g.erase(oldNumber); + } + } + d[index] = number; + g[number].insert(index); } int find(int number) { - auto it = t.find(number); - return it == t.end() || it->second.empty() ? -1 : *it->second.begin(); + return g.contains(number) ? *g[number].begin() : -1; } + +private: + unordered_map d; + unordered_map> g; }; /** diff --git a/solution/2300-2399/2349.Design a Number Container System/Solution.go b/solution/2300-2399/2349.Design a Number Container System/Solution.go index 980c474ec2e33..b2ea51905f600 100644 --- a/solution/2300-2399/2349.Design a Number Container System/Solution.go +++ b/solution/2300-2399/2349.Design a Number Container System/Solution.go @@ -1,6 +1,6 @@ type NumberContainers struct { - mp map[int]int - t map[int]*redblacktree.Tree + d map[int]int + g map[int]*redblacktree.Tree } func Constructor() NumberContainers { @@ -8,22 +8,21 @@ func Constructor() NumberContainers { } func (this *NumberContainers) Change(index int, number int) { - if num, ok := this.mp[index]; ok { - this.t[num].Remove(index) + if oldNumber, ok := this.d[index]; ok { + this.g[oldNumber].Remove(index) } - this.mp[index] = number - if this.t[number] == nil { - this.t[number] = redblacktree.NewWithIntComparator() + this.d[index] = number + if _, ok := this.g[number]; !ok { + this.g[number] = redblacktree.NewWithIntComparator() } - this.t[number].Put(index, nil) + this.g[number].Put(index, nil) } func (this *NumberContainers) Find(number int) int { - s, ok := this.t[number] - if !ok || s.Size() == 0 { - return -1 + if ids, ok := this.g[number]; ok && ids.Size() > 0 { + return ids.Left().Key.(int) } - return s.Left().Key.(int) + return -1 } /** diff --git a/solution/2300-2399/2349.Design a Number Container System/Solution.java b/solution/2300-2399/2349.Design a Number Container System/Solution.java index b5f43bd667f03..90e4d6913acd9 100644 --- a/solution/2300-2399/2349.Design a Number Container System/Solution.java +++ b/solution/2300-2399/2349.Design a Number Container System/Solution.java @@ -1,24 +1,22 @@ class NumberContainers { - private Map mp = new HashMap<>(); - private Map> t = new HashMap<>(); + private Map d = new HashMap<>(); + private Map> g = new HashMap<>(); public NumberContainers() { } public void change(int index, int number) { - if (mp.containsKey(index)) { - int v = mp.get(index); - t.get(v).remove(index); - if (t.get(v).isEmpty()) { - t.remove(v); - } + if (d.containsKey(index)) { + int oldNumber = d.get(index); + g.get(oldNumber).remove(index); } - mp.put(index, number); - t.computeIfAbsent(number, k -> new TreeSet<>()).add(index); + d.put(index, number); + g.computeIfAbsent(number, k -> new TreeSet<>()).add(index); } public int find(int number) { - return t.containsKey(number) ? t.get(number).first() : -1; + var ids = g.get(number); + return ids == null || ids.isEmpty() ? -1 : ids.first(); } } diff --git a/solution/2300-2399/2349.Design a Number Container System/Solution.py b/solution/2300-2399/2349.Design a Number Container System/Solution.py index babe82453f10f..8cba7dc3ccb9c 100644 --- a/solution/2300-2399/2349.Design a Number Container System/Solution.py +++ b/solution/2300-2399/2349.Design a Number Container System/Solution.py @@ -2,20 +2,21 @@ class NumberContainers: + def __init__(self): - self.mp = {} - self.t = defaultdict(SortedSet) + self.d = {} + self.g = defaultdict(SortedSet) def change(self, index: int, number: int) -> None: - if index in self.mp: - v = self.mp[index] - self.t[v].remove(index) - self.mp[index] = number - self.t[number].add(index) + if index in self.d: + old_number = self.d[index] + self.g[old_number].remove(index) + self.d[index] = number + self.g[number].add(index) def find(self, number: int) -> int: - s = self.t[number] - return s[0] if s else -1 + ids = self.g[number] + return ids[0] if ids else -1 # Your NumberContainers object will be instantiated and called as such: diff --git a/solution/2300-2399/2349.Design a Number Container System/Solution.ts b/solution/2300-2399/2349.Design a Number Container System/Solution.ts new file mode 100644 index 0000000000000..e720b01a5275f --- /dev/null +++ b/solution/2300-2399/2349.Design a Number Container System/Solution.ts @@ -0,0 +1,521 @@ +class NumberContainers { + private d = new Map(); + private g = new Map>(); + constructor() {} + + change(index: number, number: number): void { + if (this.d.has(index)) { + const oldNumber = this.d.get(index)!; + this.g.get(oldNumber)!.delete(index); + if (!this.g.get(oldNumber)!.size()) { + this.g.delete(oldNumber); + } + } + this.d.set(index, number); + if (!this.g.has(number)) { + this.g.set(number, new TreeSet()); + } + this.g.get(number)!.add(index); + } + + find(number: number): number { + return this.g.has(number) ? this.g.get(number)!.first()! : -1; + } +} + +type Compare = (lhs: T, rhs: T) => number; + +class RBTreeNode { + data: T; + count: number; + left: RBTreeNode | null; + right: RBTreeNode | null; + parent: RBTreeNode | null; + color: number; + constructor(data: T) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + + sibling(): RBTreeNode | null { + if (!this.parent) return null; // sibling null if no parent + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + + isOnLeft(): boolean { + return this === this.parent!.left; + } + + hasRedChild(): boolean { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +} + +class RBTree { + root: RBTreeNode | null; + lt: (l: T, r: T) => boolean; + constructor(compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0)) { + this.root = null; + this.lt = (l: T, r: T) => compare(l, r) < 0; + } + + rotateLeft(pt: RBTreeNode): void { + const right = pt.right!; + pt.right = right.left; + + if (pt.right) pt.right.parent = pt; + right.parent = pt.parent; + + if (!pt.parent) this.root = right; + else if (pt === pt.parent.left) pt.parent.left = right; + else pt.parent.right = right; + + right.left = pt; + pt.parent = right; + } + + rotateRight(pt: RBTreeNode): void { + const left = pt.left!; + pt.left = left.right; + + if (pt.left) pt.left.parent = pt; + left.parent = pt.parent; + + if (!pt.parent) this.root = left; + else if (pt === pt.parent.left) pt.parent.left = left; + else pt.parent.right = left; + + left.right = pt; + pt.parent = left; + } + + swapColor(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + + swapData(p1: RBTreeNode, p2: RBTreeNode): void { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + + fixAfterInsert(pt: RBTreeNode): void { + let parent = null; + let grandParent = null; + + while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) { + parent = pt.parent; + grandParent = pt.parent.parent; + + /* Case : A + Parent of pt is left child of Grand-parent of pt */ + if (parent === grandParent?.left) { + const uncle = grandParent.right; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + /* Case : 2 + pt is right child of its parent + Left-rotation required */ + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is left child of its parent + Right-rotation required */ + this.rotateRight(grandParent); + this.swapColor(parent!, grandParent); + pt = parent!; + } + } else { + /* Case : B + Parent of pt is right child of Grand-parent of pt */ + const uncle = grandParent!.left; + + /* Case : 1 + The uncle of pt is also red + Only Recoloring required */ + if (uncle != null && uncle.color === 0) { + grandParent!.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent!; + } else { + /* Case : 2 + pt is left child of its parent + Right-rotation required */ + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + + /* Case : 3 + pt is right child of its parent + Left-rotation required */ + this.rotateLeft(grandParent!); + this.swapColor(parent!, grandParent!); + pt = parent!; + } + } + } + this.root!.color = 1; + } + + delete(val: T): boolean { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + + deleteAll(val: T): boolean { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + + deleteNode(v: RBTreeNode): void { + const u = BSTreplace(v); + + // True when u and v are both black + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent!; + + if (!u) { + // u is null therefore v is leaf + if (v === this.root) this.root = null; + // v is root, making root null + else { + if (uvBlack) { + // u and v both black + // v is leaf, fix double black at v + this.fixDoubleBlack(v); + } else { + // u or v is red + if (v.sibling()) { + // sibling is not null, make it red" + v.sibling()!.color = 0; + } + } + // delete v from the tree + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + + if (!v.left || !v.right) { + // v has 1 child + if (v === this.root) { + // v is root, assign the value of u to v, and delete u + v.data = u.data; + v.left = v.right = null; + } else { + // Detach v from tree and move u up + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + // u and v both black, fix double black at u + else u.color = 1; // u or v red, color u black + } + return; + } + + // v has 2 children, swap data with successor and recurse + this.swapData(u, v); + this.deleteNode(u); + + // find node that replaces a deleted node in BST + function BSTreplace(x: RBTreeNode): RBTreeNode | null { + // when node have 2 children + if (x.left && x.right) return successor(x.right); + // when leaf + if (!x.left && !x.right) return null; + // when single child + return x.left ?? x.right; + } + // find node that do not have a left child + // in the subtree of the given node + function successor(x: RBTreeNode): RBTreeNode { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + + fixDoubleBlack(x: RBTreeNode): void { + if (x === this.root) return; // Reached root + + const sibling = x.sibling(); + const parent = x.parent!; + if (!sibling) { + // No sibiling, double black pushed up + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + // Sibling red + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + // left case + else this.rotateLeft(parent); // right case + this.fixDoubleBlack(x); + } else { + // Sibling black + if (sibling.hasRedChild()) { + // at least 1 red children + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + // left left + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + // right left + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + // left right + sibling.right!.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + // right right + sibling.right!.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + // 2 black children + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + + insert(data: T): boolean { + // search for a position to insert + let parent = this.root; + while (parent) { + if (this.lt(data, parent.data)) { + if (!parent.left) break; + else parent = parent.left; + } else if (this.lt(parent.data, data)) { + if (!parent.right) break; + else parent = parent.right; + } else break; + } + + // insert node into parent + const node = new RBTreeNode(data); + if (!parent) this.root = node; + else if (this.lt(node.data, parent.data)) parent.left = node; + else if (this.lt(parent.data, node.data)) parent.right = node; + else { + parent.count++; + return false; + } + node.parent = parent; + this.fixAfterInsert(node); + return true; + } + + find(data: T): RBTreeNode | null { + let p = this.root; + while (p) { + if (this.lt(data, p.data)) { + p = p.left; + } else if (this.lt(p.data, data)) { + p = p.right; + } else break; + } + return p ?? null; + } + + *inOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.inOrder(root.left!)) yield v; + yield root.data; + for (const v of this.inOrder(root.right!)) yield v; + } + + *reverseInOrder(root: RBTreeNode = this.root!): Generator { + if (!root) return; + for (const v of this.reverseInOrder(root.right!)) yield v; + yield root.data; + for (const v of this.reverseInOrder(root.left!)) yield v; + } +} + +class TreeSet { + _size: number; + tree: RBTree; + compare: Compare; + constructor( + collection: T[] | Compare = [], + compare: Compare = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0), + ) { + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const val of collection) this.add(val); + } + + size(): number { + return this._size; + } + + has(val: T): boolean { + return !!this.tree.find(val); + } + + add(val: T): boolean { + const successful = this.tree.insert(val); + this._size += successful ? 1 : 0; + return successful; + } + + delete(val: T): boolean { + const deleted = this.tree.deleteAll(val); + this._size -= deleted ? 1 : 0; + return deleted; + } + + ceil(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(p.data, val) >= 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + floor(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(val, p.data) >= 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + higher(val: T): T | undefined { + let p = this.tree.root; + let higher = null; + while (p) { + if (this.compare(val, p.data) < 0) { + higher = p; + p = p.left; + } else { + p = p.right; + } + } + return higher?.data; + } + + lower(val: T): T | undefined { + let p = this.tree.root; + let lower = null; + while (p) { + if (this.compare(p.data, val) < 0) { + lower = p; + p = p.right; + } else { + p = p.left; + } + } + return lower?.data; + } + + first(): T | undefined { + return this.tree.inOrder().next().value; + } + + last(): T | undefined { + return this.tree.reverseInOrder().next().value; + } + + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + pop(): T | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + *[Symbol.iterator](): Generator { + for (const val of this.values()) yield val; + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) yield val; + return undefined; + } + + /** + * Return a generator for reverse order traversing the set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) yield val; + return undefined; + } +} + +/** + * Your NumberContainers object will be instantiated and called as such: + * var obj = new NumberContainers() + * obj.change(index,number) + * var param_2 = obj.find(number) + */ diff --git a/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README.md b/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README.md index d23ab089fe8ee..d5b4a743ab764 100644 --- a/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README.md +++ b/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README.md @@ -59,7 +59,13 @@ tags: -### 方法一 +### 方法一:分情况讨论 + +如果起点和终点相同,那么只有当 $t \neq 1$ 时,才能在给定时间到达终点。 + +否则,我们可以计算出起点和终点的横纵坐标之差,然后取最大值,如果最大值小于等于给定时间,那么就可以在给定时间到达终点。 + +时间复杂度 $O(1)$,空间复杂度 $O(1)$。 @@ -143,8 +149,9 @@ function isReachableAtTime(sx: number, sy: number, fx: number, fy: number, t: nu ```cs public class Solution { public bool IsReachableAtTime(int sx, int sy, int fx, int fy, int t) { - if (sx == fx && sy == fy) + if (sx == fx && sy == fy) { return t != 1; + } return Math.Max(Math.Abs(sx - fx), Math.Abs(sy - fy)) <= t; } } diff --git a/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README_EN.md b/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README_EN.md index 2db4ad1b539c7..387cdc8de2679 100644 --- a/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README_EN.md +++ b/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/README_EN.md @@ -57,7 +57,13 @@ tags: -### Solution 1 +### Solution 1: Case Discussion + +If the starting point and the destination are the same, then we can only reach the destination within the given time if $t \neq 1$. + +Otherwise, we can calculate the difference in the x and y coordinates between the starting point and the destination, and then take the maximum value. If the maximum value is less than or equal to the given time, then we can reach the destination within the given time. + +The time complexity is $O(1)$, and the space complexity is $O(1)$. @@ -141,8 +147,9 @@ function isReachableAtTime(sx: number, sy: number, fx: number, fy: number, t: nu ```cs public class Solution { public bool IsReachableAtTime(int sx, int sy, int fx, int fy, int t) { - if (sx == fx && sy == fy) + if (sx == fx && sy == fy) { return t != 1; + } return Math.Max(Math.Abs(sx - fx), Math.Abs(sy - fy)) <= t; } } diff --git a/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/Solution.cs b/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/Solution.cs index 4e3f78d7f3911..376d61513e221 100644 --- a/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/Solution.cs +++ b/solution/2800-2899/2849.Determine if a Cell Is Reachable at a Given Time/Solution.cs @@ -1,7 +1,8 @@ public class Solution { public bool IsReachableAtTime(int sx, int sy, int fx, int fy, int t) { - if (sx == fx && sy == fy) + if (sx == fx && sy == fy) { return t != 1; + } return Math.Max(Math.Abs(sx - fx), Math.Abs(sy - fy)) <= t; } }