From f02fde386f0f89965e3bc594a6f3fb95bbb8838f Mon Sep 17 00:00:00 2001 From: yanglbme Date: Fri, 13 Oct 2023 09:18:15 +0800 Subject: [PATCH] feat: update solutions to lc problems * No.0184.Department Highest Salary * No.1077.Project Employees III * No.1488.Avoid Flood in The City * No.1532.The Most Recent Three Orders * No.1549.The Most Recent Orders for Each Product * No.1831.Maximum Transaction Each Day --- .../0184.Department Highest Salary/README.md | 59 +- .../README_EN.md | 59 +- .../Solution.sql | 23 +- .../1077.Project Employees III/README.md | 3 +- .../1077.Project Employees III/README_EN.md | 7 +- .../1077.Project Employees III/Solution.sql | 3 +- .../1488.Avoid Flood in The City/README.md | 537 ++++++++++++++++- .../1488.Avoid Flood in The City/README_EN.md | 541 +++++++++++++++++- .../1488.Avoid Flood in The City/Solution.py | 40 +- .../1488.Avoid Flood in The City/Solution.ts | 514 +++++++++++++++++ .../README.md | 31 +- .../README_EN.md | 31 +- .../Solution.sql | 27 +- .../README.md | 33 +- .../README_EN.md | 33 +- .../Solution.sql | 29 +- .../README.md | 20 +- .../README_EN.md | 20 +- .../Solution.sql | 16 +- 19 files changed, 1789 insertions(+), 237 deletions(-) create mode 100644 solution/1400-1499/1488.Avoid Flood in The City/Solution.ts diff --git a/solution/0100-0199/0184.Department Highest Salary/README.md b/solution/0100-0199/0184.Department Highest Salary/README.md index b5c3a433fecd4..90c130f49e264 100644 --- a/solution/0100-0199/0184.Department Highest Salary/README.md +++ b/solution/0100-0199/0184.Department Highest Salary/README.md @@ -80,42 +80,29 @@ Department 表: +**方法一:等值连接 + 子查询** + +我们可以使用等值连接,将 `Employee` 表和 `Department` 表连接起来,连接条件为 `Employee.departmentId = Department.id`,然后使用子查询来找到每个部门的最高工资,最后使用 `WHERE` 子句来筛选出每个部门中薪资最高的员工。 + +**方法二:等值连接 + 窗口函数** + +我们可以使用等值连接,将 `Employee` 表和 `Department` 表连接起来,连接条件为 `Employee.departmentId = Department.id`,然后使用窗口函数 `rank()`,它可以为每个部门的每个员工分配一个排名,然后我们可以选择排名为 $1$ 的行即可。 + ### **SQL** -```sql -SELECT - Department.NAME AS Department, - Employee.NAME AS Employee, - Salary -FROM - Employee, - Department -WHERE - Employee.DepartmentId = Department.Id - AND (Employee.DepartmentId, Salary) IN ( - SELECT DepartmentId, max(Salary) - FROM Employee - GROUP BY DepartmentId - ); -``` - ```sql # Write your MySQL query statement below -SELECT - d.NAME AS Department, - e1.NAME AS Employee, - e1.salary AS Salary +SELECT d.name AS department, e.name AS employee, salary FROM - Employee AS e1 - JOIN Department AS d ON e1.departmentId = d.id + Employee AS e + JOIN Department AS d ON e.departmentId = d.id WHERE - e1.salary = ( - SELECT - MAX(Salary) - FROM Employee AS e2 - WHERE e2.departmentId = d.id + (d.id, salary) IN ( + SELECT departmentId, max(salary) + FROM Employee + GROUP BY 1 ); ``` @@ -124,17 +111,19 @@ WHERE WITH T AS ( SELECT - *, + d.name AS department, + e.name AS employee, + salary, rank() OVER ( - PARTITION BY departmentId + PARTITION BY d.name ORDER BY salary DESC ) AS rk - FROM Employee + FROM + Employee AS e + JOIN Department AS d ON e.departmentId = d.id ) -SELECT d.name AS Department, t.name AS Employee, salary AS Salary -FROM - T AS t - JOIN Department AS d ON t.departmentId = d.id +SELECT department, employee, salary +FROM T WHERE rk = 1; ``` diff --git a/solution/0100-0199/0184.Department Highest Salary/README_EN.md b/solution/0100-0199/0184.Department Highest Salary/README_EN.md index ce1fe4445d1c4..3fd39b36476b6 100644 --- a/solution/0100-0199/0184.Department Highest Salary/README_EN.md +++ b/solution/0100-0199/0184.Department Highest Salary/README_EN.md @@ -78,42 +78,29 @@ Department table: ## Solutions +**Solution 1: Equi-Join + Subquery** + +We can use an equi-join to join the `Employee` table and the `Department` table based on `Employee.departmentId = Department.id`, and then use a subquery to find the highest salary for each department. Finally, we can use a `WHERE` clause to filter out the employees with the highest salary in each department. + +**Solution 2: Equi-Join + Window Function** + +We can use an equi-join to join the `Employee` table and the `Department` table based on `Employee.departmentId = Department.id`, and then use the window function `rank()`, which assigns a rank to each employee in each department based on their salary. Finally, we can select the rows with a rank of $1$ for each department. + ### **SQL** -```sql -SELECT - Department.NAME AS Department, - Employee.NAME AS Employee, - Salary -FROM - Employee, - Department -WHERE - Employee.DepartmentId = Department.Id - AND (Employee.DepartmentId, Salary) IN ( - SELECT DepartmentId, max(Salary) - FROM Employee - GROUP BY DepartmentId - ); -``` - ```sql # Write your MySQL query statement below -SELECT - d.NAME AS Department, - e1.NAME AS Employee, - e1.salary AS Salary +SELECT d.name AS department, e.name AS employee, salary FROM - Employee AS e1 - JOIN Department AS d ON e1.departmentId = d.id + Employee AS e + JOIN Department AS d ON e.departmentId = d.id WHERE - e1.salary = ( - SELECT - MAX(Salary) - FROM Employee AS e2 - WHERE e2.departmentId = d.id + (d.id, salary) IN ( + SELECT departmentId, max(salary) + FROM Employee + GROUP BY 1 ); ``` @@ -122,17 +109,19 @@ WHERE WITH T AS ( SELECT - *, + d.name AS department, + e.name AS employee, + salary, rank() OVER ( - PARTITION BY departmentId + PARTITION BY d.name ORDER BY salary DESC ) AS rk - FROM Employee + FROM + Employee AS e + JOIN Department AS d ON e.departmentId = d.id ) -SELECT d.name AS Department, t.name AS Employee, salary AS Salary -FROM - T AS t - JOIN Department AS d ON t.departmentId = d.id +SELECT department, employee, salary +FROM T WHERE rk = 1; ``` diff --git a/solution/0100-0199/0184.Department Highest Salary/Solution.sql b/solution/0100-0199/0184.Department Highest Salary/Solution.sql index 19119aafe1bd4..1acc8d9707cd1 100644 --- a/solution/0100-0199/0184.Department Highest Salary/Solution.sql +++ b/solution/0100-0199/0184.Department Highest Salary/Solution.sql @@ -1,16 +1,11 @@ # Write your MySQL query statement below -WITH - T AS ( - SELECT - *, - rank() OVER ( - PARTITION BY departmentId - ORDER BY salary DESC - ) AS rk - FROM Employee - ) -SELECT d.name AS Department, t.name AS Employee, salary AS Salary +SELECT d.name AS department, e.name AS employee, salary FROM - T AS t - JOIN Department AS d ON t.departmentId = d.id -WHERE rk = 1; + Employee AS e + JOIN Department AS d ON e.departmentId = d.id +WHERE + (d.id, salary) IN ( + SELECT departmentId, max(salary) + FROM Employee + GROUP BY 1 + ); diff --git a/solution/1000-1099/1077.Project Employees III/README.md b/solution/1000-1099/1077.Project Employees III/README.md index 61d30992994a7..096236399a629 100644 --- a/solution/1000-1099/1077.Project Employees III/README.md +++ b/solution/1000-1099/1077.Project Employees III/README.md @@ -95,8 +95,7 @@ Employee 表: WITH T AS ( SELECT - project_id, - employee_id, + *, rank() OVER ( PARTITION BY project_id ORDER BY experience_years DESC diff --git a/solution/1000-1099/1077.Project Employees III/README_EN.md b/solution/1000-1099/1077.Project Employees III/README_EN.md index 93f7a76dcbbdd..982eaac83bdc6 100644 --- a/solution/1000-1099/1077.Project Employees III/README_EN.md +++ b/solution/1000-1099/1077.Project Employees III/README_EN.md @@ -79,6 +79,10 @@ Employee table: ## Solutions +**Solution 1: Inner Join + Window Function** + +We can first perform an inner join between the `Project` table and the `Employee` table, and then use the window function `rank()` to group the `Project` table, sort it in descending order by `experience_years`, and finally select the most experienced employee for each project. + ### **SQL** @@ -88,8 +92,7 @@ Employee table: WITH T AS ( SELECT - project_id, - employee_id, + *, rank() OVER ( PARTITION BY project_id ORDER BY experience_years DESC diff --git a/solution/1000-1099/1077.Project Employees III/Solution.sql b/solution/1000-1099/1077.Project Employees III/Solution.sql index 5d92a7a9a7513..a1cb136371da7 100644 --- a/solution/1000-1099/1077.Project Employees III/Solution.sql +++ b/solution/1000-1099/1077.Project Employees III/Solution.sql @@ -2,8 +2,7 @@ WITH T AS ( SELECT - project_id, - employee_id, + *, rank() OVER ( PARTITION BY project_id ORDER BY experience_years DESC diff --git a/solution/1400-1499/1488.Avoid Flood in The City/README.md b/solution/1400-1499/1488.Avoid Flood in The City/README.md index 7531f87997736..6beda445fc957 100644 --- a/solution/1400-1499/1488.Avoid Flood in The City/README.md +++ b/solution/1400-1499/1488.Avoid Flood in The City/README.md @@ -79,13 +79,13 @@ **方法一:贪心 + 二分查找** -将所有晴天都存入 `sunny` 数组或者有序集合中,使用哈希表 `rainy` 记录每个湖泊最近一次下雨的日期。初始化答案数组 `ans` 每个元素为 `-1`。 +我们将所有晴天都存入 $sunny$ 数组或者有序集合中,使用哈希表 $rainy$ 记录每个湖泊最近一次下雨的日期。初始化答案数组 $ans$ 每个元素为 $-1$。 -遍历 `rains` 数组,对于每个下雨的日期 $i$,如果 `rainy[rains[i]]` 存在,说明该湖泊在之前下过雨,那么我们需要找到 `sunny` 数组中第一个大于 `rainy[rains[i]]` 的日期,将其替换为下雨的日期,否则说明无法阻止洪水,返回空数组。对于没下雨的日期 $i$,我们将 $i$ 存入 `sunny` 数组中,并且将 `ans[i]` 置为 `1`。 +接下来,我们遍历 $rains$ 数组。对于每个下雨的日期 $i$,如果 $rainy[rains[i]]$ 存在,说明该湖泊在之前下过雨,那么我们需要找到 $sunny$ 数组中第一个大于 $rainy[rains[i]]$ 的日期,将其替换为下雨的日期,否则说明无法阻止洪水,返回空数组。对于没下雨的日期 $i$,我们将 $i$ 存入 $sunny$ 数组中,并且将 $ans[i]$ 置为 $1$。 遍历结束,返回答案数组。 -时间复杂度 $O(n\log n)$,空间复杂度 $O(n)$。其中 $n$ 为 `rains` 数组的长度。 +时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 为 $rains$ 数组的长度。 @@ -94,22 +94,26 @@ ```python +from sortedcontainers import SortedList + + class Solution: def avoidFlood(self, rains: List[int]) -> List[int]: n = len(rains) ans = [-1] * n - sunny = [] + sunny = SortedList() rainy = {} for i, v in enumerate(rains): if v: if v in rainy: - idx = bisect_right(sunny, rainy[v]) + idx = sunny.bisect_right(rainy[v]) if idx == len(sunny): return [] - ans[sunny.pop(idx)] = v + ans[sunny[idx]] = v + sunny.discard(sunny[idx]) rainy[v] = i else: - sunny.append(i) + sunny.add(i) ans[i] = 1 return ans ``` @@ -211,6 +215,525 @@ func avoidFlood(rains []int) []int { } ``` +### **TypeScript** + +```ts +function avoidFlood(rains: number[]): number[] { + const n = rains.length; + const ans: number[] = new Array(n).fill(-1); + const sunny: TreeSet = new TreeSet(); + const rainy: Map = new Map(); + for (let i = 0; i < n; ++i) { + const v = rains[i]; + if (v > 0) { + if (rainy.has(v)) { + const t = sunny.higher(rainy.get(v)!); + if (t === undefined) { + return []; + } + ans[t] = v; + sunny.delete(t); + } + rainy.set(v, i); + } else { + sunny.add(i); + ans[i] = 1; + } + } + 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; + } +} +``` + ### **...** ``` diff --git a/solution/1400-1499/1488.Avoid Flood in The City/README_EN.md b/solution/1400-1499/1488.Avoid Flood in The City/README_EN.md index 433f119619bd1..410484349bbf9 100644 --- a/solution/1400-1499/1488.Avoid Flood in The City/README_EN.md +++ b/solution/1400-1499/1488.Avoid Flood in The City/README_EN.md @@ -71,27 +71,41 @@ After that, it will rain over lakes [1,2]. It's easy to prove that no matter ## Solutions +**Solution 1: Greedy + Binary Search** + +We store all sunny days in the $sunny$ array or a sorted set, and use the hash table $rainy$ to record the last rainy day for each lake. We initialize the answer array $ans$ with each element set to $-1$. + +Next, we traverse the $rains$ array. For each rainy day $i$, if $rainy[rains[i]]$ exists, it means that the lake has rained before, so we need to find the first date in the $sunny$ array that is greater than $rainy[rains[i]]$, and replace it with the rainy day. Otherwise, it means that the flood cannot be prevented, and we return an empty array. For each non-rainy day $i$, we store $i$ in the $sunny$ array and set $ans[i]$ to $1$. + +After the traversal, we return the answer array. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the $rains$ array. + ### **Python3** ```python +from sortedcontainers import SortedList + + class Solution: def avoidFlood(self, rains: List[int]) -> List[int]: n = len(rains) ans = [-1] * n - sunny = [] + sunny = SortedList() rainy = {} for i, v in enumerate(rains): if v: if v in rainy: - idx = bisect_right(sunny, rainy[v]) + idx = sunny.bisect_right(rainy[v]) if idx == len(sunny): return [] - ans[sunny.pop(idx)] = v + ans[sunny[idx]] = v + sunny.discard(sunny[idx]) rainy[v] = i else: - sunny.append(i) + sunny.add(i) ans[i] = 1 return ans ``` @@ -191,6 +205,525 @@ func avoidFlood(rains []int) []int { } ``` +### **TypeScript** + +```ts +function avoidFlood(rains: number[]): number[] { + const n = rains.length; + const ans: number[] = new Array(n).fill(-1); + const sunny: TreeSet = new TreeSet(); + const rainy: Map = new Map(); + for (let i = 0; i < n; ++i) { + const v = rains[i]; + if (v > 0) { + if (rainy.has(v)) { + const t = sunny.higher(rainy.get(v)!); + if (t === undefined) { + return []; + } + ans[t] = v; + sunny.delete(t); + } + rainy.set(v, i); + } else { + sunny.add(i); + ans[i] = 1; + } + } + 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; + } +} +``` + ### **...** ``` diff --git a/solution/1400-1499/1488.Avoid Flood in The City/Solution.py b/solution/1400-1499/1488.Avoid Flood in The City/Solution.py index 92e3a68508239..6cd34b2e447e9 100644 --- a/solution/1400-1499/1488.Avoid Flood in The City/Solution.py +++ b/solution/1400-1499/1488.Avoid Flood in The City/Solution.py @@ -1,18 +1,22 @@ -class Solution: - def avoidFlood(self, rains: List[int]) -> List[int]: - n = len(rains) - ans = [-1] * n - sunny = [] - rainy = {} - for i, v in enumerate(rains): - if v: - if v in rainy: - idx = bisect_right(sunny, rainy[v]) - if idx == len(sunny): - return [] - ans[sunny.pop(idx)] = v - rainy[v] = i - else: - sunny.append(i) - ans[i] = 1 - return ans +from sortedcontainers import SortedList + + +class Solution: + def avoidFlood(self, rains: List[int]) -> List[int]: + n = len(rains) + ans = [-1] * n + sunny = SortedList() + rainy = {} + for i, v in enumerate(rains): + if v: + if v in rainy: + idx = sunny.bisect_right(rainy[v]) + if idx == len(sunny): + return [] + ans[sunny[idx]] = v + sunny.discard(sunny[idx]) + rainy[v] = i + else: + sunny.add(i) + ans[i] = 1 + return ans diff --git a/solution/1400-1499/1488.Avoid Flood in The City/Solution.ts b/solution/1400-1499/1488.Avoid Flood in The City/Solution.ts new file mode 100644 index 0000000000000..b1154acededfb --- /dev/null +++ b/solution/1400-1499/1488.Avoid Flood in The City/Solution.ts @@ -0,0 +1,514 @@ +function avoidFlood(rains: number[]): number[] { + const n = rains.length; + const ans: number[] = new Array(n).fill(-1); + const sunny: TreeSet = new TreeSet(); + const rainy: Map = new Map(); + for (let i = 0; i < n; ++i) { + const v = rains[i]; + if (v > 0) { + if (rainy.has(v)) { + const t = sunny.higher(rainy.get(v)!); + if (t === undefined) { + return []; + } + ans[t] = v; + sunny.delete(t); + } + rainy.set(v, i); + } else { + sunny.add(i); + ans[i] = 1; + } + } + 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; + } +} diff --git a/solution/1500-1599/1532.The Most Recent Three Orders/README.md b/solution/1500-1599/1532.The Most Recent Three Orders/README.md index 54b35b6ad2e87..bf39a65008816 100644 --- a/solution/1500-1599/1532.The Most Recent Three Orders/README.md +++ b/solution/1500-1599/1532.The Most Recent Three Orders/README.md @@ -111,7 +111,9 @@ Marwan 只有 1 笔订单。 -**方法一:窗口函数** +**方法一:等值连接 + 窗口函数** + +我们可以使用等值连接,将 `Customers` 表和 `Orders` 表按照 `customer_id` 进行连接,然后使用 `row_number()` 窗口函数来为每个消费者的订单按照 `order_date` 降序排列,并为每个消费者的订单添加一个序号,最后筛选出序号小于等于 $3$ 的订单即可。 @@ -119,27 +121,22 @@ Marwan 只有 1 笔订单。 ```sql # Write your MySQL query statement below -SELECT - name AS customer_name, - o.customer_id, - order_id, - order_date -FROM - Customers AS c - JOIN ( +WITH + T AS ( SELECT - customer_id, - order_date, - order_id, - rank() OVER ( + *, + row_number() OVER ( PARTITION BY customer_id ORDER BY order_date DESC ) AS rk - FROM orders - ) AS o - ON c.customer_id = o.customer_id + FROM + Orders + JOIN Customers USING (customer_id) + ) +SELECT name AS customer_name, customer_id, order_id, order_date +FROM T WHERE rk <= 3 -ORDER BY name, o.customer_id, order_date DESC; +ORDER BY 1, 2, 4 DESC; ``` diff --git a/solution/1500-1599/1532.The Most Recent Three Orders/README_EN.md b/solution/1500-1599/1532.The Most Recent Three Orders/README_EN.md index 53749c50a4230..923928d97b36c 100644 --- a/solution/1500-1599/1532.The Most Recent Three Orders/README_EN.md +++ b/solution/1500-1599/1532.The Most Recent Three Orders/README_EN.md @@ -100,33 +100,32 @@ We sort the result table by customer_name in ascending order, by customer_id in ## Solutions +**Solution 1: Equi-Join + Window Function** + +We can use an equi-join to join the `Customers` table and the `Orders` table based on `customer_id`, and then use the window function `row_number()` to sort the orders for each customer by `order_date` in descending order and assign a row number to each order. Finally, we can filter out the orders with a row number less than or equal to $3$. + ### **SQL** ```sql # Write your MySQL query statement below -SELECT - name AS customer_name, - o.customer_id, - order_id, - order_date -FROM - Customers AS c - JOIN ( +WITH + T AS ( SELECT - customer_id, - order_date, - order_id, - rank() OVER ( + *, + row_number() OVER ( PARTITION BY customer_id ORDER BY order_date DESC ) AS rk - FROM orders - ) AS o - ON c.customer_id = o.customer_id + FROM + Orders + JOIN Customers USING (customer_id) + ) +SELECT name AS customer_name, customer_id, order_id, order_date +FROM T WHERE rk <= 3 -ORDER BY name, o.customer_id, order_date DESC; +ORDER BY 1, 2, 4 DESC; ``` diff --git a/solution/1500-1599/1532.The Most Recent Three Orders/Solution.sql b/solution/1500-1599/1532.The Most Recent Three Orders/Solution.sql index 887d8e5b7407b..901986fd6d6ab 100644 --- a/solution/1500-1599/1532.The Most Recent Three Orders/Solution.sql +++ b/solution/1500-1599/1532.The Most Recent Three Orders/Solution.sql @@ -1,22 +1,17 @@ # Write your MySQL query statement below -SELECT - name AS customer_name, - o.customer_id, - order_id, - order_date -FROM - Customers AS c - JOIN ( +WITH + T AS ( SELECT - customer_id, - order_date, - order_id, - rank() OVER ( + *, + row_number() OVER ( PARTITION BY customer_id ORDER BY order_date DESC ) AS rk - FROM orders - ) AS o - ON c.customer_id = o.customer_id + FROM + Orders + JOIN Customers USING (customer_id) + ) +SELECT name AS customer_name, customer_id, order_id, order_date +FROM T WHERE rk <= 3 -ORDER BY name, o.customer_id, order_date DESC; +ORDER BY 1, 2, 4 DESC; diff --git a/solution/1500-1599/1549.The Most Recent Orders for Each Product/README.md b/solution/1500-1599/1549.The Most Recent Orders for Each Product/README.md index d6be01465449e..67a33ddb40ac3 100644 --- a/solution/1500-1599/1549.The Most Recent Orders for Each Product/README.md +++ b/solution/1500-1599/1549.The Most Recent Orders for Each Product/README.md @@ -119,29 +119,32 @@ hard disk 没有被下单, 我们不把它包含在结果表中. +**方法一:等值连接 + 窗口函数** + +我们可以使用等值连接,将 `Orders` 表和 `Products` 表按照 `product_id` 连接起来,然后使用窗口函数 `rank()`,对 `Orders` 表中的每个 `product_id` 进行分组,按照 `order_date` 降序排列,然后取出每个分组中排名第一的记录。 + ### **SQL** ```sql # Write your MySQL query statement below -SELECT - product_name, - o.product_id, - o.order_id, - o.order_date -FROM - Orders AS o - JOIN Products AS p ON o.product_id = p.product_id -WHERE - (o.product_id, order_date) IN ( +WITH + T AS ( SELECT - product_id, - max(order_date) AS order_date - FROM Orders - GROUP BY product_id + *, + rank() OVER ( + PARTITION BY product_id + ORDER BY order_date DESC + ) AS rk + FROM + Orders + JOIN Products USING (product_id) ) -ORDER BY product_name, o.product_id, o.order_id; +SELECT product_name, product_id, order_id, order_date +FROM T +WHERE rk = 1 +ORDER BY 1, 2, 3; ``` diff --git a/solution/1500-1599/1549.The Most Recent Orders for Each Product/README_EN.md b/solution/1500-1599/1549.The Most Recent Orders for Each Product/README_EN.md index a5b02f8f99022..cfce43577bac9 100644 --- a/solution/1500-1599/1549.The Most Recent Orders for Each Product/README_EN.md +++ b/solution/1500-1599/1549.The Most Recent Orders for Each Product/README_EN.md @@ -115,29 +115,32 @@ The hard disk was never ordered and we do not include it in the result table. ## Solutions +**Solution 1: Equi-Join + Window Function** + +We can use an equi-join to join the `Orders` table and the `Products` table based on `product_id`, and then use the window function `rank()`, which assigns a rank to each `product_id` in the `Orders` table based on its `order_date` in descending order. Finally, we can select the rows with a rank of $1$ for each `product_id`. + ### **SQL** ```sql # Write your MySQL query statement below -SELECT - product_name, - o.product_id, - o.order_id, - o.order_date -FROM - Orders AS o - JOIN Products AS p ON o.product_id = p.product_id -WHERE - (o.product_id, order_date) IN ( +WITH + T AS ( SELECT - product_id, - max(order_date) AS order_date - FROM Orders - GROUP BY product_id + *, + rank() OVER ( + PARTITION BY product_id + ORDER BY order_date DESC + ) AS rk + FROM + Orders + JOIN Products USING (product_id) ) -ORDER BY product_name, o.product_id, o.order_id; +SELECT product_name, product_id, order_id, order_date +FROM T +WHERE rk = 1 +ORDER BY 1, 2, 3; ``` diff --git a/solution/1500-1599/1549.The Most Recent Orders for Each Product/Solution.sql b/solution/1500-1599/1549.The Most Recent Orders for Each Product/Solution.sql index 5eae5ff4b9021..3430592f23866 100644 --- a/solution/1500-1599/1549.The Most Recent Orders for Each Product/Solution.sql +++ b/solution/1500-1599/1549.The Most Recent Orders for Each Product/Solution.sql @@ -1,18 +1,17 @@ # Write your MySQL query statement below -SELECT - product_name, - o.product_id, - o.order_id, - o.order_date -FROM - Orders AS o - JOIN Products AS p ON o.product_id = p.product_id -WHERE - (o.product_id, order_date) IN ( +WITH + T AS ( SELECT - product_id, - max(order_date) AS order_date - FROM Orders - GROUP BY product_id + *, + rank() OVER ( + PARTITION BY product_id + ORDER BY order_date DESC + ) AS rk + FROM + Orders + JOIN Products USING (product_id) ) -ORDER BY product_name, o.product_id, o.order_id; +SELECT product_name, product_id, order_id, order_date +FROM T +WHERE rk = 1 +ORDER BY 1, 2, 3; diff --git a/solution/1800-1899/1831.Maximum Transaction Each Day/README.md b/solution/1800-1899/1831.Maximum Transaction Each Day/README.md index fd5e7706193c6..6833e47a668fc 100644 --- a/solution/1800-1899/1831.Maximum Transaction Each Day/README.md +++ b/solution/1800-1899/1831.Maximum Transaction Each Day/README.md @@ -67,6 +67,10 @@ Transactions table: +**方法一:窗口函数** + +我们可以使用窗口函数 `RANK`,按照每天的交易金额 `amount` 降序排列,然后选择排名为 $1$ 的交易。 + ### **SQL** @@ -75,20 +79,20 @@ Transactions table: ```sql # Write your MySQL query statement below -SELECT - transaction_id -FROM - ( +WITH + T AS ( SELECT transaction_id, rank() OVER ( - PARTITION BY date_format(day, '%Y-%m-%d') + PARTITION BY day(day) ORDER BY amount DESC ) AS rk FROM Transactions - ORDER BY transaction_id - ) AS t -WHERE rk = 1; + ) +SELECT transaction_id +FROM T +WHERE rk = 1 +ORDER BY 1; ``` diff --git a/solution/1800-1899/1831.Maximum Transaction Each Day/README_EN.md b/solution/1800-1899/1831.Maximum Transaction Each Day/README_EN.md index e79a331c0d79c..7fd018e8e2643 100644 --- a/solution/1800-1899/1831.Maximum Transaction Each Day/README_EN.md +++ b/solution/1800-1899/1831.Maximum Transaction Each Day/README_EN.md @@ -62,26 +62,30 @@ We order the result table by transaction_id after collecting these IDs. ## Solutions +**Solution 1: Window Function** + +We can use the window function `RANK()`, which assigns a rank to each transaction based on its amount in descending order, and then select the transactions with a rank of $1$. + ### **SQL** ```sql # Write your MySQL query statement below -SELECT - transaction_id -FROM - ( +WITH + T AS ( SELECT transaction_id, rank() OVER ( - PARTITION BY date_format(day, '%Y-%m-%d') + PARTITION BY day(day) ORDER BY amount DESC ) AS rk FROM Transactions - ORDER BY transaction_id - ) AS t -WHERE rk = 1; + ) +SELECT transaction_id +FROM T +WHERE rk = 1 +ORDER BY 1; ``` diff --git a/solution/1800-1899/1831.Maximum Transaction Each Day/Solution.sql b/solution/1800-1899/1831.Maximum Transaction Each Day/Solution.sql index df71f4c362e96..974b30778a3d3 100644 --- a/solution/1800-1899/1831.Maximum Transaction Each Day/Solution.sql +++ b/solution/1800-1899/1831.Maximum Transaction Each Day/Solution.sql @@ -1,15 +1,15 @@ # Write your MySQL query statement below -SELECT - transaction_id -FROM - ( +WITH + T AS ( SELECT transaction_id, rank() OVER ( - PARTITION BY date_format(day, '%Y-%m-%d') + PARTITION BY day(day) ORDER BY amount DESC ) AS rk FROM Transactions - ORDER BY transaction_id - ) AS t -WHERE rk = 1; + ) +SELECT transaction_id +FROM T +WHERE rk = 1 +ORDER BY 1;