From 87bdfa46a76ea6e80f90fbd585545055a552ddc8 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Tue, 9 Apr 2024 20:02:29 +0800 Subject: [PATCH 1/2] feat: add solutions to lc problem: No.2653 No.2653.Sliding Subarray Beauty --- .../2653.Sliding Subarray Beauty/README.md | 429 ++++++++++++++++- .../2653.Sliding Subarray Beauty/README_EN.md | 437 +++++++++++++++++- .../2653.Sliding Subarray Beauty/Solution.py | 23 +- .../Solution2.cpp | 97 ++++ .../2653.Sliding Subarray Beauty/Solution2.go | 104 +++++ .../Solution2.java | 90 ++++ .../2653.Sliding Subarray Beauty/Solution2.py | 79 +++- 7 files changed, 1203 insertions(+), 56 deletions(-) create mode 100644 solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp create mode 100644 solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.go create mode 100644 solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.java diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/README.md b/solution/2600-2699/2653.Sliding Subarray Beauty/README.md index a2d1752604849..6c5c0c22627bf 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/README.md +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/README.md @@ -76,6 +76,28 @@ +```python +class Solution: + def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]: + def f(x: int) -> int: + s = 0 + for i in range(50): + s += cnt[i] + if s >= x: + return i - 50 + return 0 + + cnt = [0] * 101 + for v in nums[:k]: + cnt[v + 50] += 1 + ans = [f(x)] + for i in range(k, len(nums)): + cnt[nums[i] + 50] += 1 + cnt[nums[i - k] + 50] -= 1 + ans.append(f(x)) + return ans +``` + ```python from sortedcontainers import SortedList @@ -211,32 +233,407 @@ function getSubarrayBeauty(nums: number[], k: number, x: number): number[] { -### 方法二 +### 方法二:双优先队列(大小根堆) + 延迟删除 + +我们可以使用两个优先队列(大小根堆)维护当前窗口中的元素,其中一个优先队列存储当前窗口中较小的 $x$ 个元素,另一个优先队列存储当前窗口中较大的 $k - x$ 个元素。我们还需要一个延迟删除字典 `delayed`,用于记录当前窗口中的元素是否需要删除。 + +我们设计一个类 `MedianFinder`,用于维护当前窗口中的元素。该类包含以下方法: + +- `add_num(num)`:将 `num` 加入当前窗口中。 +- `find()`:返回当前窗口的美丽值。 +- `remove_num(num)`:将 `num` 从当前窗口中移除。 +- `prune(pq)`:如果堆顶元素在延迟删除字典 `delayed` 中,则将其从堆顶弹出,并从该元素的延迟删除次数中减一。如果该元素的延迟删除次数为零,则将其从延迟删除字典中删除。 +- `rebalance()`:平衡两个优先队列的大小。 + +在 `add_num(num)` 方法中,我们先考虑将 `num` 加入较小的队列中,如果数量大于 $x$ 或者 `num` 小于等于较小的队列的堆顶元素,则将 `num` 加入较小的队列中;否则,将 `num` 加入较大的队列中。然后我们调用 `rebalance()` 方法,使得较小的队列中的元素数量不超过 $x$。 + +在 `remove_num(num)` 方法中,我们将 `num` 的延迟删除次数加一。然后我们将 `num` 与较小的队列的堆顶元素进行比较,如果 `num` 小于等于较小的队列的堆顶元素,则更新较小的队列的大小,并且调用 `prune()` 方法,使得较小的队列的堆顶元素不在延迟删除字典中。否则,我们更新较大的队列的大小,并且调用 `prune()` 方法,使得较大的队列的堆顶元素不在延迟删除字典中。 + +在 `find()` 方法中,如果较小的队列的大小等于 $x$,则返回较小的队列的堆顶元素,否则返回 $0$。 + +在 `prune(pq)` 方法中,如果堆顶元素在延迟删除字典中,则将其从堆顶弹出,并从该元素的延迟删除次数中减一。如果该元素的延迟删除次数为零,则将其从延迟删除字典中删除。 + +在 `rebalance()` 方法中,如果较小的队列的大小大于 $x$,则将较小的队列的堆顶元素加入较大的队列中,并调用 `prune()` 方法,使得较小的队列的堆顶元素不在延迟删除字典中。如果较小的队列的大小小于 $x$ 且较大的队列的大小大于 $0$,则将较大的队列的堆顶元素加入较小的队列中,并调用 `prune()` 方法,使得较大的队列的堆顶元素不在延迟删除字典中。 + +时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 `nums` 的长度。 + +相似题目: + +- [480. 滑动窗口中位数](https://github.com/doocs/leetcode/blob/main/solution/0400-0499/0480.Sliding%20Window%20Median/README.md) ```python +class MedianFinder: + def __init__(self, x: int): + self.x = x + self.small = [] + self.large = [] + self.delayed = defaultdict(int) + self.small_size = 0 + self.large_size = 0 + + def add_num(self, num: int): + if self.small_size < self.x or num <= -self.small[0]: + heappush(self.small, -num) + self.small_size += 1 + else: + heappush(self.large, num) + self.large_size += 1 + self.rebalance() + + def find(self) -> float: + return -self.small[0] if self.small_size == self.x else 0 + + def remove_num(self, num: int): + self.delayed[num] += 1 + if num <= -self.small[0]: + self.small_size -= 1 + if num == -self.small[0]: + self.prune(self.small) + else: + self.large_size -= 1 + if num == self.large[0]: + self.prune(self.large) + self.rebalance() + + def prune(self, pq: List[int]): + sign = -1 if pq is self.small else 1 + while pq and sign * pq[0] in self.delayed: + self.delayed[sign * pq[0]] -= 1 + if self.delayed[sign * pq[0]] == 0: + self.delayed.pop(sign * pq[0]) + heappop(pq) + + def rebalance(self): + if self.small_size > self.x: + heappush(self.large, -heappop(self.small)) + self.small_size -= 1 + self.large_size += 1 + self.prune(self.small) + elif self.small_size < self.x and self.large_size > 0: + heappush(self.small, -heappop(self.large)) + self.large_size -= 1 + self.small_size += 1 + self.prune(self.large) + + class Solution: def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]: - def f(x: int) -> int: - s = 0 - for i in range(50): - s += cnt[i] - if s >= x: - return i - 50 - return 0 - - cnt = [0] * 101 - for v in nums[:k]: - cnt[v + 50] += 1 - ans = [f(x)] + finder = MedianFinder(x) + for i in range(k): + if nums[i] < 0: + finder.add_num(nums[i]) + ans = [finder.find()] for i in range(k, len(nums)): - cnt[nums[i] + 50] += 1 - cnt[nums[i - k] + 50] -= 1 - ans.append(f(x)) + if nums[i] < 0: + finder.add_num(nums[i]) + if nums[i - k] < 0: + finder.remove_num(nums[i - k]) + ans.append(finder.find()) return ans ``` +```java +class MedianFinder { + private PriorityQueue small = new PriorityQueue<>(Comparator.reverseOrder()); + private PriorityQueue large = new PriorityQueue<>(); + private Map delayed = new HashMap<>(); + private int smallSize; + private int largeSize; + private int x; + + public MedianFinder(int x) { + this.x = x; + } + + public void addNum(int num) { + if (smallSize < x || num <= small.peek()) { + small.offer(num); + ++smallSize; + } else { + large.offer(num); + ++largeSize; + } + rebalance(); + } + + public int find() { + return smallSize == x ? small.peek() : 0; + } + + public void removeNum(int num) { + delayed.merge(num, 1, Integer::sum); + if (num <= small.peek()) { + --smallSize; + if (num == small.peek()) { + prune(small); + } + } else { + --largeSize; + if (num == large.peek()) { + prune(large); + } + } + rebalance(); + } + + private void prune(PriorityQueue pq) { + while (!pq.isEmpty() && delayed.containsKey(pq.peek())) { + if (delayed.merge(pq.peek(), -1, Integer::sum) == 0) { + delayed.remove(pq.peek()); + } + pq.poll(); + } + } + + private void rebalance() { + if (smallSize > x) { + large.offer(small.poll()); + --smallSize; + ++largeSize; + prune(small); + } else if (smallSize < x && largeSize > 0) { + small.offer(large.poll()); + --largeSize; + ++smallSize; + prune(large); + } + } +} + +class Solution { + public int[] getSubarrayBeauty(int[] nums, int k, int x) { + MedianFinder finder = new MedianFinder(x); + for (int i = 0; i < k; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + } + int n = nums.length; + int[] ans = new int[n - k + 1]; + ans[0] = finder.find(); + for (int i = k; i < n; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + if (nums[i - k] < 0) { + finder.removeNum(nums[i - k]); + } + ans[i - k + 1] = finder.find(); + } + return ans; + } +} +``` + +```cpp +class MedianFinder { +public: + MedianFinder(int x) { + this->x = x; + } + + void addNum(int num) { + if (smallSize < x || num <= small.top()) { + small.push(num); + ++smallSize; + } else { + large.push(num); + ++largeSize; + } + reblance(); + } + + void removeNum(int num) { + ++delayed[num]; + if (num <= small.top()) { + --smallSize; + if (num == small.top()) { + prune(small); + } + } else { + --largeSize; + if (num == large.top()) { + prune(large); + } + } + reblance(); + } + + int find() { + return smallSize == x ? small.top() : 0; + } + +private: + priority_queue small; + priority_queue, greater> large; + unordered_map delayed; + int smallSize = 0; + int largeSize = 0; + int x; + + template + void prune(T& pq) { + while (!pq.empty() && delayed[pq.top()]) { + if (--delayed[pq.top()] == 0) { + delayed.erase(pq.top()); + } + pq.pop(); + } + } + + void reblance() { + if (smallSize > x) { + large.push(small.top()); + small.pop(); + --smallSize; + ++largeSize; + prune(small); + } else if (smallSize < x && largeSize > 0) { + small.push(large.top()); + large.pop(); + ++smallSize; + --largeSize; + prune(large); + } + } +}; + + +class Solution { +public: + vector getSubarrayBeauty(vector& nums, int k, int x) { + MedianFinder finder(x); + for (int i = 0; i < k; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + } + int n = nums.size(); + vector ans; + ans.push_back(finder.find()); + for (int i = k; i < n; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + if (nums[i - k] < 0) { + finder.removeNum(nums[i - k]); + } + ans.push_back(finder.find()); + } + return ans; + } +}; +``` + +```go +type MedianFinder struct { + small hp + large hp + delayed map[int]int + smallSize, largeSize int + x int +} + +func Constructor(x int) MedianFinder { + return MedianFinder{hp{}, hp{}, map[int]int{}, 0, 0, x} +} + +func (this *MedianFinder) AddNum(num int) { + if this.smallSize < this.x || num <= -this.small.IntSlice[0] { + heap.Push(&this.small, -num) + this.smallSize++ + } else { + heap.Push(&this.large, num) + this.largeSize++ + } + this.rebalance() +} + +func (this *MedianFinder) Find() int { + if this.smallSize == this.x { + return -this.small.IntSlice[0] + } + return 0 +} + +func (this *MedianFinder) RemoveNum(num int) { + this.delayed[num]++ + if num <= -this.small.IntSlice[0] { + this.smallSize-- + if num == -this.small.IntSlice[0] { + this.prune(&this.small) + } + } else { + this.largeSize-- + if num == this.large.IntSlice[0] { + this.prune(&this.large) + } + } + this.rebalance() +} + +func (this *MedianFinder) prune(pq *hp) { + sign := 1 + if pq == &this.small { + sign = -1 + } + for pq.Len() > 0 && this.delayed[sign*pq.IntSlice[0]] > 0 { + this.delayed[sign*pq.IntSlice[0]]-- + if this.delayed[sign*pq.IntSlice[0]] == 0 { + delete(this.delayed, sign*pq.IntSlice[0]) + } + heap.Pop(pq) + } +} + +func (this *MedianFinder) rebalance() { + if this.smallSize > this.x { + heap.Push(&this.large, -heap.Pop(&this.small).(int)) + this.smallSize-- + this.largeSize++ + this.prune(&this.small) + } else if this.smallSize < this.x && this.largeSize > 0 { + heap.Push(&this.small, -heap.Pop(&this.large).(int)) + this.smallSize++ + this.largeSize-- + this.prune(&this.large) + } +} + +type hp struct{ sort.IntSlice } + +func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] } +func (h *hp) Push(v any) { h.IntSlice = append(h.IntSlice, v.(int)) } +func (h *hp) Pop() any { + a := h.IntSlice + v := a[len(a)-1] + h.IntSlice = a[:len(a)-1] + return v +} + +func getSubarrayBeauty(nums []int, k int, x int) []int { + finder := Constructor(x) + for _, num := range nums[:k] { + if num < 0 { + finder.AddNum(num) + } + } + ans := []int{finder.Find()} + for i := k; i < len(nums); i++ { + if nums[i] < 0 { + finder.AddNum(nums[i]) + } + if nums[i-k] < 0 { + finder.RemoveNum(nums[i-k]) + } + ans = append(ans, finder.Find()) + } + return ans +} +``` + diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md b/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md index cea389e5f8598..888fc0740c970 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md @@ -65,10 +65,38 @@ For [0, -3], the 1st smallest negative integer is -3.

+```python +class Solution: + def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]: + def f(x: int) -> int: + s = 0 + for i in range(50): + s += cnt[i] + if s >= x: + return i - 50 + return 0 + + cnt = [0] * 101 + for v in nums[:k]: + cnt[v + 50] += 1 + ans = [f(x)] + for i in range(k, len(nums)): + cnt[nums[i] + 50] += 1 + cnt[nums[i - k] + 50] -= 1 + ans.append(f(x)) + return ans +``` + ```python from sortedcontainers import SortedList @@ -204,32 +232,407 @@ function getSubarrayBeauty(nums: number[], k: number, x: number): number[] { -### Solution 2 +### Solution 2: Double Priority Queue (Min-Max Heap) + Delayed Deletion + +We can use two priority queues (min-max heap) to maintain the elements in the current window, one priority queue stores the smaller $x$ elements in the current window, and the other priority queue stores the larger $k - x$ elements in the current window. We also need a delayed deletion dictionary `delayed` to record whether the elements in the current window need to be deleted. + +We design a class `MedianFinder` to maintain the elements in the current window. This class includes the following methods: + +- `add_num(num)`: Add `num` to the current window. +- `find()`: Return the beauty value of the current window. +- `remove_num(num)`: Remove `num` from the current window. +- `prune(pq)`: If the top element of the heap is in the delayed deletion dictionary `delayed`, pop it from the top of the heap and subtract one from its delayed deletion count. If the delayed deletion count of the element is zero, delete it from the delayed deletion dictionary. +- `rebalance()`: Balance the size of the two priority queues. + +In the `add_num(num)` method, we first consider adding `num` to the smaller queue. If the count is greater than $x$ or `num` is less than or equal to the top element of the smaller queue, add `num` to the smaller queue; otherwise, add `num` to the larger queue. Then we call the `rebalance()` method to ensure that the number of elements in the smaller queue does not exceed $x$. + +In the `remove_num(num)` method, we increase the delayed deletion count of `num` by one. Then we compare `num` with the top element of the smaller queue. If `num` is less than or equal to the top element of the smaller queue, update the size of the smaller queue and call the `prune()` method to ensure that the top element of the smaller queue is not in the delayed deletion dictionary. Otherwise, we update the size of the larger queue and call the `prune()` method to ensure that the top element of the larger queue is not in the delayed deletion dictionary. + +In the `find()` method, if the size of the smaller queue is equal to $x$, return the top element of the smaller queue, otherwise return $0$. + +In the `prune(pq)` method, if the top element of the heap is in the delayed deletion dictionary, pop it from the top of the heap and subtract one from its delayed deletion count. If the delayed deletion count of the element is zero, delete it from the delayed deletion dictionary. + +In the `rebalance()` method, if the size of the smaller queue is greater than $x$, add the top element of the smaller queue to the larger queue and call the `prune()` method to ensure that the top element of the smaller queue is not in the delayed deletion dictionary. If the size of the smaller queue is less than $x$ and the size of the larger queue is greater than $0$, add the top element of the larger queue to the smaller queue and call the `prune()` method to ensure that the top element of the larger queue is not in the delayed deletion dictionary. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Where $n$ is the length of the array `nums`. + +Similar problems: + +- [480. Sliding Window Median](https://github.com/doocs/leetcode/blob/main/solution/0400-0499/0480.Sliding%20Window%20Median/README.md) ```python +class MedianFinder: + def __init__(self, x: int): + self.x = x + self.small = [] + self.large = [] + self.delayed = defaultdict(int) + self.small_size = 0 + self.large_size = 0 + + def add_num(self, num: int): + if self.small_size < self.x or num <= -self.small[0]: + heappush(self.small, -num) + self.small_size += 1 + else: + heappush(self.large, num) + self.large_size += 1 + self.rebalance() + + def find(self) -> float: + return -self.small[0] if self.small_size == self.x else 0 + + def remove_num(self, num: int): + self.delayed[num] += 1 + if num <= -self.small[0]: + self.small_size -= 1 + if num == -self.small[0]: + self.prune(self.small) + else: + self.large_size -= 1 + if num == self.large[0]: + self.prune(self.large) + self.rebalance() + + def prune(self, pq: List[int]): + sign = -1 if pq is self.small else 1 + while pq and sign * pq[0] in self.delayed: + self.delayed[sign * pq[0]] -= 1 + if self.delayed[sign * pq[0]] == 0: + self.delayed.pop(sign * pq[0]) + heappop(pq) + + def rebalance(self): + if self.small_size > self.x: + heappush(self.large, -heappop(self.small)) + self.small_size -= 1 + self.large_size += 1 + self.prune(self.small) + elif self.small_size < self.x and self.large_size > 0: + heappush(self.small, -heappop(self.large)) + self.large_size -= 1 + self.small_size += 1 + self.prune(self.large) + + class Solution: def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]: - def f(x: int) -> int: - s = 0 - for i in range(50): - s += cnt[i] - if s >= x: - return i - 50 - return 0 - - cnt = [0] * 101 - for v in nums[:k]: - cnt[v + 50] += 1 - ans = [f(x)] + finder = MedianFinder(x) + for i in range(k): + if nums[i] < 0: + finder.add_num(nums[i]) + ans = [finder.find()] for i in range(k, len(nums)): - cnt[nums[i] + 50] += 1 - cnt[nums[i - k] + 50] -= 1 - ans.append(f(x)) + if nums[i] < 0: + finder.add_num(nums[i]) + if nums[i - k] < 0: + finder.remove_num(nums[i - k]) + ans.append(finder.find()) return ans ``` +```java +class MedianFinder { + private PriorityQueue small = new PriorityQueue<>(Comparator.reverseOrder()); + private PriorityQueue large = new PriorityQueue<>(); + private Map delayed = new HashMap<>(); + private int smallSize; + private int largeSize; + private int x; + + public MedianFinder(int x) { + this.x = x; + } + + public void addNum(int num) { + if (smallSize < x || num <= small.peek()) { + small.offer(num); + ++smallSize; + } else { + large.offer(num); + ++largeSize; + } + rebalance(); + } + + public int find() { + return smallSize == x ? small.peek() : 0; + } + + public void removeNum(int num) { + delayed.merge(num, 1, Integer::sum); + if (num <= small.peek()) { + --smallSize; + if (num == small.peek()) { + prune(small); + } + } else { + --largeSize; + if (num == large.peek()) { + prune(large); + } + } + rebalance(); + } + + private void prune(PriorityQueue pq) { + while (!pq.isEmpty() && delayed.containsKey(pq.peek())) { + if (delayed.merge(pq.peek(), -1, Integer::sum) == 0) { + delayed.remove(pq.peek()); + } + pq.poll(); + } + } + + private void rebalance() { + if (smallSize > x) { + large.offer(small.poll()); + --smallSize; + ++largeSize; + prune(small); + } else if (smallSize < x && largeSize > 0) { + small.offer(large.poll()); + --largeSize; + ++smallSize; + prune(large); + } + } +} + +class Solution { + public int[] getSubarrayBeauty(int[] nums, int k, int x) { + MedianFinder finder = new MedianFinder(x); + for (int i = 0; i < k; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + } + int n = nums.length; + int[] ans = new int[n - k + 1]; + ans[0] = finder.find(); + for (int i = k; i < n; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + if (nums[i - k] < 0) { + finder.removeNum(nums[i - k]); + } + ans[i - k + 1] = finder.find(); + } + return ans; + } +} +``` + +```cpp +class MedianFinder { +public: + MedianFinder(int x) { + this->x = x; + } + + void addNum(int num) { + if (smallSize < x || num <= small.top()) { + small.push(num); + ++smallSize; + } else { + large.push(num); + ++largeSize; + } + reblance(); + } + + void removeNum(int num) { + ++delayed[num]; + if (num <= small.top()) { + --smallSize; + if (num == small.top()) { + prune(small); + } + } else { + --largeSize; + if (num == large.top()) { + prune(large); + } + } + reblance(); + } + + int find() { + return smallSize == x ? small.top() : 0; + } + +private: + priority_queue small; + priority_queue, greater> large; + unordered_map delayed; + int smallSize = 0; + int largeSize = 0; + int x; + + template + void prune(T& pq) { + while (!pq.empty() && delayed[pq.top()]) { + if (--delayed[pq.top()] == 0) { + delayed.erase(pq.top()); + } + pq.pop(); + } + } + + void reblance() { + if (smallSize > x) { + large.push(small.top()); + small.pop(); + --smallSize; + ++largeSize; + prune(small); + } else if (smallSize < x && largeSize > 0) { + small.push(large.top()); + large.pop(); + ++smallSize; + --largeSize; + prune(large); + } + } +}; + + +class Solution { +public: + vector getSubarrayBeauty(vector& nums, int k, int x) { + MedianFinder finder(x); + for (int i = 0; i < k; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + } + int n = nums.size(); + vector ans; + ans.push_back(finder.find()); + for (int i = k; i < n; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + if (nums[i - k] < 0) { + finder.removeNum(nums[i - k]); + } + ans.push_back(finder.find()); + } + return ans; + } +}; +``` + +```go +type MedianFinder struct { + small hp + large hp + delayed map[int]int + smallSize, largeSize int + x int +} + +func Constructor(x int) MedianFinder { + return MedianFinder{hp{}, hp{}, map[int]int{}, 0, 0, x} +} + +func (this *MedianFinder) AddNum(num int) { + if this.smallSize < this.x || num <= -this.small.IntSlice[0] { + heap.Push(&this.small, -num) + this.smallSize++ + } else { + heap.Push(&this.large, num) + this.largeSize++ + } + this.rebalance() +} + +func (this *MedianFinder) Find() int { + if this.smallSize == this.x { + return -this.small.IntSlice[0] + } + return 0 +} + +func (this *MedianFinder) RemoveNum(num int) { + this.delayed[num]++ + if num <= -this.small.IntSlice[0] { + this.smallSize-- + if num == -this.small.IntSlice[0] { + this.prune(&this.small) + } + } else { + this.largeSize-- + if num == this.large.IntSlice[0] { + this.prune(&this.large) + } + } + this.rebalance() +} + +func (this *MedianFinder) prune(pq *hp) { + sign := 1 + if pq == &this.small { + sign = -1 + } + for pq.Len() > 0 && this.delayed[sign*pq.IntSlice[0]] > 0 { + this.delayed[sign*pq.IntSlice[0]]-- + if this.delayed[sign*pq.IntSlice[0]] == 0 { + delete(this.delayed, sign*pq.IntSlice[0]) + } + heap.Pop(pq) + } +} + +func (this *MedianFinder) rebalance() { + if this.smallSize > this.x { + heap.Push(&this.large, -heap.Pop(&this.small).(int)) + this.smallSize-- + this.largeSize++ + this.prune(&this.small) + } else if this.smallSize < this.x && this.largeSize > 0 { + heap.Push(&this.small, -heap.Pop(&this.large).(int)) + this.smallSize++ + this.largeSize-- + this.prune(&this.large) + } +} + +type hp struct{ sort.IntSlice } + +func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] } +func (h *hp) Push(v any) { h.IntSlice = append(h.IntSlice, v.(int)) } +func (h *hp) Pop() any { + a := h.IntSlice + v := a[len(a)-1] + h.IntSlice = a[:len(a)-1] + return v +} + +func getSubarrayBeauty(nums []int, k int, x int) []int { + finder := Constructor(x) + for _, num := range nums[:k] { + if num < 0 { + finder.AddNum(num) + } + } + ans := []int{finder.Find()} + for i := k; i < len(nums); i++ { + if nums[i] < 0 { + finder.AddNum(nums[i]) + } + if nums[i-k] < 0 { + finder.RemoveNum(nums[i-k]) + } + ans = append(ans, finder.Find()) + } + return ans +} +``` + diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution.py b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution.py index 9789bf4bb83ef..e49aec3e83b9f 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution.py +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution.py @@ -1,12 +1,19 @@ -from sortedcontainers import SortedList - - class Solution: def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]: - sl = SortedList(nums[:k]) - ans = [sl[x - 1] if sl[x - 1] < 0 else 0] + def f(x: int) -> int: + s = 0 + for i in range(50): + s += cnt[i] + if s >= x: + return i - 50 + return 0 + + cnt = [0] * 101 + for v in nums[:k]: + cnt[v + 50] += 1 + ans = [f(x)] for i in range(k, len(nums)): - sl.remove(nums[i - k]) - sl.add(nums[i]) - ans.append(sl[x - 1] if sl[x - 1] < 0 else 0) + cnt[nums[i] + 50] += 1 + cnt[nums[i - k] + 50] -= 1 + ans.append(f(x)) return ans diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp new file mode 100644 index 0000000000000..c7c200cc2c81a --- /dev/null +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp @@ -0,0 +1,97 @@ +class MedianFinder { +public: + MedianFinder(int x) { + this->x = x; + } + + void addNum(int num) { + if (smallSize < x || num <= small.top()) { + small.push(num); + ++smallSize; + } else { + large.push(num); + ++largeSize; + } + reblance(); + } + + void removeNum(int num) { + ++delayed[num]; + if (num <= small.top()) { + --smallSize; + if (num == small.top()) { + prune(small); + } + } else { + --largeSize; + if (num == large.top()) { + prune(large); + } + } + reblance(); + } + + int find() { + return smallSize == x ? small.top() : 0; + } + +private: + priority_queue small; + priority_queue, greater> large; + unordered_map delayed; + int smallSize = 0; + int largeSize = 0; + int x; + + template + void prune(T& pq) { + while (!pq.empty() && delayed[pq.top()]) { + if (--delayed[pq.top()] == 0) { + delayed.erase(pq.top()); + } + pq.pop(); + } + } + + void reblance() { + if (smallSize > x) { + large.push(small.top()); + small.pop(); + --smallSize; + ++largeSize; + prune(small); + } else if (smallSize < x && largeSize > 0) { + small.push(large.top()); + large.pop(); + ++smallSize; + --largeSize; + prune(large); + } + } +}; + + +class Solution { +public: + vector getSubarrayBeauty(vector& nums, int k, int x) { + MedianFinder finder(x); + for (int i = 0; i < k; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + } + int n = nums.size(); + vector ans; + ans.push_back(finder.find()); + for (int i = k; i < n; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + if (nums[i - k] < 0) { + finder.removeNum(nums[i - k]); + } + ans.push_back(finder.find()); + } + return ans; + } +}; \ No newline at end of file diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.go b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.go new file mode 100644 index 0000000000000..20c68d5c1237c --- /dev/null +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.go @@ -0,0 +1,104 @@ +type MedianFinder struct { + small hp + large hp + delayed map[int]int + smallSize, largeSize int + x int +} + +func Constructor(x int) MedianFinder { + return MedianFinder{hp{}, hp{}, map[int]int{}, 0, 0, x} +} + +func (this *MedianFinder) AddNum(num int) { + if this.smallSize < this.x || num <= -this.small.IntSlice[0] { + heap.Push(&this.small, -num) + this.smallSize++ + } else { + heap.Push(&this.large, num) + this.largeSize++ + } + this.rebalance() +} + +func (this *MedianFinder) Find() int { + if this.smallSize == this.x { + return -this.small.IntSlice[0] + } + return 0 +} + +func (this *MedianFinder) RemoveNum(num int) { + this.delayed[num]++ + if num <= -this.small.IntSlice[0] { + this.smallSize-- + if num == -this.small.IntSlice[0] { + this.prune(&this.small) + } + } else { + this.largeSize-- + if num == this.large.IntSlice[0] { + this.prune(&this.large) + } + } + this.rebalance() +} + +func (this *MedianFinder) prune(pq *hp) { + sign := 1 + if pq == &this.small { + sign = -1 + } + for pq.Len() > 0 && this.delayed[sign*pq.IntSlice[0]] > 0 { + this.delayed[sign*pq.IntSlice[0]]-- + if this.delayed[sign*pq.IntSlice[0]] == 0 { + delete(this.delayed, sign*pq.IntSlice[0]) + } + heap.Pop(pq) + } +} + +func (this *MedianFinder) rebalance() { + if this.smallSize > this.x { + heap.Push(&this.large, -heap.Pop(&this.small).(int)) + this.smallSize-- + this.largeSize++ + this.prune(&this.small) + } else if this.smallSize < this.x && this.largeSize > 0 { + heap.Push(&this.small, -heap.Pop(&this.large).(int)) + this.smallSize++ + this.largeSize-- + this.prune(&this.large) + } +} + +type hp struct{ sort.IntSlice } + +func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] } +func (h *hp) Push(v any) { h.IntSlice = append(h.IntSlice, v.(int)) } +func (h *hp) Pop() any { + a := h.IntSlice + v := a[len(a)-1] + h.IntSlice = a[:len(a)-1] + return v +} + +func getSubarrayBeauty(nums []int, k int, x int) []int { + finder := Constructor(x) + for _, num := range nums[:k] { + if num < 0 { + finder.AddNum(num) + } + } + ans := []int{finder.Find()} + for i := k; i < len(nums); i++ { + if nums[i] < 0 { + finder.AddNum(nums[i]) + } + if nums[i-k] < 0 { + finder.RemoveNum(nums[i-k]) + } + ans = append(ans, finder.Find()) + } + return ans +} \ No newline at end of file diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.java b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.java new file mode 100644 index 0000000000000..c48b9365691bf --- /dev/null +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.java @@ -0,0 +1,90 @@ +class MedianFinder { + private PriorityQueue small = new PriorityQueue<>(Comparator.reverseOrder()); + private PriorityQueue large = new PriorityQueue<>(); + private Map delayed = new HashMap<>(); + private int smallSize; + private int largeSize; + private int x; + + public MedianFinder(int x) { + this.x = x; + } + + public void addNum(int num) { + if (smallSize < x || num <= small.peek()) { + small.offer(num); + ++smallSize; + } else { + large.offer(num); + ++largeSize; + } + rebalance(); + } + + public int find() { + return smallSize == x ? small.peek() : 0; + } + + public void removeNum(int num) { + delayed.merge(num, 1, Integer::sum); + if (num <= small.peek()) { + --smallSize; + if (num == small.peek()) { + prune(small); + } + } else { + --largeSize; + if (num == large.peek()) { + prune(large); + } + } + rebalance(); + } + + private void prune(PriorityQueue pq) { + while (!pq.isEmpty() && delayed.containsKey(pq.peek())) { + if (delayed.merge(pq.peek(), -1, Integer::sum) == 0) { + delayed.remove(pq.peek()); + } + pq.poll(); + } + } + + private void rebalance() { + if (smallSize > x) { + large.offer(small.poll()); + --smallSize; + ++largeSize; + prune(small); + } else if (smallSize < x && largeSize > 0) { + small.offer(large.poll()); + --largeSize; + ++smallSize; + prune(large); + } + } +} + +class Solution { + public int[] getSubarrayBeauty(int[] nums, int k, int x) { + MedianFinder finder = new MedianFinder(x); + for (int i = 0; i < k; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + } + int n = nums.length; + int[] ans = new int[n - k + 1]; + ans[0] = finder.find(); + for (int i = k; i < n; ++i) { + if (nums[i] < 0) { + finder.addNum(nums[i]); + } + if (nums[i - k] < 0) { + finder.removeNum(nums[i - k]); + } + ans[i - k + 1] = finder.find(); + } + return ans; + } +} \ No newline at end of file diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.py b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.py index e49aec3e83b9f..a34266985ef13 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.py +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.py @@ -1,19 +1,68 @@ +class MedianFinder: + def __init__(self, x: int): + self.x = x + self.small = [] + self.large = [] + self.delayed = defaultdict(int) + self.small_size = 0 + self.large_size = 0 + + def add_num(self, num: int): + if self.small_size < self.x or num <= -self.small[0]: + heappush(self.small, -num) + self.small_size += 1 + else: + heappush(self.large, num) + self.large_size += 1 + self.rebalance() + + def find(self) -> float: + return -self.small[0] if self.small_size == self.x else 0 + + def remove_num(self, num: int): + self.delayed[num] += 1 + if num <= -self.small[0]: + self.small_size -= 1 + if num == -self.small[0]: + self.prune(self.small) + else: + self.large_size -= 1 + if num == self.large[0]: + self.prune(self.large) + self.rebalance() + + def prune(self, pq: List[int]): + sign = -1 if pq is self.small else 1 + while pq and sign * pq[0] in self.delayed: + self.delayed[sign * pq[0]] -= 1 + if self.delayed[sign * pq[0]] == 0: + self.delayed.pop(sign * pq[0]) + heappop(pq) + + def rebalance(self): + if self.small_size > self.x: + heappush(self.large, -heappop(self.small)) + self.small_size -= 1 + self.large_size += 1 + self.prune(self.small) + elif self.small_size < self.x and self.large_size > 0: + heappush(self.small, -heappop(self.large)) + self.large_size -= 1 + self.small_size += 1 + self.prune(self.large) + + class Solution: def getSubarrayBeauty(self, nums: List[int], k: int, x: int) -> List[int]: - def f(x: int) -> int: - s = 0 - for i in range(50): - s += cnt[i] - if s >= x: - return i - 50 - return 0 - - cnt = [0] * 101 - for v in nums[:k]: - cnt[v + 50] += 1 - ans = [f(x)] + finder = MedianFinder(x) + for i in range(k): + if nums[i] < 0: + finder.add_num(nums[i]) + ans = [finder.find()] for i in range(k, len(nums)): - cnt[nums[i] + 50] += 1 - cnt[nums[i - k] + 50] -= 1 - ans.append(f(x)) + if nums[i] < 0: + finder.add_num(nums[i]) + if nums[i - k] < 0: + finder.remove_num(nums[i - k]) + ans.append(finder.find()) return ans From 96bcd7be658a007950c6c4964a9d549ab13533ee Mon Sep 17 00:00:00 2001 From: yanglbme Date: Tue, 9 Apr 2024 20:05:10 +0800 Subject: [PATCH 2/2] fix: cpp code --- solution/2600-2699/2653.Sliding Subarray Beauty/README.md | 1 - solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md | 1 - solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/README.md b/solution/2600-2699/2653.Sliding Subarray Beauty/README.md index 6c5c0c22627bf..e3242b8fa9c8c 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/README.md +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/README.md @@ -500,7 +500,6 @@ private: } }; - class Solution { public: vector getSubarrayBeauty(vector& nums, int k, int x) { diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md b/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md index 888fc0740c970..8427635bc7bcf 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/README_EN.md @@ -499,7 +499,6 @@ private: } }; - class Solution { public: vector getSubarrayBeauty(vector& nums, int k, int x) { diff --git a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp index c7c200cc2c81a..3ec36d4e90e09 100644 --- a/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp +++ b/solution/2600-2699/2653.Sliding Subarray Beauty/Solution2.cpp @@ -70,7 +70,6 @@ class MedianFinder { } }; - class Solution { public: vector getSubarrayBeauty(vector& nums, int k, int x) {