From 9deee394f90e7a1b142a15b3e9345d30c5c5b3c4 Mon Sep 17 00:00:00 2001 From: ITCharge Date: Fri, 4 Mar 2022 17:45:43 +0800 Subject: [PATCH] =?UTF-8?q?Create=200715.=20Range=20=E6=A8=A1=E5=9D=97.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0715. Range \346\250\241\345\235\227.md" | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 "Solutions/0715. Range \346\250\241\345\235\227.md" diff --git "a/Solutions/0715. Range \346\250\241\345\235\227.md" "b/Solutions/0715. Range \346\250\241\345\235\227.md" new file mode 100644 index 00000000..5753f4ca --- /dev/null +++ "b/Solutions/0715. Range \346\250\241\345\235\227.md" @@ -0,0 +1,131 @@ +## [0715. Range 模块](https://leetcode-cn.com/problems/range-module/) + +- 标签:设计、线段树、有序集合 +- 难度:困难 + +## 题目大意 + +**描述**:`Range` 模块是跟踪数字范围的模块。 + +**要求**: + +- 设计一个数据结构来跟踪查询半开区间 `[left, right)` 内的数字是否被跟踪。 +- 实现 `RangeModule` 类: + - `RangeModule()` 初始化数据结构的对象。 + - `void addRange(int left, int right)` 添加半开区间 `[left, right)`,跟踪该区间中的每个实数。添加与当前跟踪的数字部分重叠的区间时,应当添加在区间 `[left, right)` 中尚未跟踪的任何数字到该区间中。 + - `boolean queryRange(int left, int right)` 只有在当前正在跟踪区间 `[left, right)` 中的每一个实数时,才返回 `True` ,否则返回 `False`。 + - `void removeRange(int left, int right)` 停止跟踪半开区间 `[left, right)` 中当前正在跟踪的每个实数。 + +**说明**: + +- $1 \le left < right \le 10^9$。 + +**示例**: + +``` +rangeModule = RangeModule() -> null +rangeModule.addRange(10, 20) -> null +rangeModule.removeRange(14, 16) -> null +rangeModule.queryRange(10, 14) -> True +rangeModule.queryRange(13, 15) -> False +rangeModule.queryRange(16, 17) -> True +``` + +## 解题思路 + +### 思路 1:线段树 + +这道题可以使用线段树来做,但是效率比较差。 + +区间的范围是 $[0, 10^9]$,普通数组构成的线段树不满足要求。需要用到动态开点线段树。题目要求的是半开区间 `[left, right)` ,而线段树中常用的是闭合区间。但是我们可以将半开区间 `[left, right)` 转为 `[left, right - 1]` 的闭合空间。 + +这样构建线段树的时间复杂度为 $O(log_2n)$,单次区间更新的时间复杂度为 $O(log_2n)$,单次区间查询的时间复杂度为 $O(log_2n)$。总体时间复杂度为 $O(log_2n)$。 + +## 代码 + +### 思路 1 代码: + +```Python +# 线段树的节点类 +class TreeNode: + def __init__(self, left, right, val=False, lazy_tag=None, letNode=None, rightNode=None): + self.left = left # 区间左边界 + self.right = right # 区间右边界 + self.mid = (left + right) >> 1 + self.leftNode = letNode # 区间左节点 + self.rightNode = rightNode # 区间右节点 + self.val = val # 节点值(区间值) + self.lazy_tag = lazy_tag # 区间问题的延迟更新标记 + + +class RangeModule: + + def __init__(self): + self.tree = TreeNode(0, int(1e9)) + + # 向上更新 node 节点区间值,节点的区间值等于该节点左右子节点元素值的聚合计算结果 + def __pushup(self, node): + if node.leftNode and node.rightNode: + node.val = node.leftNode.val and node.rightNode.val + else: + node.val = False + + # 向下更新 node 节点所在区间的左右子节点的值和懒惰标记 + def __pushdown(self, node): + if not node.leftNode: + node.leftNode = TreeNode(node.left, node.mid) + if not node.rightNode: + node.rightNode = TreeNode(node.mid + 1, node.right) + if node.lazy_tag is not None: + node.leftNode.lazy_tag = node.lazy_tag # 更新左子节点懒惰标记 + node.leftNode.val = node.lazy_tag # 左子节点每个元素值增加 lazy_tag + + node.rightNode.lazy_tag = node.lazy_tag # 更新右子节点懒惰标记 + node.rightNode.val = node.lazy_tag # 右子节点每个元素值增加 lazy_tag + + node.lazy_tag = None # 更新当前节点的懒惰标记 + + # 区间更新 + def __update_interval(self, q_left, q_right, val, node): + if q_left <= node.left and node.right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖 + node.lazy_tag = val # 将当前节点的延迟标记增加 val + node.val = val # 当前节点所在区间每个元素值增加 val + return + + self.__pushdown(node) + + if q_left <= node.mid: + self.__update_interval(q_left, q_right, val, node.leftNode) + if q_right > node.mid: + self.__update_interval(q_left, q_right, val, node.rightNode) + + self.__pushup(node) + + # 区间查询,在线段树的 [left, right] 区间范围中搜索区间为 [q_left, q_right] 的区间值 + def __query_interval(self, q_left, q_right, node): + if q_left <= node.left and node.right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖 + return node.val # 直接返回节点值 + + # 需要向下更新节点所在区间的左右子节点的值和懒惰标记 + self.__pushdown(node) + + if q_right <= node.mid: + return self.__query_interval(q_left, q_right, node.leftNode) + if q_left > node.mid: + return self.__query_interval(q_left, q_right, node.rightNode) + + return self.__query_interval(q_left, q_right, node.leftNode) and self.__query_interval(q_left, q_right, node.rightNode) # 返回左右子树元素值的聚合计算结果 + + + def addRange(self, left: int, right: int) -> None: + self.__update_interval(left, right - 1, True, self.tree) + + + def queryRange(self, left: int, right: int) -> bool: + return self.__query_interval(left, right - 1, self.tree) + + + def removeRange(self, left: int, right: int) -> None: + self.__update_interval(left, right - 1, False, self.tree) +``` +