diff --git "a/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/README.md" "b/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/README.md" index 8e0eac472629d..d1fb5d5a450cb 100644 --- "a/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/README.md" +++ "b/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/README.md" @@ -47,7 +47,13 @@ -“滑动窗口 + 有序集合”实现。 +**方法一:滑动窗口 + 有序集合** + +维护一个大小为 $k$ 的滑动窗口,窗口中的元素保持有序。 + +遍历数组 `nums`,对于每个元素 $nums[i]$,我们在有序集合中查找第一个大于等于 $nums[i] - t$ 的元素,如果元素存在,并且该元素小于等于 $nums[i] + t$,说明找到了一对符合条件的元素,返回 `true`。否则,我们将 $nums[i]$ 插入到有序集合中,并且如果有序集合的大小超过了 $k$,我们需要将最早加入有序集合的元素删除。 + +时间复杂度 $O(n\times \log k)$,其中 $n$ 是数组 `nums` 的长度。对于每个元素,我们需要 $O(\log k)$ 的时间来查找有序集合中的元素,一共有 $n$ 个元素,因此总时间复杂度是 $O(n\times \log k)$。 @@ -139,6 +145,673 @@ func containsNearbyAlmostDuplicate(nums []int, k int, t int) bool { } ``` +### **TypeScript** + +```ts +function containsNearbyAlmostDuplicate( + nums: number[], + k: number, + t: number, +): boolean { + const ts = new TreeSet(); + for (let i = 0; i < nums.length; ++i) { + const x = ts.ceil(nums[i] - t); + if (x != null && x <= nums[i] + t) { + return true; + } + ts.add(nums[i]); + if (i >= k) { + ts.delete(nums[i - k]); + } + } + return false; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} +``` + ### **...** ``` diff --git "a/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/Solution.ts" "b/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/Solution.ts" new file mode 100644 index 0000000000000..ea04552ac72b8 --- /dev/null +++ "b/lcof2/\345\211\221\346\214\207 Offer II 057. \345\200\274\345\222\214\344\270\213\346\240\207\344\271\213\345\267\256\351\203\275\345\234\250\347\273\231\345\256\232\347\232\204\350\214\203\345\233\264\345\206\205/Solution.ts" @@ -0,0 +1,662 @@ +function containsNearbyAlmostDuplicate( + nums: number[], + k: number, + t: number, +): boolean { + const ts = new TreeSet(); + for (let i = 0; i < nums.length; ++i) { + const x = ts.ceil(nums[i] - t); + if (x != null && x <= nums[i] + t) { + return true; + } + ts.add(nums[i]); + if (i >= k) { + ts.delete(nums[i - k]); + } + } + return false; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} diff --git a/solution/0200-0299/0220.Contains Duplicate III/README.md b/solution/0200-0299/0220.Contains Duplicate III/README.md index e7536e75b35f9..2789b404acc49 100644 --- a/solution/0200-0299/0220.Contains Duplicate III/README.md +++ b/solution/0200-0299/0220.Contains Duplicate III/README.md @@ -171,6 +171,673 @@ public class Solution { } ``` +### **TypeScript** + +```ts +function containsNearbyAlmostDuplicate( + nums: number[], + indexDiff: number, + valueDiff: number, +): boolean { + const ts = new TreeSet(); + for (let i = 0; i < nums.length; ++i) { + const x = ts.ceil(nums[i] - valueDiff); + if (x != null && x <= nums[i] + valueDiff) { + return true; + } + ts.add(nums[i]); + if (i >= indexDiff) { + ts.delete(nums[i - indexDiff]); + } + } + return false; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} +``` + ### **...** ``` diff --git a/solution/0200-0299/0220.Contains Duplicate III/README_EN.md b/solution/0200-0299/0220.Contains Duplicate III/README_EN.md index 3a7fac2bc08cf..50b9a25a80d82 100644 --- a/solution/0200-0299/0220.Contains Duplicate III/README_EN.md +++ b/solution/0200-0299/0220.Contains Duplicate III/README_EN.md @@ -163,6 +163,673 @@ public class Solution { } ``` +### **TypeScript** + +```ts +function containsNearbyAlmostDuplicate( + nums: number[], + indexDiff: number, + valueDiff: number, +): boolean { + const ts = new TreeSet(); + for (let i = 0; i < nums.length; ++i) { + const x = ts.ceil(nums[i] - valueDiff); + if (x != null && x <= nums[i] + valueDiff) { + return true; + } + ts.add(nums[i]); + if (i >= indexDiff) { + ts.delete(nums[i - indexDiff]); + } + } + return false; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} +``` + ### **...** ``` diff --git a/solution/0200-0299/0220.Contains Duplicate III/Solution.ts b/solution/0200-0299/0220.Contains Duplicate III/Solution.ts new file mode 100644 index 0000000000000..90f527c55bac3 --- /dev/null +++ b/solution/0200-0299/0220.Contains Duplicate III/Solution.ts @@ -0,0 +1,662 @@ +function containsNearbyAlmostDuplicate( + nums: number[], + indexDiff: number, + valueDiff: number, +): boolean { + const ts = new TreeSet(); + for (let i = 0; i < nums.length; ++i) { + const x = ts.ceil(nums[i] - valueDiff); + if (x != null && x <= nums[i] + valueDiff) { + return true; + } + ts.add(nums[i]); + if (i >= indexDiff) { + ts.delete(nums[i - indexDiff]); + } + } + return false; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} diff --git a/solution/0200-0299/0229.Majority Element II/README.md b/solution/0200-0299/0229.Majority Element II/README.md index e8c0d5f581e5c..378200ccf2cfe 100644 --- a/solution/0200-0299/0229.Majority Element II/README.md +++ b/solution/0200-0299/0229.Majority Element II/README.md @@ -230,6 +230,27 @@ public class Solution { } ``` +### **PHP** + +```php +class Solution { + /** + * @param Integer[] $nums + * @return Integer[] + */ + function majorityElement($nums) { + $rs = []; + $n = count($nums); + for ($i = 0; $i < $n; $i++) { + $hashmap[$nums[$i]] += 1; + if ($hashmap[$nums[$i]] > $n / 3) + array_push($rs, $nums[$i]); + } + return array_unique($rs); + } +} +``` + ### **...** ``` diff --git a/solution/0200-0299/0229.Majority Element II/README_EN.md b/solution/0200-0299/0229.Majority Element II/README_EN.md index 5b796802b06b7..9650863146185 100644 --- a/solution/0200-0299/0229.Majority Element II/README_EN.md +++ b/solution/0200-0299/0229.Majority Element II/README_EN.md @@ -219,6 +219,27 @@ public class Solution { } ``` +### **PHP** + +```php +class Solution { + /** + * @param Integer[] $nums + * @return Integer[] + */ + function majorityElement($nums) { + $rs = []; + $n = count($nums); + for ($i = 0; $i < $n; $i++) { + $hashmap[$nums[$i]] += 1; + if ($hashmap[$nums[$i]] > $n / 3) + array_push($rs, $nums[$i]); + } + return array_unique($rs); + } +} +``` + ### **...** ``` diff --git a/solution/0200-0299/0229.Majority Element II/Solution.php b/solution/0200-0299/0229.Majority Element II/Solution.php new file mode 100644 index 0000000000000..15fb1a90d9d62 --- /dev/null +++ b/solution/0200-0299/0229.Majority Element II/Solution.php @@ -0,0 +1,16 @@ +class Solution { + /** + * @param Integer[] $nums + * @return Integer[] + */ + function majorityElement($nums) { + $rs = []; + $n = count($nums); + for ($i = 0; $i < $n; $i++) { + $hashmap[$nums[$i]] += 1; + if ($hashmap[$nums[$i]] > $n / 3) + array_push($rs, $nums[$i]); + } + return array_unique($rs); + } +} \ No newline at end of file diff --git a/solution/2600-2699/2604.Minimum Time to Eat All Grains/README.md b/solution/2600-2699/2604.Minimum Time to Eat All Grains/README.md index b2ef85c1ee88a..7db8980467805 100644 --- a/solution/2600-2699/2604.Minimum Time to Eat All Grains/README.md +++ b/solution/2600-2699/2604.Minimum Time to Eat All Grains/README.md @@ -270,6 +270,58 @@ func abs(x int) int { } ``` +### **TypeScript** + +```ts +function minimumTime(hens: number[], grains: number[]): number { + hens.sort((a, b) => a - b); + grains.sort((a, b) => a - b); + const m = grains.length; + let l = 0; + let r = Math.abs(hens[0] - grains[0]) + grains[m - 1] - grains[0] + 1; + + const check = (t: number): boolean => { + let j = 0; + for (const x of hens) { + if (j === m) { + return true; + } + const y = grains[j]; + if (y <= x) { + const d = x - y; + if (d > t) { + return false; + } + while (j < m && grains[j] <= x) { + ++j; + } + while ( + j < m && + Math.min(d, grains[j] - x) + grains[j] - y <= t + ) { + ++j; + } + } else { + while (j < m && grains[j] - x <= t) { + ++j; + } + } + } + return j === m; + }; + + while (l < r) { + const mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l; +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2604.Minimum Time to Eat All Grains/README_EN.md b/solution/2600-2699/2604.Minimum Time to Eat All Grains/README_EN.md index 0fde9c2ef3547..27f5267e9dcf7 100644 --- a/solution/2600-2699/2604.Minimum Time to Eat All Grains/README_EN.md +++ b/solution/2600-2699/2604.Minimum Time to Eat All Grains/README_EN.md @@ -249,6 +249,58 @@ func abs(x int) int { } ``` +### **TypeScript** + +```ts +function minimumTime(hens: number[], grains: number[]): number { + hens.sort((a, b) => a - b); + grains.sort((a, b) => a - b); + const m = grains.length; + let l = 0; + let r = Math.abs(hens[0] - grains[0]) + grains[m - 1] - grains[0] + 1; + + const check = (t: number): boolean => { + let j = 0; + for (const x of hens) { + if (j === m) { + return true; + } + const y = grains[j]; + if (y <= x) { + const d = x - y; + if (d > t) { + return false; + } + while (j < m && grains[j] <= x) { + ++j; + } + while ( + j < m && + Math.min(d, grains[j] - x) + grains[j] - y <= t + ) { + ++j; + } + } else { + while (j < m && grains[j] - x <= t) { + ++j; + } + } + } + return j === m; + }; + + while (l < r) { + const mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l; +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2604.Minimum Time to Eat All Grains/Solution.ts b/solution/2600-2699/2604.Minimum Time to Eat All Grains/Solution.ts new file mode 100644 index 0000000000000..78d17a5b0f2f0 --- /dev/null +++ b/solution/2600-2699/2604.Minimum Time to Eat All Grains/Solution.ts @@ -0,0 +1,47 @@ +function minimumTime(hens: number[], grains: number[]): number { + hens.sort((a, b) => a - b); + grains.sort((a, b) => a - b); + const m = grains.length; + let l = 0; + let r = Math.abs(hens[0] - grains[0]) + grains[m - 1] - grains[0] + 1; + + const check = (t: number): boolean => { + let j = 0; + for (const x of hens) { + if (j === m) { + return true; + } + const y = grains[j]; + if (y <= x) { + const d = x - y; + if (d > t) { + return false; + } + while (j < m && grains[j] <= x) { + ++j; + } + while ( + j < m && + Math.min(d, grains[j] - x) + grains[j] - y <= t + ) { + ++j; + } + } else { + while (j < m && grains[j] - x <= t) { + ++j; + } + } + } + return j === m; + }; + + while (l < r) { + const mid = (l + r) >> 1; + if (check(mid)) { + r = mid; + } else { + l = mid + 1; + } + } + return l; +} diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README.md b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README.md index d68811e583ec5..0294cb7735ee0 100644 --- a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README.md +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README.md @@ -330,6 +330,100 @@ func min(a, b int) int { } ``` +### **TypeScript** + +```ts +function minNumber(nums1: number[], nums2: number[]): number { + let ans = 100; + for (const a of nums1) { + for (const b of nums2) { + if (a == b) { + ans = Math.min(ans, a); + } else { + ans = Math.min(ans, a * 10 + b, b * 10 + a); + } + } + } + return ans; +} +``` + +```ts +function minNumber(nums1: number[], nums2: number[]): number { + const s1: boolean[] = new Array(10).fill(false); + const s2: boolean[] = new Array(10).fill(false); + for (const x of nums1) { + s1[x] = true; + } + for (const x of nums2) { + s2[x] = true; + } + let a = 0; + let b = 0; + for (let i = 1; i < 10; ++i) { + if (s1[i] && s2[i]) { + return i; + } + if (a == 0 && s1[i]) { + a = i; + } + if (b == 0 && s2[i]) { + b = i; + } + } + return Math.min(a * 10 + b, b * 10 + a); +} +``` + +```ts +function minNumber(nums1: number[], nums2: number[]): number { + let mask1: number = 0; + let mask2: number = 0; + for (const x of nums1) { + mask1 |= 1 << x; + } + for (const x of nums2) { + mask2 |= 1 << x; + } + const mask = mask1 & mask2; + if (mask !== 0) { + return numberOfTrailingZeros(mask); + } + const a = numberOfTrailingZeros(mask1); + const b = numberOfTrailingZeros(mask2); + return Math.min(a * 10 + b, b * 10 + a); +} + +function numberOfTrailingZeros(i: number): number { + let y = 0; + if (i === 0) { + return 32; + } + let n = 31; + y = i << 16; + if (y != 0) { + n = n - 16; + i = y; + } + y = i << 8; + if (y != 0) { + n = n - 8; + i = y; + } + y = i << 4; + if (y != 0) { + n = n - 4; + i = y; + } + y = i << 2; + if (y != 0) { + n = n - 2; + i = y; + } + return n - ((i << 1) >>> 31); +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README_EN.md b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README_EN.md index 9b2a611786507..8b0c110b97b43 100644 --- a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README_EN.md +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/README_EN.md @@ -300,6 +300,100 @@ func min(a, b int) int { } ``` +### **TypeScript** + +```ts +function minNumber(nums1: number[], nums2: number[]): number { + let ans = 100; + for (const a of nums1) { + for (const b of nums2) { + if (a == b) { + ans = Math.min(ans, a); + } else { + ans = Math.min(ans, a * 10 + b, b * 10 + a); + } + } + } + return ans; +} +``` + +```ts +function minNumber(nums1: number[], nums2: number[]): number { + const s1: boolean[] = new Array(10).fill(false); + const s2: boolean[] = new Array(10).fill(false); + for (const x of nums1) { + s1[x] = true; + } + for (const x of nums2) { + s2[x] = true; + } + let a = 0; + let b = 0; + for (let i = 1; i < 10; ++i) { + if (s1[i] && s2[i]) { + return i; + } + if (a == 0 && s1[i]) { + a = i; + } + if (b == 0 && s2[i]) { + b = i; + } + } + return Math.min(a * 10 + b, b * 10 + a); +} +``` + +```ts +function minNumber(nums1: number[], nums2: number[]): number { + let mask1: number = 0; + let mask2: number = 0; + for (const x of nums1) { + mask1 |= 1 << x; + } + for (const x of nums2) { + mask2 |= 1 << x; + } + const mask = mask1 & mask2; + if (mask !== 0) { + return numberOfTrailingZeros(mask); + } + const a = numberOfTrailingZeros(mask1); + const b = numberOfTrailingZeros(mask2); + return Math.min(a * 10 + b, b * 10 + a); +} + +function numberOfTrailingZeros(i: number): number { + let y = 0; + if (i === 0) { + return 32; + } + let n = 31; + y = i << 16; + if (y != 0) { + n = n - 16; + i = y; + } + y = i << 8; + if (y != 0) { + n = n - 8; + i = y; + } + y = i << 4; + if (y != 0) { + n = n - 4; + i = y; + } + y = i << 2; + if (y != 0) { + n = n - 2; + i = y; + } + return n - ((i << 1) >>> 31); +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.cpp b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.cpp index 0e5298d8239b3..b5bcf71e0cd3b 100644 --- a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.cpp +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.cpp @@ -1,16 +1,19 @@ class Solution { public: int minNumber(vector& nums1, vector& nums2) { - int ans = 100; - for (int a : nums1) { - for (int b : nums2) { - if (a == b) { - ans = min(ans, a); - } else { - ans = min({ans, a * 10 + b, b * 10 + a}); - } - } + int mask1 = 0, mask2 = 0; + for (int x : nums1) { + mask1 |= 1 << x; } - return ans; + for (int x : nums2) { + mask2 |= 1 << x; + } + int mask = mask1 & mask2; + if (mask) { + return __builtin_ctz(mask); + } + int a = __builtin_ctz(mask1); + int b = __builtin_ctz(mask2); + return min(a * 10 + b, b * 10 + a); } }; \ No newline at end of file diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.go b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.go index 783e8a448c877..500fba4d114a9 100644 --- a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.go +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.go @@ -1,15 +1,16 @@ func minNumber(nums1 []int, nums2 []int) int { - ans := 100 - for _, a := range nums1 { - for _, b := range nums2 { - if a == b { - ans = min(ans, a) - } else { - ans = min(ans, min(a*10+b, b*10+a)) - } - } + var mask1, mask2 uint + for _, x := range nums1 { + mask1 |= 1 << x } - return ans + for _, x := range nums2 { + mask2 |= 1 << x + } + if mask := mask1 & mask2; mask != 0 { + return bits.TrailingZeros(mask) + } + a, b := bits.TrailingZeros(mask1), bits.TrailingZeros(mask2) + return min(a*10+b, b*10+a) } func min(a, b int) int { diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.java b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.java index 3fb58eeeeaa61..002d72ba65520 100644 --- a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.java +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.java @@ -1,15 +1,18 @@ class Solution { public int minNumber(int[] nums1, int[] nums2) { - int ans = 100; - for (int a : nums1) { - for (int b : nums2) { - if (a == b) { - ans = Math.min(ans, a); - } else { - ans = Math.min(ans, Math.min(a * 10 + b, b * 10 + a)); - } - } + int mask1 = 0, mask2 = 0; + for (int x : nums1) { + mask1 |= 1 << x; } - return ans; + for (int x : nums2) { + mask2 |= 1 << x; + } + int mask = mask1 & mask2; + if (mask != 0) { + return Integer.numberOfTrailingZeros(mask); + } + int a = Integer.numberOfTrailingZeros(mask1); + int b = Integer.numberOfTrailingZeros(mask2); + return Math.min(a * 10 + b, b * 10 + a); } } \ No newline at end of file diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.py b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.py index 46585c47d6975..1c7e6038a0f7e 100644 --- a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.py +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.py @@ -1,10 +1,13 @@ class Solution: def minNumber(self, nums1: List[int], nums2: List[int]) -> int: - ans = 100 - for a in nums1: - for b in nums2: - if a == b: - ans = min(ans, a) - else: - ans = min(ans, 10 * a + b, 10 * b + a) - return ans + mask1 = mask2 = 0 + for x in nums1: + mask1 |= 1 << x + for x in nums2: + mask2 |= 1 << x + mask = mask1 & mask2 + if mask: + return (mask & -mask).bit_length() - 1 + a = (mask1 & -mask1).bit_length() - 1 + b = (mask2 & -mask2).bit_length() - 1 + return min(a * 10 + b, b * 10 + a) diff --git a/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.ts b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.ts new file mode 100644 index 0000000000000..4694d1b93a6da --- /dev/null +++ b/solution/2600-2699/2605.Form Smallest Number From Two Digit Arrays/Solution.ts @@ -0,0 +1,46 @@ +function minNumber(nums1: number[], nums2: number[]): number { + let mask1: number = 0; + let mask2: number = 0; + for (const x of nums1) { + mask1 |= 1 << x; + } + for (const x of nums2) { + mask2 |= 1 << x; + } + const mask = mask1 & mask2; + if (mask !== 0) { + return numberOfTrailingZeros(mask); + } + const a = numberOfTrailingZeros(mask1); + const b = numberOfTrailingZeros(mask2); + return Math.min(a * 10 + b, b * 10 + a); +} + +function numberOfTrailingZeros(i: number): number { + let y = 0; + if (i === 0) { + return 32; + } + let n = 31; + y = i << 16; + if (y != 0) { + n = n - 16; + i = y; + } + y = i << 8; + if (y != 0) { + n = n - 8; + i = y; + } + y = i << 4; + if (y != 0) { + n = n - 4; + i = y; + } + y = i << 2; + if (y != 0) { + n = n - 2; + i = y; + } + return n - ((i << 1) >>> 31); +} diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/README.md b/solution/2600-2699/2606.Find the Substring With Maximum Cost/README.md index 2bfeb2fb34592..7161878d5e456 100644 --- a/solution/2600-2699/2606.Find the Substring With Maximum Cost/README.md +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/README.md @@ -85,10 +85,10 @@ ```python class Solution: def maximumCostSubstring(self, s: str, chars: str, vals: List[int]) -> int: - d = {c: i for i, c in enumerate(chars)} + d = {c: v for c, v in zip(chars, vals)} ans = tot = mi = 0 for c in s: - v = vals[d[c]] if c in d else ord(c) - ord('a') + 1 + v = d.get(c, ord(c) - ord('a') + 1) tot += v ans = max(ans, tot - mi) mi = min(mi, tot) @@ -98,10 +98,10 @@ class Solution: ```python class Solution: def maximumCostSubstring(self, s: str, chars: str, vals: List[int]) -> int: - d = {c: i for i, c in enumerate(chars)} + d = {c: v for c, v in zip(chars, vals)} ans = f = 0 for c in s: - v = vals[d[c]] if c in d else ord(c) - ord('a') + 1 + v = d.get(c, ord(c) - ord('a') + 1) f = max(f, 0) + v ans = max(ans, f) return ans @@ -115,16 +115,17 @@ class Solution: class Solution { public int maximumCostSubstring(String s, String chars, int[] vals) { int[] d = new int[26]; - Arrays.fill(d, -1); + for (int i = 0; i < d.length; ++i) { + d[i] = i + 1; + } int m = chars.length(); for (int i = 0; i < m; ++i) { - d[chars.charAt(i) - 'a'] = i; + d[chars.charAt(i) - 'a'] = vals[i]; } int ans = 0, tot = 0, mi = 0; int n = s.length(); for (int i = 0; i < n; ++i) { - int j = s.charAt(i) - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; + int v = d[s.charAt(i) - 'a']; tot += v; ans = Math.max(ans, tot - mi); mi = Math.min(mi, tot); @@ -138,16 +139,17 @@ class Solution { class Solution { public int maximumCostSubstring(String s, String chars, int[] vals) { int[] d = new int[26]; - Arrays.fill(d, -1); + for (int i = 0; i < d.length; ++i) { + d[i] = i + 1; + } int m = chars.length(); for (int i = 0; i < m; ++i) { - d[chars.charAt(i) - 'a'] = i; + d[chars.charAt(i) - 'a'] = vals[i]; } int ans = 0, f = 0; int n = s.length(); for (int i = 0; i < n; ++i) { - int j = s.charAt(i) - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; + int v = d[s.charAt(i) - 'a']; f = Math.max(f, 0) + v; ans = Math.max(ans, f); } @@ -162,15 +164,15 @@ class Solution { class Solution { public: int maximumCostSubstring(string s, string chars, vector& vals) { - vector d(26, -1); + vector d(26); + iota(d.begin(), d.end(), 1); int m = chars.size(); for (int i = 0; i < m; ++i) { - d[chars[i] - 'a'] = i; + d[chars[i] - 'a'] = vals[i]; } int ans = 0, tot = 0, mi = 0; for (char& c : s) { - int j = c - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; + int v = d[c - 'a']; tot += v; ans = max(ans, tot - mi); mi = min(mi, tot); @@ -184,15 +186,15 @@ public: class Solution { public: int maximumCostSubstring(string s, string chars, vector& vals) { - vector d(26, -1); + vector d(26); + iota(d.begin(), d.end(), 1); int m = chars.size(); for (int i = 0; i < m; ++i) { - d[chars[i] - 'a'] = i; + d[chars[i] - 'a'] = vals[i]; } int ans = 0, f = 0; for (char& c : s) { - int j = c - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; + int v = d[c - 'a']; f = max(f, 0) + v; ans = max(ans, f); } @@ -207,18 +209,14 @@ public: func maximumCostSubstring(s string, chars string, vals []int) (ans int) { d := [26]int{} for i := range d { - d[i] = -1 + d[i] = i + 1 } for i, c := range chars { - d[c-'a'] = i + d[c-'a'] = vals[i] } tot, mi := 0, 0 for _, c := range s { - j := int(c - 'a') - v := j + 1 - if d[j] != -1 { - v = vals[d[j]] - } + v := d[c-'a'] tot += v ans = max(ans, tot-mi) mi = min(mi, tot) @@ -245,18 +243,14 @@ func min(a, b int) int { func maximumCostSubstring(s string, chars string, vals []int) (ans int) { d := [26]int{} for i := range d { - d[i] = -1 + d[i] = i + 1 } for i, c := range chars { - d[c-'a'] = i + d[c-'a'] = vals[i] } f := 0 for _, c := range s { - j := int(c - 'a') - v := j + 1 - if d[j] != -1 { - v = vals[d[j]] - } + v := d[c-'a'] f = max(f, 0) + v ans = max(ans, f) } @@ -278,6 +272,50 @@ func min(a, b int) int { } ``` +### **TypeScript** + +```ts +function maximumCostSubstring( + s: string, + chars: string, + vals: number[], +): number { + const d: number[] = Array.from({ length: 26 }, (_, i) => i + 1); + for (let i = 0; i < chars.length; ++i) { + d[chars.charCodeAt(i) - 97] = vals[i]; + } + let ans = 0; + let tot = 0; + let mi = 0; + for (const c of s) { + tot += d[c.charCodeAt(0) - 97]; + ans = Math.max(ans, tot - mi); + mi = Math.min(mi, tot); + } + return ans; +} +``` + +```ts +function maximumCostSubstring( + s: string, + chars: string, + vals: number[], +): number { + const d: number[] = Array.from({ length: 26 }, (_, i) => i + 1); + for (let i = 0; i < chars.length; ++i) { + d[chars.charCodeAt(i) - 97] = vals[i]; + } + let ans = 0; + let f = 0; + for (const c of s) { + f = Math.max(f, 0) + d[c.charCodeAt(0) - 97]; + ans = Math.max(ans, f); + } + return ans; +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/README_EN.md b/solution/2600-2699/2606.Find the Substring With Maximum Cost/README_EN.md index 6cb850069910b..cd8dece4a3257 100644 --- a/solution/2600-2699/2606.Find the Substring With Maximum Cost/README_EN.md +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/README_EN.md @@ -63,10 +63,10 @@ It can be proven that 0 is the maximum cost. ```python class Solution: def maximumCostSubstring(self, s: str, chars: str, vals: List[int]) -> int: - d = {c: i for i, c in enumerate(chars)} + d = {c: v for c, v in zip(chars, vals)} ans = tot = mi = 0 for c in s: - v = vals[d[c]] if c in d else ord(c) - ord('a') + 1 + v = d.get(c, ord(c) - ord('a') + 1) tot += v ans = max(ans, tot - mi) mi = min(mi, tot) @@ -76,10 +76,10 @@ class Solution: ```python class Solution: def maximumCostSubstring(self, s: str, chars: str, vals: List[int]) -> int: - d = {c: i for i, c in enumerate(chars)} + d = {c: v for c, v in zip(chars, vals)} ans = f = 0 for c in s: - v = vals[d[c]] if c in d else ord(c) - ord('a') + 1 + v = d.get(c, ord(c) - ord('a') + 1) f = max(f, 0) + v ans = max(ans, f) return ans @@ -91,16 +91,17 @@ class Solution: class Solution { public int maximumCostSubstring(String s, String chars, int[] vals) { int[] d = new int[26]; - Arrays.fill(d, -1); + for (int i = 0; i < d.length; ++i) { + d[i] = i + 1; + } int m = chars.length(); for (int i = 0; i < m; ++i) { - d[chars.charAt(i) - 'a'] = i; + d[chars.charAt(i) - 'a'] = vals[i]; } int ans = 0, tot = 0, mi = 0; int n = s.length(); for (int i = 0; i < n; ++i) { - int j = s.charAt(i) - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; + int v = d[s.charAt(i) - 'a']; tot += v; ans = Math.max(ans, tot - mi); mi = Math.min(mi, tot); @@ -114,16 +115,17 @@ class Solution { class Solution { public int maximumCostSubstring(String s, String chars, int[] vals) { int[] d = new int[26]; - Arrays.fill(d, -1); + for (int i = 0; i < d.length; ++i) { + d[i] = i + 1; + } int m = chars.length(); for (int i = 0; i < m; ++i) { - d[chars.charAt(i) - 'a'] = i; + d[chars.charAt(i) - 'a'] = vals[i]; } int ans = 0, f = 0; int n = s.length(); for (int i = 0; i < n; ++i) { - int j = s.charAt(i) - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; + int v = d[s.charAt(i) - 'a']; f = Math.max(f, 0) + v; ans = Math.max(ans, f); } @@ -160,17 +162,18 @@ public: class Solution { public: int maximumCostSubstring(string s, string chars, vector& vals) { - vector d(26, -1); + vector d(26); + iota(d.begin(), d.end(), 1); int m = chars.size(); for (int i = 0; i < m; ++i) { - d[chars[i] - 'a'] = i; + d[chars[i] - 'a'] = vals[i]; } - int ans = 0, f = 0; + int ans = 0, tot = 0, mi = 0; for (char& c : s) { - int j = c - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; - f = max(f, 0) + v; - ans = max(ans, f); + int v = d[c - 'a']; + tot += v; + ans = max(ans, tot - mi); + mi = min(mi, tot); } return ans; } @@ -183,18 +186,14 @@ public: func maximumCostSubstring(s string, chars string, vals []int) (ans int) { d := [26]int{} for i := range d { - d[i] = -1 + d[i] = i + 1 } for i, c := range chars { - d[c-'a'] = i + d[c-'a'] = vals[i] } tot, mi := 0, 0 for _, c := range s { - j := int(c - 'a') - v := j + 1 - if d[j] != -1 { - v = vals[d[j]] - } + v := d[c-'a'] tot += v ans = max(ans, tot-mi) mi = min(mi, tot) @@ -221,18 +220,14 @@ func min(a, b int) int { func maximumCostSubstring(s string, chars string, vals []int) (ans int) { d := [26]int{} for i := range d { - d[i] = -1 + d[i] = i + 1 } for i, c := range chars { - d[c-'a'] = i + d[c-'a'] = vals[i] } f := 0 for _, c := range s { - j := int(c - 'a') - v := j + 1 - if d[j] != -1 { - v = vals[d[j]] - } + v := d[c-'a'] f = max(f, 0) + v ans = max(ans, f) } @@ -254,6 +249,50 @@ func min(a, b int) int { } ``` +### **TypeScript** + +```ts +function maximumCostSubstring( + s: string, + chars: string, + vals: number[], +): number { + const d: number[] = Array.from({ length: 26 }, (_, i) => i + 1); + for (let i = 0; i < chars.length; ++i) { + d[chars.charCodeAt(i) - 97] = vals[i]; + } + let ans = 0; + let tot = 0; + let mi = 0; + for (const c of s) { + tot += d[c.charCodeAt(0) - 97]; + ans = Math.max(ans, tot - mi); + mi = Math.min(mi, tot); + } + return ans; +} +``` + +```ts +function maximumCostSubstring( + s: string, + chars: string, + vals: number[], +): number { + const d: number[] = Array.from({ length: 26 }, (_, i) => i + 1); + for (let i = 0; i < chars.length; ++i) { + d[chars.charCodeAt(i) - 97] = vals[i]; + } + let ans = 0; + let f = 0; + for (const c of s) { + f = Math.max(f, 0) + d[c.charCodeAt(0) - 97]; + ans = Math.max(ans, f); + } + return ans; +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.cpp b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.cpp index 57962e1ad9400..2d92dfef6c591 100644 --- a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.cpp +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.cpp @@ -1,18 +1,17 @@ class Solution { public: int maximumCostSubstring(string s, string chars, vector& vals) { - vector d(26, -1); + vector d(26); + iota(d.begin(), d.end(), 1); int m = chars.size(); for (int i = 0; i < m; ++i) { - d[chars[i] - 'a'] = i; + d[chars[i] - 'a'] = vals[i]; } - int ans = 0, tot = 0, mi = 0; + int ans = 0, f = 0; for (char& c : s) { - int j = c - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; - tot += v; - ans = max(ans, tot - mi); - mi = min(mi, tot); + int v = d[c - 'a']; + f = max(f, 0) + v; + ans = max(ans, f); } return ans; } diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.go b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.go index 647772b312cd0..d8c9b2e2b7d57 100644 --- a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.go +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.go @@ -1,21 +1,16 @@ func maximumCostSubstring(s string, chars string, vals []int) (ans int) { d := [26]int{} for i := range d { - d[i] = -1 + d[i] = i + 1 } for i, c := range chars { - d[c-'a'] = i + d[c-'a'] = vals[i] } - tot, mi := 0, 0 + f := 0 for _, c := range s { - j := int(c - 'a') - v := j + 1 - if d[j] != -1 { - v = vals[d[j]] - } - tot += v - ans = max(ans, tot-mi) - mi = min(mi, tot) + v := d[c-'a'] + f = max(f, 0) + v + ans = max(ans, f) } return } diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.java b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.java index 1f027ce32ae7b..fc3aab51eedb4 100644 --- a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.java +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.java @@ -1,19 +1,19 @@ class Solution { public int maximumCostSubstring(String s, String chars, int[] vals) { int[] d = new int[26]; - Arrays.fill(d, -1); + for (int i = 0; i < d.length; ++i) { + d[i] = i + 1; + } int m = chars.length(); for (int i = 0; i < m; ++i) { - d[chars.charAt(i) - 'a'] = i; + d[chars.charAt(i) - 'a'] = vals[i]; } - int ans = 0, tot = 0, mi = 0; + int ans = 0, f = 0; int n = s.length(); for (int i = 0; i < n; ++i) { - int j = s.charAt(i) - 'a'; - int v = d[j] == -1 ? j + 1 : vals[d[j]]; - tot += v; - ans = Math.max(ans, tot - mi); - mi = Math.min(mi, tot); + int v = d[s.charAt(i) - 'a']; + f = Math.max(f, 0) + v; + ans = Math.max(ans, f); } return ans; } diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.py b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.py index ac13e9c91191e..e3bf230590448 100644 --- a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.py +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.py @@ -1,10 +1,9 @@ class Solution: def maximumCostSubstring(self, s: str, chars: str, vals: List[int]) -> int: - d = {c: i for i, c in enumerate(chars)} - ans = tot = mi = 0 + d = {c: v for c, v in zip(chars, vals)} + ans = f = 0 for c in s: - v = vals[d[c]] if c in d else ord(c) - ord('a') + 1 - tot += v - ans = max(ans, tot - mi) - mi = min(mi, tot) + v = d.get(c, ord(c) - ord('a') + 1) + f = max(f, 0) + v + ans = max(ans, f) return ans diff --git a/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.ts b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.ts new file mode 100644 index 0000000000000..62f1d2a9f1347 --- /dev/null +++ b/solution/2600-2699/2606.Find the Substring With Maximum Cost/Solution.ts @@ -0,0 +1,19 @@ +function maximumCostSubstring( + s: string, + chars: string, + vals: number[], +): number { + const d: number[] = Array.from({ length: 26 }, (_, i) => i + 1); + for (let i = 0; i < chars.length; ++i) { + d[chars.charCodeAt(i) - 97] = vals[i]; + } + let ans = 0; + let tot = 0; + let mi = 0; + for (const c of s) { + tot += d[c.charCodeAt(0) - 97]; + ans = Math.max(ans, tot - mi); + mi = Math.min(mi, tot); + } + return ans; +} diff --git a/solution/2600-2699/2607.Make K-Subarray Sums Equal/README.md b/solution/2600-2699/2607.Make K-Subarray Sums Equal/README.md index 9755f87012a42..4218ac8913ea5 100644 --- a/solution/2600-2699/2607.Make K-Subarray Sums Equal/README.md +++ b/solution/2600-2699/2607.Make K-Subarray Sums Equal/README.md @@ -196,6 +196,35 @@ func gcd(a, b int) int { } ``` +### **TypeScript** + +```ts +function makeSubKSumEqual(arr: number[], k: number): number { + const n = arr.length; + const g = gcd(n, k); + let ans = 0; + for (let i = 0; i < g; ++i) { + const t: number[] = []; + for (let j = i; j < n; j += g) { + t.push(arr[j]); + } + t.sort((a, b) => a - b); + const mid = t[t.length >> 1]; + for (const x of t) { + ans += Math.abs(x - mid); + } + } + return ans; +} + +function gcd(a: number, b: number): number { + if (b === 0) { + return a; + } + return gcd(b, a % b); +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2607.Make K-Subarray Sums Equal/README_EN.md b/solution/2600-2699/2607.Make K-Subarray Sums Equal/README_EN.md index dd41a6806b0dc..c10529fe9008b 100644 --- a/solution/2600-2699/2607.Make K-Subarray Sums Equal/README_EN.md +++ b/solution/2600-2699/2607.Make K-Subarray Sums Equal/README_EN.md @@ -158,6 +158,35 @@ func gcd(a, b int) int { } ``` +### **TypeScript** + +```ts +function makeSubKSumEqual(arr: number[], k: number): number { + const n = arr.length; + const g = gcd(n, k); + let ans = 0; + for (let i = 0; i < g; ++i) { + const t: number[] = []; + for (let j = i; j < n; j += g) { + t.push(arr[j]); + } + t.sort((a, b) => a - b); + const mid = t[t.length >> 1]; + for (const x of t) { + ans += Math.abs(x - mid); + } + } + return ans; +} + +function gcd(a: number, b: number): number { + if (b === 0) { + return a; + } + return gcd(b, a % b); +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2607.Make K-Subarray Sums Equal/Solution.ts b/solution/2600-2699/2607.Make K-Subarray Sums Equal/Solution.ts new file mode 100644 index 0000000000000..9494511ab8689 --- /dev/null +++ b/solution/2600-2699/2607.Make K-Subarray Sums Equal/Solution.ts @@ -0,0 +1,17 @@ +function maximumCostSubstring( + s: string, + chars: string, + vals: number[], +): number { + const d: number[] = Array.from({ length: 26 }, (_, i) => i + 1); + for (let i = 0; i < chars.length; ++i) { + d[chars.charCodeAt(i) - 97] = vals[i]; + } + let ans = 0; + let f = 0; + for (const c of s) { + f = Math.max(f, 0) + d[c.charCodeAt(0) - 97]; + ans = Math.max(ans, f); + } + return ans; +} diff --git a/solution/2600-2699/2612.Minimum Reverse Operations/README.md b/solution/2600-2699/2612.Minimum Reverse Operations/README.md index 8d8627bd948c2..dee50dd3a8799 100644 --- a/solution/2600-2699/2612.Minimum Reverse Operations/README.md +++ b/solution/2600-2699/2612.Minimum Reverse Operations/README.md @@ -255,6 +255,1420 @@ func min(a, b int) int { } ``` +### **TypeScript** + +```ts +function minReverseOperations( + n: number, + p: number, + banned: number[], + k: number, +): number[] { + const ans = new Array(n).fill(-1); + const ts = new Array(2).fill(0).map(() => new TreeSet()); + for (let i = 0; i < n; ++i) { + ts[i % 2].add(i); + } + ans[p] = 0; + ts[p % 2].delete(p); + for (const i of banned) { + ts[i % 2].delete(i); + } + ts[0].add(n); + ts[1].add(n); + let q = [p]; + while (q.length) { + const t: number[] = []; + for (const i of q) { + const mi = Math.max(i - k + 1, k - i - 1); + const mx = Math.min(i + k - 1, n * 2 - k - i - 1); + const s = ts[mi % 2]; + for (let j = s.ceil(mi)!; j <= mx; j = s.ceil(j)!) { + t.push(j); + ans[j] = ans[i] + 1; + s.delete(j); + } + } + q = t; + } + return ans; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} +``` + +```ts +function minReverseOperations( + n: number, + p: number, + banned: number[], + k: number, +): number[] { + const ans = new Array(n).fill(-1); + const ts = new Array(2).fill(0).map(() => new TreapMultiSet()); + for (let i = 0; i < n; ++i) { + ts[i % 2].add(i); + } + ans[p] = 0; + ts[p % 2].delete(p); + for (const i of banned) { + ts[i % 2].delete(i); + } + ts[0].add(n); + ts[1].add(n); + let q = [p]; + while (q.length) { + const t: number[] = []; + for (const i of q) { + const mi = Math.max(i - k + 1, k - i - 1); + const mx = Math.min(i + k - 1, n * 2 - k - i - 1); + const s = ts[mi % 2]; + for (let j = s.ceil(mi)!; j <= mx; j = s.ceil(j)!) { + t.push(j); + ans[j] = ans[i] + 1; + s.delete(j); + } + } + q = t; + } + return ans; +} + +type CompareFunction = ( + a: T, + b: T, +) => R extends 'number' ? number : boolean; + +interface ITreapMultiSet extends Iterable { + add: (...value: T[]) => this; + has: (value: T) => boolean; + delete: (value: T) => void; + + bisectLeft: (value: T) => number; + bisectRight: (value: T) => number; + + indexOf: (value: T) => number; + lastIndexOf: (value: T) => number; + + at: (index: number) => T | undefined; + first: () => T | undefined; + last: () => T | undefined; + + lower: (value: T) => T | undefined; + higher: (value: T) => T | undefined; + floor: (value: T) => T | undefined; + ceil: (value: T) => T | undefined; + + shift: () => T | undefined; + pop: (index?: number) => T | undefined; + + count: (value: T) => number; + + keys: () => IterableIterator; + values: () => IterableIterator; + rvalues: () => IterableIterator; + entries: () => IterableIterator<[number, T]>; + + readonly size: number; +} + +class TreapNode { + value: T; + count: number; + size: number; + priority: number; + left: TreapNode | null; + right: TreapNode | null; + + constructor(value: T) { + this.value = value; + this.count = 1; + this.size = 1; + this.priority = Math.random(); + this.left = null; + this.right = null; + } + + static getSize(node: TreapNode | null): number { + return node?.size ?? 0; + } + + static getFac(node: TreapNode | null): number { + return node?.priority ?? 0; + } + + pushUp(): void { + let tmp = this.count; + tmp += TreapNode.getSize(this.left); + tmp += TreapNode.getSize(this.right); + this.size = tmp; + } + + rotateRight(): TreapNode { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let node: TreapNode = this; + const left = node.left; + node.left = left?.right ?? null; + left && (left.right = node); + left && (node = left); + node.right?.pushUp(); + node.pushUp(); + return node; + } + + rotateLeft(): TreapNode { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let node: TreapNode = this; + const right = node.right; + node.right = right?.left ?? null; + right && (right.left = node); + right && (node = right); + node.left?.pushUp(); + node.pushUp(); + return node; + } +} + +class TreapMultiSet implements ITreapMultiSet { + private readonly root: TreapNode; + private readonly compareFn: CompareFunction; + private readonly leftBound: T; + private readonly rightBound: T; + + /** + * + * @param compareFn A compare function which returns boolean or number + * @param leftBound defalut value is `-Infinity` + * @param rightBound defalut value is `Infinity` + * @description + * create a `MultiSet`, compare elements using `compareFn`, which is increasing order by default. + * @example + * ```ts + * interface Person { + name: string + age: number + } + + const leftBound = { + name: 'Alice', + age: -Infinity, + } + + const rightBound = { + name: 'Bob', + age: Infinity, + } + + const sortedList = new TreapMultiSet( + (a: Person, b: Person) => a.age - b.age, + leftBound, + rightBound + ) + * ``` + */ + constructor(compareFn?: CompareFunction); + constructor( + compareFn: CompareFunction, + leftBound: T, + rightBound: T, + ); + constructor( + compareFn: CompareFunction = (a: any, b: any) => a - b, + leftBound: any = -Infinity, + rightBound: any = Infinity, + ) { + this.root = new TreapNode(rightBound); + this.root.priority = Infinity; + this.root.left = new TreapNode(leftBound); + this.root.left.priority = -Infinity; + this.root.pushUp(); + + this.leftBound = leftBound; + this.rightBound = rightBound; + this.compareFn = compareFn; + } + + get size(): number { + return this.root.size - 2; + } + + get height(): number { + const getHeight = (node: TreapNode | null): number => { + if (node == null) return 0; + return 1 + Math.max(getHeight(node.left), getHeight(node.right)); + }; + + return getHeight(this.root); + } + + /** + * + * @complexity `O(logn)` + * @description Returns true if value is a member. + */ + has(value: T): boolean { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): boolean => { + if (node == null) return false; + if (compare(node.value, value) === 0) return true; + if (compare(node.value, value) < 0) return dfs(node.right, value); + return dfs(node.left, value); + }; + + return dfs(this.root, value); + } + + /** + * + * @complexity `O(logn)` + * @description Add value to sorted set. + */ + add(...values: T[]): this { + const compare = this.compareFn; + const dfs = ( + node: TreapNode | null, + value: T, + parent: TreapNode, + direction: 'left' | 'right', + ): void => { + if (node == null) return; + if (compare(node.value, value) === 0) { + node.count++; + node.pushUp(); + } else if (compare(node.value, value) > 0) { + if (node.left) { + dfs(node.left, value, node, 'left'); + } else { + node.left = new TreapNode(value); + node.pushUp(); + } + + if (TreapNode.getFac(node.left) > node.priority) { + parent[direction] = node.rotateRight(); + } + } else if (compare(node.value, value) < 0) { + if (node.right) { + dfs(node.right, value, node, 'right'); + } else { + node.right = new TreapNode(value); + node.pushUp(); + } + + if (TreapNode.getFac(node.right) > node.priority) { + parent[direction] = node.rotateLeft(); + } + } + parent.pushUp(); + }; + + values.forEach(value => dfs(this.root.left, value, this.root, 'left')); + return this; + } + + /** + * + * @complexity `O(logn)` + * @description Remove value from sorted set if it is a member. + * If value is not a member, do nothing. + */ + delete(value: T): void { + const compare = this.compareFn; + const dfs = ( + node: TreapNode | null, + value: T, + parent: TreapNode, + direction: 'left' | 'right', + ): void => { + if (node == null) return; + + if (compare(node.value, value) === 0) { + if (node.count > 1) { + node.count--; + node?.pushUp(); + } else if (node.left == null && node.right == null) { + parent[direction] = null; + } else { + // 旋到根节点 + if ( + node.right == null || + TreapNode.getFac(node.left) > + TreapNode.getFac(node.right) + ) { + parent[direction] = node.rotateRight(); + dfs( + parent[direction]?.right ?? null, + value, + parent[direction]!, + 'right', + ); + } else { + parent[direction] = node.rotateLeft(); + dfs( + parent[direction]?.left ?? null, + value, + parent[direction]!, + 'left', + ); + } + } + } else if (compare(node.value, value) > 0) { + dfs(node.left, value, node, 'left'); + } else if (compare(node.value, value) < 0) { + dfs(node.right, value, node, 'right'); + } + + parent?.pushUp(); + }; + + dfs(this.root.left, value, this.root, 'left'); + } + + /** + * + * @complexity `O(logn)` + * @description Returns an index to insert value in the sorted set. + * If the value is already present, the insertion point will be before (to the left of) any existing values. + */ + bisectLeft(value: T): number { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + return TreapNode.getSize(node.left); + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + + return dfs(this.root, value) - 1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns an index to insert value in the sorted set. + * If the value is already present, the insertion point will be before (to the right of) any existing values. + */ + bisectRight(value: T): number { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + return TreapNode.getSize(node.left) + node.count; + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + return dfs(this.root, value) - 1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns the index of the first occurrence of a value in the set, or -1 if it is not present. + */ + indexOf(value: T): number { + const compare = this.compareFn; + let isExist = false; + + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + isExist = true; + return TreapNode.getSize(node.left); + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + const res = dfs(this.root, value) - 1; + return isExist ? res : -1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns the index of the last occurrence of a value in the set, or -1 if it is not present. + */ + lastIndexOf(value: T): number { + const compare = this.compareFn; + let isExist = false; + + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + isExist = true; + return TreapNode.getSize(node.left) + node.count - 1; + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + + const res = dfs(this.root, value) - 1; + return isExist ? res : -1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns the item located at the specified index. + * @param index The zero-based index of the desired code unit. A negative index will count back from the last item. + */ + at(index: number): T | undefined { + if (index < 0) index += this.size; + if (index < 0 || index >= this.size) return undefined; + + const dfs = ( + node: TreapNode | null, + rank: number, + ): T | undefined => { + if (node == null) return undefined; + + if (TreapNode.getSize(node.left) >= rank) { + return dfs(node.left, rank); + } else if (TreapNode.getSize(node.left) + node.count >= rank) { + return node.value; + } else { + return dfs( + node.right, + rank - TreapNode.getSize(node.left) - node.count, + ); + } + }; + + const res = dfs(this.root, index + 2); + return ([this.leftBound, this.rightBound] as any[]).includes(res) + ? undefined + : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element less than `val`, return `undefined` if no such element found. + */ + lower(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) >= 0) return dfs(node.left, value); + + const tmp = dfs(node.right, value); + if (tmp == null || compare(node.value, tmp) > 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.leftBound ? undefined : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element greater than `val`, return `undefined` if no such element found. + */ + higher(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) <= 0) return dfs(node.right, value); + + const tmp = dfs(node.left, value); + + if (tmp == null || compare(node.value, tmp) < 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.rightBound ? undefined : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element less than or equal to `val`, return `undefined` if no such element found. + */ + floor(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) === 0) return node.value; + if (compare(node.value, value) >= 0) return dfs(node.left, value); + + const tmp = dfs(node.right, value); + if (tmp == null || compare(node.value, tmp) > 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.leftBound ? undefined : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element greater than or equal to `val`, return `undefined` if no such element found. + */ + ceil(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) === 0) return node.value; + if (compare(node.value, value) <= 0) return dfs(node.right, value); + + const tmp = dfs(node.left, value); + + if (tmp == null || compare(node.value, tmp) < 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.rightBound ? undefined : res; + } + + /** + * @complexity `O(logn)` + * @description + * Returns the last element from set. + * If the set is empty, undefined is returned. + */ + first(): T | undefined { + const iter = this.inOrder(); + iter.next(); + const res = iter.next().value; + return res === this.rightBound ? undefined : res; + } + + /** + * @complexity `O(logn)` + * @description + * Returns the last element from set. + * If the set is empty, undefined is returned . + */ + last(): T | undefined { + const iter = this.reverseInOrder(); + iter.next(); + const res = iter.next().value; + return res === this.leftBound ? undefined : res; + } + + /** + * @complexity `O(logn)` + * @description + * Removes the first element from an set and returns it. + * If the set is empty, undefined is returned and the set is not modified. + */ + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + /** + * @complexity `O(logn)` + * @description + * Removes the last element from an set and returns it. + * If the set is empty, undefined is returned and the set is not modified. + */ + pop(index?: number): T | undefined { + if (index == null) { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + const toDelete = this.at(index); + if (toDelete == null) return; + this.delete(toDelete); + return toDelete; + } + + /** + * + * @complexity `O(logn)` + * @description + * Returns number of occurrences of value in the sorted set. + */ + count(value: T): number { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + if (compare(node.value, value) === 0) return node.count; + if (compare(node.value, value) < 0) return dfs(node.right, value); + return dfs(node.left, value); + }; + + return dfs(this.root, value); + } + + *[Symbol.iterator](): Generator { + yield* this.values(); + } + + /** + * @description + * Returns an iterable of keys in the set. + */ + *keys(): Generator { + yield* this.values(); + } + + /** + * @description + * Returns an iterable of values in the set. + */ + *values(): Generator { + const iter = this.inOrder(); + iter.next(); + const steps = this.size; + for (let _ = 0; _ < steps; _++) { + yield iter.next().value; + } + } + + /** + * @description + * Returns a generator for reversed order traversing the set. + */ + *rvalues(): Generator { + const iter = this.reverseInOrder(); + iter.next(); + const steps = this.size; + for (let _ = 0; _ < steps; _++) { + yield iter.next().value; + } + } + + /** + * @description + * Returns an iterable of key, value pairs for every entry in the set. + */ + *entries(): IterableIterator<[number, T]> { + const iter = this.inOrder(); + iter.next(); + const steps = this.size; + for (let i = 0; i < steps; i++) { + yield [i, iter.next().value]; + } + } + + private *inOrder( + root: TreapNode | null = this.root, + ): Generator { + if (root == null) return; + yield* this.inOrder(root.left); + const count = root.count; + for (let _ = 0; _ < count; _++) { + yield root.value; + } + yield* this.inOrder(root.right); + } + + private *reverseInOrder( + root: TreapNode | null = this.root, + ): Generator { + if (root == null) return; + yield* this.reverseInOrder(root.right); + const count = root.count; + for (let _ = 0; _ < count; _++) { + yield root.value; + } + yield* this.reverseInOrder(root.left); + } +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2612.Minimum Reverse Operations/README_EN.md b/solution/2600-2699/2612.Minimum Reverse Operations/README_EN.md index 30119e0bf4c29..62ca91ddd28b4 100644 --- a/solution/2600-2699/2612.Minimum Reverse Operations/README_EN.md +++ b/solution/2600-2699/2612.Minimum Reverse Operations/README_EN.md @@ -217,6 +217,1420 @@ func min(a, b int) int { } ``` +### **TypeScript** + +```ts +function minReverseOperations( + n: number, + p: number, + banned: number[], + k: number, +): number[] { + const ans = new Array(n).fill(-1); + const ts = new Array(2).fill(0).map(() => new TreeSet()); + for (let i = 0; i < n; ++i) { + ts[i % 2].add(i); + } + ans[p] = 0; + ts[p % 2].delete(p); + for (const i of banned) { + ts[i % 2].delete(i); + } + ts[0].add(n); + ts[1].add(n); + let q = [p]; + while (q.length) { + const t: number[] = []; + for (const i of q) { + const mi = Math.max(i - k + 1, k - i - 1); + const mx = Math.min(i + k - 1, n * 2 - k - i - 1); + const s = ts[mi % 2]; + for (let j = s.ceil(mi)!; j <= mx; j = s.ceil(j)!) { + t.push(j); + ans[j] = ans[i] + 1; + s.delete(j); + } + } + q = t; + } + return ans; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +} +``` + +```ts +function minReverseOperations( + n: number, + p: number, + banned: number[], + k: number, +): number[] { + const ans = new Array(n).fill(-1); + const ts = new Array(2).fill(0).map(() => new TreapMultiSet()); + for (let i = 0; i < n; ++i) { + ts[i % 2].add(i); + } + ans[p] = 0; + ts[p % 2].delete(p); + for (const i of banned) { + ts[i % 2].delete(i); + } + ts[0].add(n); + ts[1].add(n); + let q = [p]; + while (q.length) { + const t: number[] = []; + for (const i of q) { + const mi = Math.max(i - k + 1, k - i - 1); + const mx = Math.min(i + k - 1, n * 2 - k - i - 1); + const s = ts[mi % 2]; + for (let j = s.ceil(mi)!; j <= mx; j = s.ceil(j)!) { + t.push(j); + ans[j] = ans[i] + 1; + s.delete(j); + } + } + q = t; + } + return ans; +} + +type CompareFunction = ( + a: T, + b: T, +) => R extends 'number' ? number : boolean; + +interface ITreapMultiSet extends Iterable { + add: (...value: T[]) => this; + has: (value: T) => boolean; + delete: (value: T) => void; + + bisectLeft: (value: T) => number; + bisectRight: (value: T) => number; + + indexOf: (value: T) => number; + lastIndexOf: (value: T) => number; + + at: (index: number) => T | undefined; + first: () => T | undefined; + last: () => T | undefined; + + lower: (value: T) => T | undefined; + higher: (value: T) => T | undefined; + floor: (value: T) => T | undefined; + ceil: (value: T) => T | undefined; + + shift: () => T | undefined; + pop: (index?: number) => T | undefined; + + count: (value: T) => number; + + keys: () => IterableIterator; + values: () => IterableIterator; + rvalues: () => IterableIterator; + entries: () => IterableIterator<[number, T]>; + + readonly size: number; +} + +class TreapNode { + value: T; + count: number; + size: number; + priority: number; + left: TreapNode | null; + right: TreapNode | null; + + constructor(value: T) { + this.value = value; + this.count = 1; + this.size = 1; + this.priority = Math.random(); + this.left = null; + this.right = null; + } + + static getSize(node: TreapNode | null): number { + return node?.size ?? 0; + } + + static getFac(node: TreapNode | null): number { + return node?.priority ?? 0; + } + + pushUp(): void { + let tmp = this.count; + tmp += TreapNode.getSize(this.left); + tmp += TreapNode.getSize(this.right); + this.size = tmp; + } + + rotateRight(): TreapNode { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let node: TreapNode = this; + const left = node.left; + node.left = left?.right ?? null; + left && (left.right = node); + left && (node = left); + node.right?.pushUp(); + node.pushUp(); + return node; + } + + rotateLeft(): TreapNode { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let node: TreapNode = this; + const right = node.right; + node.right = right?.left ?? null; + right && (right.left = node); + right && (node = right); + node.left?.pushUp(); + node.pushUp(); + return node; + } +} + +class TreapMultiSet implements ITreapMultiSet { + private readonly root: TreapNode; + private readonly compareFn: CompareFunction; + private readonly leftBound: T; + private readonly rightBound: T; + + /** + * + * @param compareFn A compare function which returns boolean or number + * @param leftBound defalut value is `-Infinity` + * @param rightBound defalut value is `Infinity` + * @description + * create a `MultiSet`, compare elements using `compareFn`, which is increasing order by default. + * @example + * ```ts + * interface Person { + name: string + age: number + } + + const leftBound = { + name: 'Alice', + age: -Infinity, + } + + const rightBound = { + name: 'Bob', + age: Infinity, + } + + const sortedList = new TreapMultiSet( + (a: Person, b: Person) => a.age - b.age, + leftBound, + rightBound + ) + * ``` + */ + constructor(compareFn?: CompareFunction); + constructor( + compareFn: CompareFunction, + leftBound: T, + rightBound: T, + ); + constructor( + compareFn: CompareFunction = (a: any, b: any) => a - b, + leftBound: any = -Infinity, + rightBound: any = Infinity, + ) { + this.root = new TreapNode(rightBound); + this.root.priority = Infinity; + this.root.left = new TreapNode(leftBound); + this.root.left.priority = -Infinity; + this.root.pushUp(); + + this.leftBound = leftBound; + this.rightBound = rightBound; + this.compareFn = compareFn; + } + + get size(): number { + return this.root.size - 2; + } + + get height(): number { + const getHeight = (node: TreapNode | null): number => { + if (node == null) return 0; + return 1 + Math.max(getHeight(node.left), getHeight(node.right)); + }; + + return getHeight(this.root); + } + + /** + * + * @complexity `O(logn)` + * @description Returns true if value is a member. + */ + has(value: T): boolean { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): boolean => { + if (node == null) return false; + if (compare(node.value, value) === 0) return true; + if (compare(node.value, value) < 0) return dfs(node.right, value); + return dfs(node.left, value); + }; + + return dfs(this.root, value); + } + + /** + * + * @complexity `O(logn)` + * @description Add value to sorted set. + */ + add(...values: T[]): this { + const compare = this.compareFn; + const dfs = ( + node: TreapNode | null, + value: T, + parent: TreapNode, + direction: 'left' | 'right', + ): void => { + if (node == null) return; + if (compare(node.value, value) === 0) { + node.count++; + node.pushUp(); + } else if (compare(node.value, value) > 0) { + if (node.left) { + dfs(node.left, value, node, 'left'); + } else { + node.left = new TreapNode(value); + node.pushUp(); + } + + if (TreapNode.getFac(node.left) > node.priority) { + parent[direction] = node.rotateRight(); + } + } else if (compare(node.value, value) < 0) { + if (node.right) { + dfs(node.right, value, node, 'right'); + } else { + node.right = new TreapNode(value); + node.pushUp(); + } + + if (TreapNode.getFac(node.right) > node.priority) { + parent[direction] = node.rotateLeft(); + } + } + parent.pushUp(); + }; + + values.forEach(value => dfs(this.root.left, value, this.root, 'left')); + return this; + } + + /** + * + * @complexity `O(logn)` + * @description Remove value from sorted set if it is a member. + * If value is not a member, do nothing. + */ + delete(value: T): void { + const compare = this.compareFn; + const dfs = ( + node: TreapNode | null, + value: T, + parent: TreapNode, + direction: 'left' | 'right', + ): void => { + if (node == null) return; + + if (compare(node.value, value) === 0) { + if (node.count > 1) { + node.count--; + node?.pushUp(); + } else if (node.left == null && node.right == null) { + parent[direction] = null; + } else { + // 旋到根节点 + if ( + node.right == null || + TreapNode.getFac(node.left) > + TreapNode.getFac(node.right) + ) { + parent[direction] = node.rotateRight(); + dfs( + parent[direction]?.right ?? null, + value, + parent[direction]!, + 'right', + ); + } else { + parent[direction] = node.rotateLeft(); + dfs( + parent[direction]?.left ?? null, + value, + parent[direction]!, + 'left', + ); + } + } + } else if (compare(node.value, value) > 0) { + dfs(node.left, value, node, 'left'); + } else if (compare(node.value, value) < 0) { + dfs(node.right, value, node, 'right'); + } + + parent?.pushUp(); + }; + + dfs(this.root.left, value, this.root, 'left'); + } + + /** + * + * @complexity `O(logn)` + * @description Returns an index to insert value in the sorted set. + * If the value is already present, the insertion point will be before (to the left of) any existing values. + */ + bisectLeft(value: T): number { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + return TreapNode.getSize(node.left); + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + + return dfs(this.root, value) - 1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns an index to insert value in the sorted set. + * If the value is already present, the insertion point will be before (to the right of) any existing values. + */ + bisectRight(value: T): number { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + return TreapNode.getSize(node.left) + node.count; + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + return dfs(this.root, value) - 1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns the index of the first occurrence of a value in the set, or -1 if it is not present. + */ + indexOf(value: T): number { + const compare = this.compareFn; + let isExist = false; + + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + isExist = true; + return TreapNode.getSize(node.left); + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + const res = dfs(this.root, value) - 1; + return isExist ? res : -1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns the index of the last occurrence of a value in the set, or -1 if it is not present. + */ + lastIndexOf(value: T): number { + const compare = this.compareFn; + let isExist = false; + + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + + if (compare(node.value, value) === 0) { + isExist = true; + return TreapNode.getSize(node.left) + node.count - 1; + } else if (compare(node.value, value) > 0) { + return dfs(node.left, value); + } else if (compare(node.value, value) < 0) { + return ( + dfs(node.right, value) + + TreapNode.getSize(node.left) + + node.count + ); + } + + return 0; + }; + + const res = dfs(this.root, value) - 1; + return isExist ? res : -1; + } + + /** + * + * @complexity `O(logn)` + * @description Returns the item located at the specified index. + * @param index The zero-based index of the desired code unit. A negative index will count back from the last item. + */ + at(index: number): T | undefined { + if (index < 0) index += this.size; + if (index < 0 || index >= this.size) return undefined; + + const dfs = ( + node: TreapNode | null, + rank: number, + ): T | undefined => { + if (node == null) return undefined; + + if (TreapNode.getSize(node.left) >= rank) { + return dfs(node.left, rank); + } else if (TreapNode.getSize(node.left) + node.count >= rank) { + return node.value; + } else { + return dfs( + node.right, + rank - TreapNode.getSize(node.left) - node.count, + ); + } + }; + + const res = dfs(this.root, index + 2); + return ([this.leftBound, this.rightBound] as any[]).includes(res) + ? undefined + : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element less than `val`, return `undefined` if no such element found. + */ + lower(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) >= 0) return dfs(node.left, value); + + const tmp = dfs(node.right, value); + if (tmp == null || compare(node.value, tmp) > 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.leftBound ? undefined : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element greater than `val`, return `undefined` if no such element found. + */ + higher(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) <= 0) return dfs(node.right, value); + + const tmp = dfs(node.left, value); + + if (tmp == null || compare(node.value, tmp) < 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.rightBound ? undefined : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element less than or equal to `val`, return `undefined` if no such element found. + */ + floor(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) === 0) return node.value; + if (compare(node.value, value) >= 0) return dfs(node.left, value); + + const tmp = dfs(node.right, value); + if (tmp == null || compare(node.value, tmp) > 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.leftBound ? undefined : res; + } + + /** + * + * @complexity `O(logn)` + * @description Find and return the element greater than or equal to `val`, return `undefined` if no such element found. + */ + ceil(value: T): T | undefined { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): T | undefined => { + if (node == null) return undefined; + if (compare(node.value, value) === 0) return node.value; + if (compare(node.value, value) <= 0) return dfs(node.right, value); + + const tmp = dfs(node.left, value); + + if (tmp == null || compare(node.value, tmp) < 0) { + return node.value; + } else { + return tmp; + } + }; + + const res = dfs(this.root, value) as any; + return res === this.rightBound ? undefined : res; + } + + /** + * @complexity `O(logn)` + * @description + * Returns the last element from set. + * If the set is empty, undefined is returned. + */ + first(): T | undefined { + const iter = this.inOrder(); + iter.next(); + const res = iter.next().value; + return res === this.rightBound ? undefined : res; + } + + /** + * @complexity `O(logn)` + * @description + * Returns the last element from set. + * If the set is empty, undefined is returned . + */ + last(): T | undefined { + const iter = this.reverseInOrder(); + iter.next(); + const res = iter.next().value; + return res === this.leftBound ? undefined : res; + } + + /** + * @complexity `O(logn)` + * @description + * Removes the first element from an set and returns it. + * If the set is empty, undefined is returned and the set is not modified. + */ + shift(): T | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first); + return first; + } + + /** + * @complexity `O(logn)` + * @description + * Removes the last element from an set and returns it. + * If the set is empty, undefined is returned and the set is not modified. + */ + pop(index?: number): T | undefined { + if (index == null) { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last); + return last; + } + + const toDelete = this.at(index); + if (toDelete == null) return; + this.delete(toDelete); + return toDelete; + } + + /** + * + * @complexity `O(logn)` + * @description + * Returns number of occurrences of value in the sorted set. + */ + count(value: T): number { + const compare = this.compareFn; + const dfs = (node: TreapNode | null, value: T): number => { + if (node == null) return 0; + if (compare(node.value, value) === 0) return node.count; + if (compare(node.value, value) < 0) return dfs(node.right, value); + return dfs(node.left, value); + }; + + return dfs(this.root, value); + } + + *[Symbol.iterator](): Generator { + yield* this.values(); + } + + /** + * @description + * Returns an iterable of keys in the set. + */ + *keys(): Generator { + yield* this.values(); + } + + /** + * @description + * Returns an iterable of values in the set. + */ + *values(): Generator { + const iter = this.inOrder(); + iter.next(); + const steps = this.size; + for (let _ = 0; _ < steps; _++) { + yield iter.next().value; + } + } + + /** + * @description + * Returns a generator for reversed order traversing the set. + */ + *rvalues(): Generator { + const iter = this.reverseInOrder(); + iter.next(); + const steps = this.size; + for (let _ = 0; _ < steps; _++) { + yield iter.next().value; + } + } + + /** + * @description + * Returns an iterable of key, value pairs for every entry in the set. + */ + *entries(): IterableIterator<[number, T]> { + const iter = this.inOrder(); + iter.next(); + const steps = this.size; + for (let i = 0; i < steps; i++) { + yield [i, iter.next().value]; + } + } + + private *inOrder( + root: TreapNode | null = this.root, + ): Generator { + if (root == null) return; + yield* this.inOrder(root.left); + const count = root.count; + for (let _ = 0; _ < count; _++) { + yield root.value; + } + yield* this.inOrder(root.right); + } + + private *reverseInOrder( + root: TreapNode | null = this.root, + ): Generator { + if (root == null) return; + yield* this.reverseInOrder(root.right); + const count = root.count; + for (let _ = 0; _ < count; _++) { + yield root.value; + } + yield* this.reverseInOrder(root.left); + } +} +``` + ### **...** ``` diff --git a/solution/2600-2699/2612.Minimum Reverse Operations/Solution.ts b/solution/2600-2699/2612.Minimum Reverse Operations/Solution.ts new file mode 100644 index 0000000000000..e16fa38ad9c26 --- /dev/null +++ b/solution/2600-2699/2612.Minimum Reverse Operations/Solution.ts @@ -0,0 +1,679 @@ +function minReverseOperations( + n: number, + p: number, + banned: number[], + k: number, +): number[] { + const ans = new Array(n).fill(-1); + const ts = new Array(2).fill(0).map(() => new TreeSet()); + for (let i = 0; i < n; ++i) { + ts[i % 2].add(i); + } + ans[p] = 0; + ts[p % 2].delete(p); + for (const i of banned) { + ts[i % 2].delete(i); + } + ts[0].add(n); + ts[1].add(n); + let q = [p]; + while (q.length) { + const t: number[] = []; + for (const i of q) { + const mi = Math.max(i - k + 1, k - i - 1); + const mx = Math.min(i + k - 1, n * 2 - k - i - 1); + const s = ts[mi % 2]; + for (let j = s.ceil(mi)!; j <= mx; j = s.ceil(j)!) { + t.push(j); + ans[j] = ans[i] + 1; + s.delete(j); + } + } + q = t; + } + return ans; +} + +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; + } +} + +class TreeMultiSet { + _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++; + return successful; + } + + delete(val: T): boolean { + const successful = this.tree.delete(val); + if (!successful) return false; + this._size--; + return true; + } + + count(val: T): number { + const node = this.tree.find(val); + return node ? node.count : 0; + } + + 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 { + yield* this.values(); + } + + *keys(): Generator { + for (const val of this.values()) yield val; + } + + *values(): Generator { + for (const val of this.tree.inOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } + + /** + * Return a generator for reverse order traversing the multi-set + */ + *rvalues(): Generator { + for (const val of this.tree.reverseInOrder()) { + let count = this.count(val); + while (count--) yield val; + } + return undefined; + } +}