diff --git a/solution/0700-0799/0729.My Calendar I/README.md b/solution/0700-0799/0729.My Calendar I/README.md index 6c485255a0fb7..0d1bf730d0f95 100644 --- a/solution/0700-0799/0729.My Calendar I/README.md +++ b/solution/0700-0799/0729.My Calendar I/README.md @@ -65,7 +65,13 @@ myCalendar.book(20, 30); // return True ,这个日程安排可以添加到日 -### 方法一 +### 方法一:有序集合 + +我们可以使用有序集合来存储日程安排,有序集合可以在 $O(\log n)$ 的时间内完成插入、删除、查找操作。有序集合中的元素,按照日程安排的 $\textit{endTime}$ 从小到大排序。 + +调用 $\text{book}(start, end)$ 方法时,我们在有序集合中查找第一个结束时间大于 $\textit{start}$ 的日程安排,如果存在并且其开始时间小于 $\textit{end}$,则说明存在重复预订,返回 $\text{false}$;否则,将 $\textit{end}$ 作为键,将 $\textit{start}$ 作为值插入有序集合中,返回 $\text{true}$。 + +时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 为日程安排的数量。 @@ -76,12 +82,13 @@ from sortedcontainers import SortedDict class MyCalendar: + def __init__(self): self.sd = SortedDict() def book(self, start: int, end: int) -> bool: idx = self.sd.bisect_right(start) - if idx < len(self.sd) and end > self.sd.values()[idx]: + if idx < len(self.sd) and self.sd.values()[idx] < end: return False self.sd[end] = start return True @@ -95,33 +102,26 @@ class MyCalendar: #### Java ```java -import java.util.Map; -import java.util.TreeMap; - class MyCalendar { - private final TreeMap tm = new TreeMap<>(); public MyCalendar() { } - public boolean book(int start, int end) { - Map.Entry ent = tm.floorEntry(start); - if (ent != null && ent.getValue() > start) { - return false; - } - ent = tm.ceilingEntry(start); - if (ent != null && ent.getKey() < end) { + public boolean book(int startTime, int endTime) { + var e = tm.ceilingEntry(startTime + 1); + if (e != null && e.getValue() < endTime) { return false; } - tm.put(start, end); + tm.put(endTime, startTime); return true; } } /** - * Your MyCalendar object will be instantiated and called as such: MyCalendar - * obj = new MyCalendar(); boolean param_1 = obj.book(start,end); + * Your MyCalendar object will be instantiated and called as such: + * MyCalendar obj = new MyCalendar(); + * boolean param_1 = obj.book(startTime,endTime); */ ``` @@ -130,31 +130,26 @@ class MyCalendar { ```cpp class MyCalendar { public: - map m; - MyCalendar() { } - bool book(int start, int end) { - ++m[start]; - --m[end]; - int s = 0; - for (auto& [k, v] : m) { - s += v; - if (s > 1) { - --m[start]; - ++m[end]; - return false; - } + bool book(int startTime, int endTime) { + auto e = m.lower_bound(startTime + 1); + if (e != m.end() && e->second < endTime) { + return false; } + m[endTime] = startTime; return true; } + +private: + map m; }; /** * Your MyCalendar object will be instantiated and called as such: * MyCalendar* obj = new MyCalendar(); - * bool param_1 = obj->book(start,end); + * bool param_1 = obj->book(startTime,endTime); */ ``` @@ -171,21 +166,18 @@ func Constructor() MyCalendar { } } -func (this *MyCalendar) Book(start int, end int) bool { - if p, ok := this.rbt.Floor(start); ok && p.Value.(int) > start { - return false - } - if p, ok := this.rbt.Ceiling(start); ok && p.Key.(int) < end { +func (this *MyCalendar) Book(startTime int, endTime int) bool { + if p, ok := this.rbt.Ceiling(startTime + 1); ok && p.Value.(int) < endTime { return false } - this.rbt.Put(start, end) + this.rbt.Put(endTime, startTime) return true } /** * Your MyCalendar object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); + * param_1 := obj.Book(startTime,endTime); */ ``` @@ -193,28 +185,509 @@ func (this *MyCalendar) Book(start int, end int) bool { ```ts class MyCalendar { - private calendar: number[][]; - + tm: TreeMap; constructor() { - this.calendar = []; + this.tm = new TreeMap(); + } + + book(startTime: number, endTime: number): boolean { + const e = this.tm.higher(startTime); + if (e && e[1] < endTime) { + return false; + } + this.tm.set(endTime, startTime); + return true; + } +} + +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; } - book(start: number, end: number): boolean { - for (const item of this.calendar) { - if (end <= item[0] || item[1] <= start) { - continue; + 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; } - this.calendar.push([start, end]); + node.parent = parent; + this.fixAfterInsert(node); return true; } + + search(predicate: (val: T) => boolean, direction: 'left' | 'right'): T | undefined { + let p = this.root; + let result = null; + while (p) { + if (predicate(p.data)) { + result = p; + p = p[direction]; + } else { + p = p[direction === 'left' ? 'right' : 'left']; + } + } + return result?.data; + } + + 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; + } + + count(data: T): number { + const node = this.find(data); + return node ? node.count : 0; + } + + *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 TreeMap { + _size: number; + tree: RBTree; + map: Map = new Map(); + compare: Compare; + constructor( + collection: Array<[K, V]> | Compare = [], + compare: Compare = (l: K, r: K) => (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 [key, val] of collection) this.set(key, val); + } + + size(): number { + return this._size; + } + + has(key: K): boolean { + return !!this.tree.find(key); + } + + get(key: K): V | undefined { + return this.map.get(key); + } + + set(key: K, val: V): boolean { + const successful = this.tree.insert(key); + this._size += successful ? 1 : 0; + this.map.set(key, val); + return successful; + } + + delete(key: K): boolean { + const deleted = this.tree.deleteAll(key); + this._size -= deleted ? 1 : 0; + return deleted; + } + + ceil(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left')); + } + + floor(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right')); + } + + higher(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left')); + } + + lower(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right')); + } + + first(): [K, V] | undefined { + return this.toKeyValue(this.tree.inOrder().next().value); + } + + last(): [K, V] | undefined { + return this.toKeyValue(this.tree.reverseInOrder().next().value); + } + + shift(): [K, V] | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first[0]); + return first; + } + + pop(): [K, V] | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last[0]); + return last; + } + + toKeyValue(key: K): [K, V]; + toKeyValue(key: undefined): undefined; + toKeyValue(key: K | undefined): [K, V] | undefined; + toKeyValue(key: K | undefined): [K, V] | undefined { + return key != null ? [key, this.map.get(key)!] : undefined; + } + + *[Symbol.iterator](): Generator<[K, V], void, void> { + for (const key of this.keys()) yield this.toKeyValue(key); + } + + *keys(): Generator { + for (const key of this.tree.inOrder()) yield key; + } + + *values(): Generator { + for (const key of this.keys()) yield this.map.get(key)!; + return undefined; + } + + *rkeys(): Generator { + for (const key of this.tree.reverseInOrder()) yield key; + return undefined; + } + + *rvalues(): Generator { + for (const key of this.rkeys()) yield this.map.get(key)!; + return undefined; + } } /** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` @@ -224,33 +697,23 @@ class MyCalendar { use std::collections::BTreeMap; struct MyCalendar { - bt: BTreeMap, + tm: BTreeMap, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MyCalendar { fn new() -> Self { MyCalendar { - bt: BTreeMap::new(), + tm: BTreeMap::new(), } } - fn book(&mut self, start: i32, end: i32) -> bool { - if let Some((_, &val)) = self.bt.range(..=start).last() { - println!("{} {} {}", start, end, val); - if val > start { - return false; - } - } - if let Some((&key, _)) = self.bt.range(start..).next() { - if key < end { + fn book(&mut self, start_time: i32, end_time: i32) -> bool { + if let Some((&key, &value)) = self.tm.range(start_time + 1..).next() { + if value < end_time { return false; } } - self.bt.insert(start, end); + self.tm.insert(end_time, start_time); true } } @@ -260,29 +723,389 @@ impl MyCalendar { ```js var MyCalendar = function () { - this.calendar = []; + this.tm = new TreeMap(); }; /** - * @param {number} start - * @param {number} end + * @param {number} startTime + * @param {number} endTime * @return {boolean} */ -MyCalendar.prototype.book = function (start, end) { - for (const item of this.calendar) { - if (end <= item[0] || item[1] <= start) { - continue; - } +MyCalendar.prototype.book = function (startTime, endTime) { + const e = this.tm.higher(startTime); + if (e && e[1] < endTime) { return false; } - this.calendar.push([start, end]); + this.tm.set(endTime, startTime); return true; }; +var RBTreeNode = class { + constructor(data) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + sibling() { + if (!this.parent) return null; + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + isOnLeft() { + return this === this.parent.left; + } + hasRedChild() { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +}; +var RBTree = class { + constructor(compare = (l, r) => (l < r ? -1 : l > r ? 1 : 0)) { + this.root = null; + this.lt = (l, r) => compare(l, r) < 0; + } + rotateLeft(pt) { + 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) { + 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, p2) { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + swapData(p1, p2) { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + fixAfterInsert(pt) { + var _a; + let parent = null; + let grandParent = null; + while ( + pt !== this.root && + pt.color !== 1 && + ((_a = pt.parent) == null ? void 0 : _a.color) === 0 + ) { + parent = pt.parent; + grandParent = pt.parent.parent; + if (parent === (grandParent == null ? void 0 : grandParent.left)) { + const uncle = grandParent.right; + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + this.rotateRight(grandParent); + this.swapColor(parent, grandParent); + pt = parent; + } + } else { + const uncle = grandParent.left; + if (uncle != null && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + this.rotateLeft(grandParent); + this.swapColor(parent, grandParent); + pt = parent; + } + } + } + this.root.color = 1; + } + delete(val) { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + deleteAll(val) { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + deleteNode(v) { + const u = BSTreplace(v); + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent; + if (!u) { + if (v === this.root) this.root = null; + else { + if (uvBlack) { + this.fixDoubleBlack(v); + } else { + if (v.sibling()) { + v.sibling().color = 0; + } + } + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + if (!v.left || !v.right) { + if (v === this.root) { + v.data = u.data; + v.left = v.right = null; + } else { + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + else u.color = 1; + } + return; + } + this.swapData(u, v); + this.deleteNode(u); + function BSTreplace(x) { + var _a; + if (x.left && x.right) return successor(x.right); + if (!x.left && !x.right) return null; + return (_a = x.left) != null ? _a : x.right; + } + function successor(x) { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + fixDoubleBlack(x) { + if (x === this.root) return; + const sibling = x.sibling(); + const parent = x.parent; + if (!sibling) { + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + else this.rotateLeft(parent); + this.fixDoubleBlack(x); + } else { + if (sibling.hasRedChild()) { + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + sibling.right.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + sibling.right.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + insert(data) { + 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; + } + 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; + } + search(predicate, direction) { + let p = this.root; + let result = null; + while (p) { + if (predicate(p.data)) { + result = p; + p = p[direction]; + } else { + p = p[direction === 'left' ? 'right' : 'left']; + } + } + return result == null ? void 0 : result.data; + } + find(data) { + 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 ? p : null; + } + count(data) { + const node = this.find(data); + return node ? node.count : 0; + } + *inOrder(root = this.root) { + 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 = this.root) { + 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; + } +}; + +// src/treemap.ts +var TreeMap = class { + constructor(collection = [], compare = (l, r) => (l < r ? -1 : l > r ? 1 : 0)) { + this.map = new Map(); + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const [key, val] of collection) this.set(key, val); + } + size() { + return this._size; + } + has(key) { + return !!this.tree.find(key); + } + get(key) { + return this.map.get(key); + } + set(key, val) { + const successful = this.tree.insert(key); + this._size += successful ? 1 : 0; + this.map.set(key, val); + return successful; + } + delete(key) { + const deleted = this.tree.deleteAll(key); + this._size -= deleted ? 1 : 0; + return deleted; + } + ceil(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left')); + } + floor(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right')); + } + higher(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left')); + } + lower(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right')); + } + first() { + return this.toKeyValue(this.tree.inOrder().next().value); + } + last() { + return this.toKeyValue(this.tree.reverseInOrder().next().value); + } + shift() { + const first = this.first(); + if (first === void 0) return void 0; + this.delete(first[0]); + return first; + } + pop() { + const last = this.last(); + if (last === void 0) return void 0; + this.delete(last[0]); + return last; + } + toKeyValue(key) { + return key != null ? [key, this.map.get(key)] : void 0; + } + *[Symbol.iterator]() { + for (const key of this.keys()) yield this.toKeyValue(key); + } + *keys() { + for (const key of this.tree.inOrder()) yield key; + } + *values() { + for (const key of this.keys()) yield this.map.get(key); + return void 0; + } + *rkeys() { + for (const key of this.tree.reverseInOrder()) yield key; + return void 0; + } + *rvalues() { + for (const key of this.rkeys()) yield this.map.get(key); + return void 0; + } +}; + /** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` diff --git a/solution/0700-0799/0729.My Calendar I/README_EN.md b/solution/0700-0799/0729.My Calendar I/README_EN.md index 4a064579219ac..a164293f28af7 100644 --- a/solution/0700-0799/0729.My Calendar I/README_EN.md +++ b/solution/0700-0799/0729.My Calendar I/README_EN.md @@ -74,12 +74,13 @@ from sortedcontainers import SortedDict class MyCalendar: + def __init__(self): self.sd = SortedDict() def book(self, start: int, end: int) -> bool: idx = self.sd.bisect_right(start) - if idx < len(self.sd) and end > self.sd.values()[idx]: + if idx < len(self.sd) and self.sd.values()[idx] < end: return False self.sd[end] = start return True @@ -93,33 +94,26 @@ class MyCalendar: #### Java ```java -import java.util.Map; -import java.util.TreeMap; - class MyCalendar { - private final TreeMap tm = new TreeMap<>(); public MyCalendar() { } - public boolean book(int start, int end) { - Map.Entry ent = tm.floorEntry(start); - if (ent != null && ent.getValue() > start) { + public boolean book(int startTime, int endTime) { + var e = tm.ceilingEntry(startTime + 1); + if (e != null && e.getValue() < endTime) { return false; } - ent = tm.ceilingEntry(start); - if (ent != null && ent.getKey() < end) { - return false; - } - tm.put(start, end); + tm.put(endTime, startTime); return true; } } /** - * Your MyCalendar object will be instantiated and called as such: MyCalendar - * obj = new MyCalendar(); boolean param_1 = obj.book(start,end); + * Your MyCalendar object will be instantiated and called as such: + * MyCalendar obj = new MyCalendar(); + * boolean param_1 = obj.book(startTime,endTime); */ ``` @@ -128,31 +122,26 @@ class MyCalendar { ```cpp class MyCalendar { public: - map m; - MyCalendar() { } - bool book(int start, int end) { - ++m[start]; - --m[end]; - int s = 0; - for (auto& [k, v] : m) { - s += v; - if (s > 1) { - --m[start]; - ++m[end]; - return false; - } + bool book(int startTime, int endTime) { + auto e = m.lower_bound(startTime + 1); + if (e != m.end() && e->second < endTime) { + return false; } + m[endTime] = startTime; return true; } + +private: + map m; }; /** * Your MyCalendar object will be instantiated and called as such: * MyCalendar* obj = new MyCalendar(); - * bool param_1 = obj->book(start,end); + * bool param_1 = obj->book(startTime,endTime); */ ``` @@ -169,21 +158,18 @@ func Constructor() MyCalendar { } } -func (this *MyCalendar) Book(start int, end int) bool { - if p, ok := this.rbt.Floor(start); ok && p.Value.(int) > start { +func (this *MyCalendar) Book(startTime int, endTime int) bool { + if p, ok := this.rbt.Ceiling(startTime + 1); ok && p.Value.(int) < endTime { return false } - if p, ok := this.rbt.Ceiling(start); ok && p.Key.(int) < end { - return false - } - this.rbt.Put(start, end) + this.rbt.Put(endTime, startTime) return true } /** * Your MyCalendar object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); + * param_1 := obj.Book(startTime,endTime); */ ``` @@ -191,28 +177,509 @@ func (this *MyCalendar) Book(start int, end int) bool { ```ts class MyCalendar { - private calendar: number[][]; - + tm: TreeMap; constructor() { - this.calendar = []; + this.tm = new TreeMap(); + } + + book(startTime: number, endTime: number): boolean { + const e = this.tm.higher(startTime); + if (e && e[1] < endTime) { + return false; + } + this.tm.set(endTime, startTime); + return true; + } +} + +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; } - book(start: number, end: number): boolean { - for (const item of this.calendar) { - if (end <= item[0] || item[1] <= start) { - continue; + 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; } - this.calendar.push([start, end]); + node.parent = parent; + this.fixAfterInsert(node); return true; } + + search(predicate: (val: T) => boolean, direction: 'left' | 'right'): T | undefined { + let p = this.root; + let result = null; + while (p) { + if (predicate(p.data)) { + result = p; + p = p[direction]; + } else { + p = p[direction === 'left' ? 'right' : 'left']; + } + } + return result?.data; + } + + 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; + } + + count(data: T): number { + const node = this.find(data); + return node ? node.count : 0; + } + + *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 TreeMap { + _size: number; + tree: RBTree; + map: Map = new Map(); + compare: Compare; + constructor( + collection: Array<[K, V]> | Compare = [], + compare: Compare = (l: K, r: K) => (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 [key, val] of collection) this.set(key, val); + } + + size(): number { + return this._size; + } + + has(key: K): boolean { + return !!this.tree.find(key); + } + + get(key: K): V | undefined { + return this.map.get(key); + } + + set(key: K, val: V): boolean { + const successful = this.tree.insert(key); + this._size += successful ? 1 : 0; + this.map.set(key, val); + return successful; + } + + delete(key: K): boolean { + const deleted = this.tree.deleteAll(key); + this._size -= deleted ? 1 : 0; + return deleted; + } + + ceil(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left')); + } + + floor(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right')); + } + + higher(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left')); + } + + lower(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right')); + } + + first(): [K, V] | undefined { + return this.toKeyValue(this.tree.inOrder().next().value); + } + + last(): [K, V] | undefined { + return this.toKeyValue(this.tree.reverseInOrder().next().value); + } + + shift(): [K, V] | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first[0]); + return first; + } + + pop(): [K, V] | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last[0]); + return last; + } + + toKeyValue(key: K): [K, V]; + toKeyValue(key: undefined): undefined; + toKeyValue(key: K | undefined): [K, V] | undefined; + toKeyValue(key: K | undefined): [K, V] | undefined { + return key != null ? [key, this.map.get(key)!] : undefined; + } + + *[Symbol.iterator](): Generator<[K, V], void, void> { + for (const key of this.keys()) yield this.toKeyValue(key); + } + + *keys(): Generator { + for (const key of this.tree.inOrder()) yield key; + } + + *values(): Generator { + for (const key of this.keys()) yield this.map.get(key)!; + return undefined; + } + + *rkeys(): Generator { + for (const key of this.tree.reverseInOrder()) yield key; + return undefined; + } + + *rvalues(): Generator { + for (const key of this.rkeys()) yield this.map.get(key)!; + return undefined; + } } /** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` @@ -222,33 +689,23 @@ class MyCalendar { use std::collections::BTreeMap; struct MyCalendar { - bt: BTreeMap, + tm: BTreeMap, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MyCalendar { fn new() -> Self { MyCalendar { - bt: BTreeMap::new(), + tm: BTreeMap::new(), } } - fn book(&mut self, start: i32, end: i32) -> bool { - if let Some((_, &val)) = self.bt.range(..=start).last() { - println!("{} {} {}", start, end, val); - if val > start { - return false; - } - } - if let Some((&key, _)) = self.bt.range(start..).next() { - if key < end { + fn book(&mut self, start_time: i32, end_time: i32) -> bool { + if let Some((&key, &value)) = self.tm.range(start_time + 1..).next() { + if value < end_time { return false; } } - self.bt.insert(start, end); + self.tm.insert(end_time, start_time); true } } @@ -258,29 +715,389 @@ impl MyCalendar { ```js var MyCalendar = function () { - this.calendar = []; + this.tm = new TreeMap(); }; /** - * @param {number} start - * @param {number} end + * @param {number} startTime + * @param {number} endTime * @return {boolean} */ -MyCalendar.prototype.book = function (start, end) { - for (const item of this.calendar) { - if (end <= item[0] || item[1] <= start) { - continue; - } +MyCalendar.prototype.book = function (startTime, endTime) { + const e = this.tm.higher(startTime); + if (e && e[1] < endTime) { return false; } - this.calendar.push([start, end]); + this.tm.set(endTime, startTime); return true; }; +var RBTreeNode = class { + constructor(data) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + sibling() { + if (!this.parent) return null; + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + isOnLeft() { + return this === this.parent.left; + } + hasRedChild() { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +}; +var RBTree = class { + constructor(compare = (l, r) => (l < r ? -1 : l > r ? 1 : 0)) { + this.root = null; + this.lt = (l, r) => compare(l, r) < 0; + } + rotateLeft(pt) { + 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) { + 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, p2) { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + swapData(p1, p2) { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + fixAfterInsert(pt) { + var _a; + let parent = null; + let grandParent = null; + while ( + pt !== this.root && + pt.color !== 1 && + ((_a = pt.parent) == null ? void 0 : _a.color) === 0 + ) { + parent = pt.parent; + grandParent = pt.parent.parent; + if (parent === (grandParent == null ? void 0 : grandParent.left)) { + const uncle = grandParent.right; + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + this.rotateRight(grandParent); + this.swapColor(parent, grandParent); + pt = parent; + } + } else { + const uncle = grandParent.left; + if (uncle != null && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + this.rotateLeft(grandParent); + this.swapColor(parent, grandParent); + pt = parent; + } + } + } + this.root.color = 1; + } + delete(val) { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + deleteAll(val) { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + deleteNode(v) { + const u = BSTreplace(v); + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent; + if (!u) { + if (v === this.root) this.root = null; + else { + if (uvBlack) { + this.fixDoubleBlack(v); + } else { + if (v.sibling()) { + v.sibling().color = 0; + } + } + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + if (!v.left || !v.right) { + if (v === this.root) { + v.data = u.data; + v.left = v.right = null; + } else { + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + else u.color = 1; + } + return; + } + this.swapData(u, v); + this.deleteNode(u); + function BSTreplace(x) { + var _a; + if (x.left && x.right) return successor(x.right); + if (!x.left && !x.right) return null; + return (_a = x.left) != null ? _a : x.right; + } + function successor(x) { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + fixDoubleBlack(x) { + if (x === this.root) return; + const sibling = x.sibling(); + const parent = x.parent; + if (!sibling) { + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + else this.rotateLeft(parent); + this.fixDoubleBlack(x); + } else { + if (sibling.hasRedChild()) { + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + sibling.right.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + sibling.right.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + insert(data) { + 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; + } + 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; + } + search(predicate, direction) { + let p = this.root; + let result = null; + while (p) { + if (predicate(p.data)) { + result = p; + p = p[direction]; + } else { + p = p[direction === 'left' ? 'right' : 'left']; + } + } + return result == null ? void 0 : result.data; + } + find(data) { + 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 ? p : null; + } + count(data) { + const node = this.find(data); + return node ? node.count : 0; + } + *inOrder(root = this.root) { + 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 = this.root) { + 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; + } +}; + +// src/treemap.ts +var TreeMap = class { + constructor(collection = [], compare = (l, r) => (l < r ? -1 : l > r ? 1 : 0)) { + this.map = new Map(); + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const [key, val] of collection) this.set(key, val); + } + size() { + return this._size; + } + has(key) { + return !!this.tree.find(key); + } + get(key) { + return this.map.get(key); + } + set(key, val) { + const successful = this.tree.insert(key); + this._size += successful ? 1 : 0; + this.map.set(key, val); + return successful; + } + delete(key) { + const deleted = this.tree.deleteAll(key); + this._size -= deleted ? 1 : 0; + return deleted; + } + ceil(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left')); + } + floor(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right')); + } + higher(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left')); + } + lower(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right')); + } + first() { + return this.toKeyValue(this.tree.inOrder().next().value); + } + last() { + return this.toKeyValue(this.tree.reverseInOrder().next().value); + } + shift() { + const first = this.first(); + if (first === void 0) return void 0; + this.delete(first[0]); + return first; + } + pop() { + const last = this.last(); + if (last === void 0) return void 0; + this.delete(last[0]); + return last; + } + toKeyValue(key) { + return key != null ? [key, this.map.get(key)] : void 0; + } + *[Symbol.iterator]() { + for (const key of this.keys()) yield this.toKeyValue(key); + } + *keys() { + for (const key of this.tree.inOrder()) yield key; + } + *values() { + for (const key of this.keys()) yield this.map.get(key); + return void 0; + } + *rkeys() { + for (const key of this.tree.reverseInOrder()) yield key; + return void 0; + } + *rvalues() { + for (const key of this.rkeys()) yield this.map.get(key); + return void 0; + } +}; + /** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` diff --git a/solution/0700-0799/0729.My Calendar I/Solution.cpp b/solution/0700-0799/0729.My Calendar I/Solution.cpp index cbc395d669f2c..62199ef5e9098 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.cpp +++ b/solution/0700-0799/0729.My Calendar I/Solution.cpp @@ -1,28 +1,23 @@ class MyCalendar { public: - map m; - MyCalendar() { } - bool book(int start, int end) { - ++m[start]; - --m[end]; - int s = 0; - for (auto& [k, v] : m) { - s += v; - if (s > 1) { - --m[start]; - ++m[end]; - return false; - } + bool book(int startTime, int endTime) { + auto e = m.lower_bound(startTime + 1); + if (e != m.end() && e->second < endTime) { + return false; } + m[endTime] = startTime; return true; } + +private: + map m; }; /** * Your MyCalendar object will be instantiated and called as such: * MyCalendar* obj = new MyCalendar(); - * bool param_1 = obj->book(start,end); - */ \ No newline at end of file + * bool param_1 = obj->book(startTime,endTime); + */ diff --git a/solution/0700-0799/0729.My Calendar I/Solution.go b/solution/0700-0799/0729.My Calendar I/Solution.go index 169e1b056fd69..5de9e4b48a64e 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.go +++ b/solution/0700-0799/0729.My Calendar I/Solution.go @@ -8,19 +8,16 @@ func Constructor() MyCalendar { } } -func (this *MyCalendar) Book(start int, end int) bool { - if p, ok := this.rbt.Floor(start); ok && p.Value.(int) > start { +func (this *MyCalendar) Book(startTime int, endTime int) bool { + if p, ok := this.rbt.Ceiling(startTime + 1); ok && p.Value.(int) < endTime { return false } - if p, ok := this.rbt.Ceiling(start); ok && p.Key.(int) < end { - return false - } - this.rbt.Put(start, end) + this.rbt.Put(endTime, startTime) return true } /** * Your MyCalendar object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); - */ \ No newline at end of file + * param_1 := obj.Book(startTime,endTime); + */ diff --git a/solution/0700-0799/0729.My Calendar I/Solution.java b/solution/0700-0799/0729.My Calendar I/Solution.java index 3d04816dc4b2e..efd9c50b7ddf1 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.java +++ b/solution/0700-0799/0729.My Calendar I/Solution.java @@ -1,28 +1,21 @@ -import java.util.Map; -import java.util.TreeMap; - class MyCalendar { - private final TreeMap tm = new TreeMap<>(); public MyCalendar() { } - public boolean book(int start, int end) { - Map.Entry ent = tm.floorEntry(start); - if (ent != null && ent.getValue() > start) { - return false; - } - ent = tm.ceilingEntry(start); - if (ent != null && ent.getKey() < end) { + public boolean book(int startTime, int endTime) { + var e = tm.ceilingEntry(startTime + 1); + if (e != null && e.getValue() < endTime) { return false; } - tm.put(start, end); + tm.put(endTime, startTime); return true; } } /** - * Your MyCalendar object will be instantiated and called as such: MyCalendar - * obj = new MyCalendar(); boolean param_1 = obj.book(start,end); - */ \ No newline at end of file + * Your MyCalendar object will be instantiated and called as such: + * MyCalendar obj = new MyCalendar(); + * boolean param_1 = obj.book(startTime,endTime); + */ diff --git a/solution/0700-0799/0729.My Calendar I/Solution.js b/solution/0700-0799/0729.My Calendar I/Solution.js index a8be4d933e83b..bbbdfb5a27543 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.js +++ b/solution/0700-0799/0729.My Calendar I/Solution.js @@ -1,25 +1,385 @@ var MyCalendar = function () { - this.calendar = []; + this.tm = new TreeMap(); }; /** - * @param {number} start - * @param {number} end + * @param {number} startTime + * @param {number} endTime * @return {boolean} */ -MyCalendar.prototype.book = function (start, end) { - for (const item of this.calendar) { - if (end <= item[0] || item[1] <= start) { - continue; - } +MyCalendar.prototype.book = function (startTime, endTime) { + const e = this.tm.higher(startTime); + if (e && e[1] < endTime) { return false; } - this.calendar.push([start, end]); + this.tm.set(endTime, startTime); return true; }; +var RBTreeNode = class { + constructor(data) { + this.data = data; + this.left = this.right = this.parent = null; + this.color = 0; + this.count = 1; + } + sibling() { + if (!this.parent) return null; + return this.isOnLeft() ? this.parent.right : this.parent.left; + } + isOnLeft() { + return this === this.parent.left; + } + hasRedChild() { + return ( + Boolean(this.left && this.left.color === 0) || + Boolean(this.right && this.right.color === 0) + ); + } +}; +var RBTree = class { + constructor(compare = (l, r) => (l < r ? -1 : l > r ? 1 : 0)) { + this.root = null; + this.lt = (l, r) => compare(l, r) < 0; + } + rotateLeft(pt) { + 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) { + 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, p2) { + const tmp = p1.color; + p1.color = p2.color; + p2.color = tmp; + } + swapData(p1, p2) { + const tmp = p1.data; + p1.data = p2.data; + p2.data = tmp; + } + fixAfterInsert(pt) { + var _a; + let parent = null; + let grandParent = null; + while ( + pt !== this.root && + pt.color !== 1 && + ((_a = pt.parent) == null ? void 0 : _a.color) === 0 + ) { + parent = pt.parent; + grandParent = pt.parent.parent; + if (parent === (grandParent == null ? void 0 : grandParent.left)) { + const uncle = grandParent.right; + if (uncle && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + if (pt === parent.right) { + this.rotateLeft(parent); + pt = parent; + parent = pt.parent; + } + this.rotateRight(grandParent); + this.swapColor(parent, grandParent); + pt = parent; + } + } else { + const uncle = grandParent.left; + if (uncle != null && uncle.color === 0) { + grandParent.color = 0; + parent.color = 1; + uncle.color = 1; + pt = grandParent; + } else { + if (pt === parent.left) { + this.rotateRight(parent); + pt = parent; + parent = pt.parent; + } + this.rotateLeft(grandParent); + this.swapColor(parent, grandParent); + pt = parent; + } + } + } + this.root.color = 1; + } + delete(val) { + const node = this.find(val); + if (!node) return false; + node.count--; + if (!node.count) this.deleteNode(node); + return true; + } + deleteAll(val) { + const node = this.find(val); + if (!node) return false; + this.deleteNode(node); + return true; + } + deleteNode(v) { + const u = BSTreplace(v); + const uvBlack = (u === null || u.color === 1) && v.color === 1; + const parent = v.parent; + if (!u) { + if (v === this.root) this.root = null; + else { + if (uvBlack) { + this.fixDoubleBlack(v); + } else { + if (v.sibling()) { + v.sibling().color = 0; + } + } + if (v.isOnLeft()) parent.left = null; + else parent.right = null; + } + return; + } + if (!v.left || !v.right) { + if (v === this.root) { + v.data = u.data; + v.left = v.right = null; + } else { + if (v.isOnLeft()) parent.left = u; + else parent.right = u; + u.parent = parent; + if (uvBlack) this.fixDoubleBlack(u); + else u.color = 1; + } + return; + } + this.swapData(u, v); + this.deleteNode(u); + function BSTreplace(x) { + var _a; + if (x.left && x.right) return successor(x.right); + if (!x.left && !x.right) return null; + return (_a = x.left) != null ? _a : x.right; + } + function successor(x) { + let temp = x; + while (temp.left) temp = temp.left; + return temp; + } + } + fixDoubleBlack(x) { + if (x === this.root) return; + const sibling = x.sibling(); + const parent = x.parent; + if (!sibling) { + this.fixDoubleBlack(parent); + } else { + if (sibling.color === 0) { + parent.color = 0; + sibling.color = 1; + if (sibling.isOnLeft()) this.rotateRight(parent); + else this.rotateLeft(parent); + this.fixDoubleBlack(x); + } else { + if (sibling.hasRedChild()) { + if (sibling.left && sibling.left.color === 0) { + if (sibling.isOnLeft()) { + sibling.left.color = sibling.color; + sibling.color = parent.color; + this.rotateRight(parent); + } else { + sibling.left.color = parent.color; + this.rotateRight(sibling); + this.rotateLeft(parent); + } + } else { + if (sibling.isOnLeft()) { + sibling.right.color = parent.color; + this.rotateLeft(sibling); + this.rotateRight(parent); + } else { + sibling.right.color = sibling.color; + sibling.color = parent.color; + this.rotateLeft(parent); + } + } + parent.color = 1; + } else { + sibling.color = 0; + if (parent.color === 1) this.fixDoubleBlack(parent); + else parent.color = 1; + } + } + } + } + insert(data) { + 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; + } + 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; + } + search(predicate, direction) { + let p = this.root; + let result = null; + while (p) { + if (predicate(p.data)) { + result = p; + p = p[direction]; + } else { + p = p[direction === 'left' ? 'right' : 'left']; + } + } + return result == null ? void 0 : result.data; + } + find(data) { + 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 ? p : null; + } + count(data) { + const node = this.find(data); + return node ? node.count : 0; + } + *inOrder(root = this.root) { + 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 = this.root) { + 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; + } +}; + +// src/treemap.ts +var TreeMap = class { + constructor(collection = [], compare = (l, r) => (l < r ? -1 : l > r ? 1 : 0)) { + this.map = new Map(); + if (typeof collection === 'function') { + compare = collection; + collection = []; + } + this._size = 0; + this.compare = compare; + this.tree = new RBTree(compare); + for (const [key, val] of collection) this.set(key, val); + } + size() { + return this._size; + } + has(key) { + return !!this.tree.find(key); + } + get(key) { + return this.map.get(key); + } + set(key, val) { + const successful = this.tree.insert(key); + this._size += successful ? 1 : 0; + this.map.set(key, val); + return successful; + } + delete(key) { + const deleted = this.tree.deleteAll(key); + this._size -= deleted ? 1 : 0; + return deleted; + } + ceil(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left')); + } + floor(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right')); + } + higher(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left')); + } + lower(target) { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right')); + } + first() { + return this.toKeyValue(this.tree.inOrder().next().value); + } + last() { + return this.toKeyValue(this.tree.reverseInOrder().next().value); + } + shift() { + const first = this.first(); + if (first === void 0) return void 0; + this.delete(first[0]); + return first; + } + pop() { + const last = this.last(); + if (last === void 0) return void 0; + this.delete(last[0]); + return last; + } + toKeyValue(key) { + return key != null ? [key, this.map.get(key)] : void 0; + } + *[Symbol.iterator]() { + for (const key of this.keys()) yield this.toKeyValue(key); + } + *keys() { + for (const key of this.tree.inOrder()) yield key; + } + *values() { + for (const key of this.keys()) yield this.map.get(key); + return void 0; + } + *rkeys() { + for (const key of this.tree.reverseInOrder()) yield key; + return void 0; + } + *rvalues() { + for (const key of this.rkeys()) yield this.map.get(key); + return void 0; + } +}; + /** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ diff --git a/solution/0700-0799/0729.My Calendar I/Solution.py b/solution/0700-0799/0729.My Calendar I/Solution.py index 4b0facc49fd13..d1f07110b5437 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.py +++ b/solution/0700-0799/0729.My Calendar I/Solution.py @@ -7,7 +7,7 @@ def __init__(self): def book(self, start: int, end: int) -> bool: idx = self.sd.bisect_right(start) - if idx < len(self.sd) and end > self.sd.values()[idx]: + if idx < len(self.sd) and self.sd.values()[idx] < end: return False self.sd[end] = start return True diff --git a/solution/0700-0799/0729.My Calendar I/Solution.rs b/solution/0700-0799/0729.My Calendar I/Solution.rs index 2c489c868f025..0c375be25b9b2 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.rs +++ b/solution/0700-0799/0729.My Calendar I/Solution.rs @@ -1,33 +1,23 @@ use std::collections::BTreeMap; struct MyCalendar { - bt: BTreeMap, + tm: BTreeMap, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MyCalendar { fn new() -> Self { MyCalendar { - bt: BTreeMap::new(), + tm: BTreeMap::new(), } } - fn book(&mut self, start: i32, end: i32) -> bool { - if let Some((_, &val)) = self.bt.range(..=start).last() { - println!("{} {} {}", start, end, val); - if val > start { + fn book(&mut self, start_time: i32, end_time: i32) -> bool { + if let Some((&key, &value)) = self.tm.range(start_time + 1..).next() { + if value < end_time { return false; } } - if let Some((&key, _)) = self.bt.range(start..).next() { - if key < end { - return false; - } - } - self.bt.insert(start, end); + self.tm.insert(end_time, start_time); true } } diff --git a/solution/0700-0799/0729.My Calendar I/Solution.ts b/solution/0700-0799/0729.My Calendar I/Solution.ts index 9431622baa6b9..90209496b29d4 100644 --- a/solution/0700-0799/0729.My Calendar I/Solution.ts +++ b/solution/0700-0799/0729.My Calendar I/Solution.ts @@ -1,24 +1,505 @@ class MyCalendar { - private calendar: number[][]; - + tm: TreeMap; constructor() { - this.calendar = []; + this.tm = new TreeMap(); + } + + book(startTime: number, endTime: number): boolean { + const e = this.tm.higher(startTime); + if (e && e[1] < endTime) { + return false; + } + this.tm.set(endTime, startTime); + return true; + } +} + +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; } - book(start: number, end: number): boolean { - for (const item of this.calendar) { - if (end <= item[0] || item[1] <= start) { - continue; + 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; } - this.calendar.push([start, end]); + node.parent = parent; + this.fixAfterInsert(node); return true; } + + search(predicate: (val: T) => boolean, direction: 'left' | 'right'): T | undefined { + let p = this.root; + let result = null; + while (p) { + if (predicate(p.data)) { + result = p; + p = p[direction]; + } else { + p = p[direction === 'left' ? 'right' : 'left']; + } + } + return result?.data; + } + + 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; + } + + count(data: T): number { + const node = this.find(data); + return node ? node.count : 0; + } + + *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 TreeMap { + _size: number; + tree: RBTree; + map: Map = new Map(); + compare: Compare; + constructor( + collection: Array<[K, V]> | Compare = [], + compare: Compare = (l: K, r: K) => (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 [key, val] of collection) this.set(key, val); + } + + size(): number { + return this._size; + } + + has(key: K): boolean { + return !!this.tree.find(key); + } + + get(key: K): V | undefined { + return this.map.get(key); + } + + set(key: K, val: V): boolean { + const successful = this.tree.insert(key); + this._size += successful ? 1 : 0; + this.map.set(key, val); + return successful; + } + + delete(key: K): boolean { + const deleted = this.tree.deleteAll(key); + this._size -= deleted ? 1 : 0; + return deleted; + } + + ceil(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left')); + } + + floor(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right')); + } + + higher(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left')); + } + + lower(target: K): [K, V] | undefined { + return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right')); + } + + first(): [K, V] | undefined { + return this.toKeyValue(this.tree.inOrder().next().value); + } + + last(): [K, V] | undefined { + return this.toKeyValue(this.tree.reverseInOrder().next().value); + } + + shift(): [K, V] | undefined { + const first = this.first(); + if (first === undefined) return undefined; + this.delete(first[0]); + return first; + } + + pop(): [K, V] | undefined { + const last = this.last(); + if (last === undefined) return undefined; + this.delete(last[0]); + return last; + } + + toKeyValue(key: K): [K, V]; + toKeyValue(key: undefined): undefined; + toKeyValue(key: K | undefined): [K, V] | undefined; + toKeyValue(key: K | undefined): [K, V] | undefined { + return key != null ? [key, this.map.get(key)!] : undefined; + } + + *[Symbol.iterator](): Generator<[K, V], void, void> { + for (const key of this.keys()) yield this.toKeyValue(key); + } + + *keys(): Generator { + for (const key of this.tree.inOrder()) yield key; + } + + *values(): Generator { + for (const key of this.keys()) yield this.map.get(key)!; + return undefined; + } + + *rkeys(): Generator { + for (const key of this.tree.reverseInOrder()) yield key; + return undefined; + } + + *rvalues(): Generator { + for (const key of this.rkeys()) yield this.map.get(key)!; + return undefined; + } } /** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */