動機
- 不要想著線段樹什麼的
- 魔鬼藏在細節中
- 謝謝花花醬
- 我應該要複習bsearch QQ
Problem
A Range Module is a module that tracks ranges of numbers. Design a data structure to track the ranges represented as half-open intervals and query about them.
A half-open interval [left, right)
denotes all the real numbers x
where left <= x < right
.
Implement the RangeModule
class:
RangeModule()
Initializes the object of the data structure.void addRange(int left, int right)
Adds the half-open interval[left, right)
, tracking every real number in that interval. Adding an interval that partially overlaps with currently tracked numbers should add any numbers in the interval[left, right)
that are not already tracked.boolean queryRange(int left, int right)
Returnstrue
if every real number in the interval[left, right)
is currently being tracked, andfalse
otherwise.void removeRange(int left, int right)
Stops tracking every real number currently being tracked in the half-open interval[left, right)
.
Example 1:
Input["RangeModule", "addRange", "removeRange", "queryRange", "queryRange", "queryRange"][[], [10, 20], [14, 16], [10, 14], [13, 15], [16, 17]]Output[Leetcode, null, null, true, false, true]ExplanationRangeModule rangeModule = new RangeModule();rangeModule.addRange(10, 20);rangeModule.removeRange(14, 16);rangeModule.queryRange(10, 14); // return True,(Every number in [10, 14) is being tracked)rangeModule.queryRange(13, 15); // return False,(Numbers like 14, 14.03, 14.17 in [13, 15) are not being tracked)rangeModule.queryRange(16, 17); // return True, (The number 16 in [16, 17) is still being tracked, despite the remove operation)
Constraints:
1 <= left < right <= 109
- At most
104
calls will be made toaddRange
,queryRange
, andremoveRange
.
sol
如果沒有花花醬,我解不出來 QQ
其實就是延續insert interval的思緒,我當初做insert interval是用mono stack去做…
但insert interval其實就是找出有overlap的range(左閉右開)之後整個幹掉(中間可能有很多interval),之後重建
這裡挑overlap是有細節的!!
假設當前segments有[[1,10], [10,20]]
,要加[10,30]
,右邊的位置會是2,但左邊的位置不能是1!!
原本我挑overlap的方式是這樣
def findSeg(self, left, right):
l, r = self.seg.bisect(left), self.seg.bisect(right)
return [
l-1 if l != 0 and self.seg[self.seg.iloc[l-1]] > left else l,
r
]
也就是要seg完全包含left才算進去,但是這樣會產生剛剛提到的悲劇
所以要改成
def findSeg(self, left, right):
l, r = self.seg.bisect(left), self.seg.bisect(right)
return [
l-1 if l != 0 and self.seg[self.seg.iloc[l-1]] >= left else l,
r
]
但是這樣不是會包含到[1,10]
,這不符合左閉右開的定義阿
如果說,每一次都是[a,x], [b,c] 但是 x = b
這種狀態,這樣就okay,但每次都會這樣嗎?
有趣的是,會。 這個就是特別處理這個case的,剛好符合我們要的所有需求,所以okay
from sortedcontainers import SortedDict
class RangeModule:
def __init__(self):
self.seg = SortedDict()
def findSeg(self, left, right):
l, r = self.seg.bisect(left), self.seg.bisect(right)
return [
l-1 if l != 0 and self.seg[self.seg.iloc[l-1]] > left else l,
r
]
def removeSegRange(self, l, r):
keys = [self.seg.iloc[i] for i in range(l,r)]
#print(f'removeSegRange {self.seg} {keys} {l} {r}')
[self.seg.pop(k) for k in keys]
def addRange(self, left: int, right: int) -> None:
l, r = self.findSeg(left, right)
print(f'{self.seg} {l} {r}')
if l != r:
left = min(left, self.seg.peekitem(l)[0])
right = max(right, self.seg.peekitem(r-1)[1])
self.removeSegRange(l, r)
self.seg[left] = right
print(f'ADD {(left,right)} {self.seg} {l} {r}')
def queryRange(self, left: int, right: int) -> bool:
l, r = self.findSeg(left, right)
#print(f'Q {(left,right)} {self.seg} {l} {r}')
return l != r and self.seg.iloc[l] <= left and self.seg[self.seg.iloc[l]] >= right
def removeRange(self, left: int, right: int) -> None:
l, r = self.findSeg(left, right)
if l != r:
start = min(left, self.seg.peekitem(l)[0])
end = max(right, self.seg.peekitem(r-1)[1])
self.removeSegRange(l, r)
if start < left:
self.seg[start] = left
if right < end:
self.seg[right] = end
#print(f'RM {(left,right)} {self.seg} {l} {r}')