Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0b7c945

Browse files
Merge pull request SharingSource#653 from SharingSource/ac_oier
✨feat: add 239、793
2 parents e5afa86 + 104901b commit 0b7c945

File tree

8 files changed

+382
-10
lines changed

8 files changed

+382
-10
lines changed

‎Index/二分.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
| [744. 寻找比目标字母大的最小字母](https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/solution/by-ac_oier-to07/) | 简单 | 🤩🤩🤩🤩🤩 |
4242
| [778. 水位上升的泳池中游泳](https://leetcode-cn.com/problems/swim-in-rising-water/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/swim-in-rising-water/solution/gong-shui-san-xie-yi-ti-shuang-jie-krusk-7c6o/) | 困难 | 🤩🤩🤩 |
4343
| [786. 第 K 个最小的素数分数](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/k-th-smallest-prime-fraction/solution/gong-shui-san-xie-yi-ti-shuang-jie-you-x-8ymk/) | 中等 | 🤩🤩🤩 |
44+
| [793. 阶乘函数后 K 个零](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/) | [LeetCode 题解链接](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/solution/by-ac_oier-pk9g/) | 困难 | 🤩🤩🤩🤩 |
4445
| [852. 山脉数组的峰顶索引](https://leetcode-cn.com/problems/peak-index-in-a-mountain-array/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/peak-index-in-a-mountain-array/solution/gong-shui-san-xie-er-fen-san-fen-cha-zhi-5gfv/) | 简单 | 🤩🤩🤩🤩🤩 |
4546
| [875. 爱吃香蕉的珂珂](https://leetcode.cn/problems/koko-eating-bananas/) | [LeetCode 题解链接](https://leetcode.cn/problems/koko-eating-bananas/solution/by-ac_oier-4z7i/) | 中等 | 🤩🤩🤩🤩 |
4647
| [911. 在线选举](https://leetcode-cn.com/problems/online-election/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/online-election/solution/gong-shui-san-xie-er-fen-yun-yong-ti-by-5y3hi/) | 中等 | 🤩🤩🤩🤩🤩 |

‎Index/分治.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
| 题目 | 题解 | 难度 | 推荐指数 |
22
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---- | -------- |
33
| [4. 寻找两个正序数组的中位数 ](https://leetcode-cn.com/problems/median-of-two-sorted-arrays/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/shua-chuan-lc-po-su-jie-fa-fen-zhi-jie-f-wtu2/) | 困难 | 🤩🤩🤩🤩 |
4+
| [654. 最大二叉树](https://leetcode.cn/problems/maximum-binary-tree/) | [LeetCode 题解链接](https://leetcode.cn/problems/maximum-binary-tree/solution/by-ac_oier-s0wc/) | 中等 | 🤩🤩🤩🤩🤩 |
45

‎Index/容斥原理.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
| [673. 最长递增子序列的个数](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/solution/gong-shui-san-xie-lis-de-fang-an-shu-wen-obuz/) | 中等 | 🤩🤩🤩🤩 |
1818
| [689. 三个无重叠子数组的最大和](https://leetcode-cn.com/problems/maximum-sum-of-3-non-overlapping-subarrays/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-sum-of-3-non-overlapping-subarrays/solution/gong-shui-san-xie-jie-he-qian-zhui-he-de-ancx/) | 困难 | 🤩🤩🤩 |
1919
| [724. 寻找数组的中心下标](https://leetcode-cn.com/problems/find-pivot-index/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-pivot-index/solution/shi-yong-shao-bing-ji-qiao-liang-bian-qi-vkju/) | 简单 | 🤩🤩🤩🤩🤩 |
20+
| [793. 阶乘函数后 K 个零](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/) | [LeetCode 题解链接](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/solution/by-ac_oier-pk9g/) | 困难 | 🤩🤩🤩🤩 |
2021
| [825. 适龄的朋友](https://leetcode-cn.com/problems/friends-of-appropriate-ages/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/friends-of-appropriate-ages/solution/gong-shui-san-xie-yi-ti-shuang-jie-pai-x-maa8/) | 中等 | 🤩🤩🤩🤩 |
2122
| [926. 将字符串翻转到单调递增](https://leetcode.cn/problems/flip-string-to-monotone-increasing/) | [LeetCode 题解链接](https://leetcode.cn/problems/flip-string-to-monotone-increasing/solution/by-ac_oier-h0we/) | 中等 | 🤩🤩🤩🤩 |
2223
| [930. 和相同的二元子数组](https://leetcode-cn.com/problems/binary-subarrays-with-sum/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/binary-subarrays-with-sum/solution/gong-shui-san-xie-yi-ti-shuang-jie-qian-hfoc0/) | 中等 | 🤩🤩🤩 |

‎Index/数学.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
| [650. 只有两个键的键盘](https://leetcode-cn.com/problems/2-keys-keyboard/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/2-keys-keyboard/solution/gong-shui-san-xie-yi-ti-san-jie-dong-tai-f035/) | 中等 | 🤩🤩🤩🤩 |
4949
| [780. 到达终点](https://leetcode-cn.com/problems/reaching-points/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/reaching-points/solution/by-ac_oier-hw11/) | 困难 | 🤩🤩🤩🤩🤩 |
5050
| [789. 逃脱阻碍者](https://leetcode-cn.com/problems/escape-the-ghosts/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/escape-the-ghosts/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-w69gr/) | 中等 | 🤩🤩🤩🤩🤩 |
51+
| [793. 阶乘函数后 K 个零](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/) | [LeetCode 题解链接](https://leetcode.cn/problems/preimage-size-of-factorial-zeroes-function/solution/by-ac_oier-pk9g/) | 困难 | 🤩🤩🤩🤩 |
5152
| [810. 黑板异或游戏](https://leetcode-cn.com/problems/chalkboard-xor-game/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/chalkboard-xor-game/solution/gong-shui-san-xie-noxiang-xin-ke-xue-xi-ges7k/) | 困难 | 🤩🤩🤩🤩 |
5253
| [829. 连续整数求和](https://leetcode.cn/problems/consecutive-numbers-sum/) | [LeetCode 题解链接](https://leetcode.cn/problems/consecutive-numbers-sum/solution/by-ac_oier-220q/) | 困难 | 🤩🤩🤩🤩 |
5354
| [869. 重新排序得到 2 的幂](https://leetcode-cn.com/problems/reordered-power-of-2/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/reordered-power-of-2/solution/gong-shui-san-xie-yi-ti-shuang-jie-dfs-c-3s1e/) | 中等 | 🤩🤩🤩🤩 |
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[239. 滑动窗口最大值]()** ,难度为 **困难**
4+
5+
Tag : 「优先队列(堆)」、「线段树」、「分块」、「单调队列」、「RMQ」
6+
7+
8+
9+
给你一个整数数组 `nums`,有一个大小为 `k` 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 `k` 个数字。滑动窗口每次只向右移动一位。
10+
11+
返回滑动窗口中的最大值 。
12+
13+
示例 1:
14+
```
15+
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
16+
17+
输出:[3,3,5,5,6,7]
18+
19+
解释:
20+
滑动窗口的位置 最大值
21+
--------------- -----
22+
[1 3 -1] -3 5 3 6 7 3
23+
1 [3 -1 -3] 5 3 6 7 3
24+
1 3 [-1 -3 5] 3 6 7 5
25+
1 3 -1 [-3 5 3] 6 7 5
26+
1 3 -1 -3 [5 3 6] 7 6
27+
1 3 -1 -3 5 [3 6 7] 7
28+
```
29+
示例 2:
30+
```
31+
输入:nums = [1], k = 1
32+
33+
输出:[1]
34+
```
35+
36+
提示:
37+
* 1ドル <= nums.length <= 10^5$
38+
* $-10^4 <= nums[i] <= 10^4$
39+
* 1ドル <= k <= nums.length$
40+
41+
---
42+
43+
### 优先队列(堆)
44+
45+
根据题意,容易想到优先队列(大根堆),同时为了确保滑动窗口的大小合法性,我们以二元组 $(idx, nums[idx])$ 的形式进行入队。
46+
47+
当下标达到首个滑动窗口的右端点后,每次尝试从优先队列(大根堆)中取出最大值(若堆顶元素的下标小于当前滑动窗口左端点时,则丢弃该元素)。
48+
49+
代码:
50+
```Java
51+
class Solution {
52+
public int[] maxSlidingWindow(int[] nums, int k) {
53+
PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->b[1]-a[1]);
54+
int n = nums.length, m = n - k + 1, idx = 0;
55+
int[] ans = new int[m];
56+
for (int i = 0; i < n; i++) {
57+
q.add(new int[]{i, nums[i]});
58+
if (i >= k - 1) {
59+
while (q.peek()[0] <= i - k) q.poll();
60+
ans[idx++] = q.peek()[1];
61+
}
62+
}
63+
return ans;
64+
}
65+
}
66+
```
67+
* 时间复杂度:$O(n\log{n})$
68+
* 空间复杂度:$O(n)$
69+
70+
---
71+
72+
### 线段树
73+
74+
容易将问题转换为「区间求和」问题:使用原始的 `nums` 构建线段树等价于在位置 $i$ 插入 $nums[i],ドル即单点操作,而查询每个滑动窗口最大值,则对应的区间查询。
75+
76+
由于只涉及单点修改,无须实现懒标记 `pushdown` 操作,再结合 $n$ 的数据范围为 10ドル^5,ドル无须进行动态开点。
77+
78+
直接写 `build` 四倍空间的线段树数组实现即可。
79+
80+
代码:
81+
```Java
82+
class Solution {
83+
class Node {
84+
int l, r, val;
85+
Node (int _l, int _r) {
86+
l = _l; r = _r; val = Integer.MIN_VALUE;
87+
}
88+
}
89+
Node[] tr = new Node[100010 * 4];
90+
void build(int u, int l, int r) {
91+
tr[u] = new Node(l, r);
92+
if (l == r) return ;
93+
int mid = l + r >> 1;
94+
build(u << 1, l, mid);
95+
build(u << 1 | 1, mid + 1, r);
96+
}
97+
void update(int u, int x, int v) {
98+
if (tr[u].l == x && tr[u].r == x) {
99+
tr[u].val = Math.max(tr[u].val, v);
100+
return ;
101+
}
102+
int mid = tr[u].l + tr[u].r >> 1;
103+
if (x <= mid) update(u << 1, x, v);
104+
else update(u << 1 | 1, x, v);
105+
pushup(u);
106+
}
107+
int query(int u, int l, int r) {
108+
if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
109+
int mid = tr[u].l + tr[u].r >> 1, ans = Integer.MIN_VALUE;
110+
if (l <= mid) ans = query(u << 1, l, r);
111+
if (r > mid) ans = Math.max(ans, query(u << 1 | 1, l, r));
112+
return ans;
113+
}
114+
void pushup(int u) {
115+
tr[u].val = Math.max(tr[u << 1].val, tr[u << 1 | 1].val);
116+
}
117+
public int[] maxSlidingWindow(int[] nums, int k) {
118+
int n = nums.length, m = n - k + 1;
119+
int[] ans = new int[m];
120+
build(1, 1, n);
121+
for (int i = 0; i < n; i++) update(1, i + 1, nums[i]);
122+
for (int i = k; i <= n; i++) ans[i - k] = query(1, i - k + 1, i);
123+
return ans;
124+
}
125+
}
126+
```
127+
* 时间复杂度:建立线段树复杂度为 $O(n\log{n})$;构建答案复杂度为 $O(n\log{n})$。整体复杂度为 $O(n\log{n})$
128+
* 空间复杂度:$O(n)$
129+
130+
---
131+
132+
### 分块
133+
134+
另外一个做法是分块,又名「优雅的暴力」,也是莫队算法的基础。
135+
136+
具体的,除了给定的 `nums` 以外,我们构建一个分块数组 `region`,其中 `region[idx] = x` 含义为块编号为 `idx` 的最大值为 `x`,其中一个块对应一个原始区间 $[l, r]$。
137+
138+
如何定义块大小是实现分块算法的关键。
139+
140+
对于本题,本质是求若干个大小为 $k$ 的区间最大值。
141+
142+
我们可以设定块大小为 $\sqrt{k},ドル这样所需创建的分块数组大小为 $\frac{n}{\sqrt{k}}$。分块数组的更新操作为 $O(1),ドル而查询则为 $\sqrt{k}$。
143+
144+
容易证明查询操作的复杂度:对于每个长度为 $k$ 的 $[l, r]$ 查询操作而言,最多遍历两个(左右端点对应的块)的块内元素,复杂度为 $O(\sqrt{k}),ドル同时最多遍历 $\sqrt{k}$ 个块,复杂度同为 $O(\sqrt{k})$。
145+
146+
因此最多两步复杂度为 $O(\sqrt{k})$ 的块内操作,最多 $\sqrt{k}$ 步复杂度为 $O(1)$ 的块间操作,整体复杂度为 $O(\sqrt{k})$。
147+
148+
因此使用分块算法总的计算量为 $n\times\sqrt{k} = 10^6,ドル可以过。
149+
150+
分块算法的几个操作函数:
151+
152+
* `int getIdx(int x)` :计算原始下标对应的块编号;
153+
* `void add(int x, int v)` :计算原始下标 `x` 对应的下标 `idx`,并将 `region[idx]``v``max` 来更新 `region[idx]`;
154+
* `int query(int l, int r)` :查询 $[l, r]$ 中的最大值,如果 $l$ 和 $r$ 所在块相同,直接遍历 $[l, r]$ 进行取值;若 $l$ 和 $r$ 不同块,则处理 $l$ 和 $r$ 对应的块内元素后,对块编号在 $(getIdx(l), getIdx(r))$ 之间的块进行遍历。
155+
156+
代码:
157+
```Java
158+
class Solution {
159+
int n, m, len;
160+
int[] nums, region;
161+
int getIdx(int x) {
162+
return x / len;
163+
}
164+
void add(int x, int v) {
165+
region[getIdx(x)] = Math.max(region[getIdx(x)], v);
166+
}
167+
int query(int l, int r) {
168+
int ans = Integer.MIN_VALUE;
169+
if (getIdx(l) == getIdx(r)) {
170+
for (int i = l; i <= r; i++) ans = Math.max(ans, nums[i]);
171+
} else {
172+
int i = l, j = r;
173+
while (getIdx(i) == getIdx(l)) ans = Math.max(ans, nums[i++]);
174+
while (getIdx(j) == getIdx(r)) ans = Math.max(ans, nums[j--]);
175+
for (int k = getIdx(i); k <= getIdx(j); k++) ans = Math.max(ans, region[k]);
176+
}
177+
return ans;
178+
}
179+
public int[] maxSlidingWindow(int[] _nums, int k) {
180+
nums = _nums;
181+
n = nums.length; len = (int) Math.sqrt(k); m = n / len + 10;
182+
region = new int[m];
183+
Arrays.fill(region, Integer.MIN_VALUE);
184+
for (int i = 0; i < n; i++) add(i, nums[i]);
185+
int[] ans = new int[n - k + 1];
186+
for (int i = 0; i < n - k + 1; i++) ans[i] = query(i, i + k - 1);
187+
return ans;
188+
}
189+
}
190+
```
191+
* 时间复杂度:数组大小为 $n,ドル块大小为 $\sqrt{k},ドル分块数组大小为 $\frac{n}{\sqrt{k}}$。预处理分块数组复杂度为 $O(n)$(即 `add` 操作复杂度为 $O(1)$ );构造答案复杂度为 $O(n\times\sqrt{k})$(即 `query` 操作复杂度为 $O(\sqrt{k}),ドル最多有 $n$ 次查询)
192+
* 空间复杂度:$\frac{n}{\sqrt{k}}$
193+
194+
---
195+
196+
### 单调队列
197+
198+
关于 `RMQ` 的另外一个优秀做法通常是使用「单调队列/单调栈」。
199+
200+
当然,我们也能不依靠经验,而从问题的本身出发,逐步分析出该做法。
201+
202+
假设我们当前处理到某个长度为 $k$ 的窗口,此时窗口往后滑动一格,会导致后一个数(新窗口的右端点)添加进来,同时会导致前一个数(旧窗口的左端点)移出窗口。
203+
204+
随着窗口的不断平移,该过程会一直发生。**若同一时刻存在两个数 $nums[j]$ 和 $nums[i]$($j < i$)所在一个窗口内,下标更大的数会被更晚移出窗口,此时如果有 $nums[j] <= nums[i]$ 的话,可以完全确定 $nums[j]$ 将不会成为后续任何一个窗口的最大值,此时可以将必然不会是答案的 $nums[j]$ 从候选中进行移除**
205+
206+
不难发现,当我们将所有必然不可能作为答案的元素(即所有满足的小于等于 $nums[i]$ )移除后,候选集合满足「单调递减」特性,即集合首位元素为当前窗口中的最大值(为了满足窗口长度为 $k$ 的要求,在从集合头部取答案时需要先将下标小于的等于的 $i - k$ 的元素移除)。
207+
208+
为方便从尾部添加元素,从头部获取答案,我们可使用「双端队列」存储所有候选元素。
209+
210+
代码:
211+
```Java
212+
class Solution {
213+
public int[] maxSlidingWindow(int[] nums, int k) {
214+
Deque<Integer> d = new ArrayDeque<>();
215+
int n = nums.length, m = n - k + 1;
216+
int[] ans = new int[m];
217+
for (int i = 0; i < n; i++) {
218+
while (!d.isEmpty() && nums[d.peekLast()] <= nums[i]) d.pollLast();
219+
d.addLast(i);
220+
if (i >= k - 1) {
221+
while (!d.isEmpty() && d.peekFirst() <= i - k) d.pollFirst();
222+
ans[i - k + 1] = nums[d.peekFirst()];
223+
}
224+
}
225+
return ans;
226+
}
227+
}
228+
```
229+
* 时间复杂度:$O(n)$
230+
* 空间复杂度:$O(n)$
231+
232+
---
233+
234+
### 最后
235+
236+
这是我们「刷穿 LeetCode」系列文章的第 `No.239` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
237+
238+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
239+
240+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
241+
242+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
243+

‎LeetCode/661-670/662. 二叉树最大宽度(中等).md‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ Tag : 「二叉树」、「DFS」、「哈希表」
5656

5757
而实现上,我们可以利用先 `DFS` 左节点,再 `DFS` 右节点的性质可知,每层的最左节点必然是最先被遍历到,因此我们只需要记录当前层最先被遍历到点编号(即当前层最小节点编号),并在 `DFS` 过程中计算宽度,更新答案即可。
5858

59+
> 看到评论区有同学讨论关于编号溢出问题,之所以溢出仍能 `AC` 是因为测试数组中没有同层内「宽度」左端点不溢出,右端点溢出,同时该层就是最大宽度的数据点。
60+
我们可以通过 `u = u - map.get(depth) + 1` 操作来对同层内的节点进行重新编号(使得同层最靠左的非空节点编号为 1ドル$)。
61+
通过重编号操作 我们可以消除由于深度加深带来的编号溢出问题,同时 `TS` 代码不再需要使用 `bigint`
62+
5963
Java 代码:
6064
```Java
6165
class Solution {
@@ -69,27 +73,29 @@ class Solution {
6973
if (root == null) return ;
7074
if (!map.containsKey(depth)) map.put(depth, u);
7175
ans = Math.max(ans, u - map.get(depth) + 1);
76+
u = u - map.get(depth) + 1;
7277
dfs(root.left, u << 1, depth + 1);
7378
dfs(root.right, u << 1 | 1, depth + 1);
7479
}
7580
}
7681
```
7782
TypeScript 代码:
7883
```TypeScript
79-
let map = new Map<number, bigint>()
80-
let ans = 0n
84+
let map = new Map<number, number>()
85+
let ans = 0
8186
function widthOfBinaryTree(root: TreeNode | null): number {
8287
map.clear()
83-
ans = 0n
84-
dfs(root, 1n, 0)
85-
return Number(ans)
88+
ans = 0
89+
dfs(root, 1, 0)
90+
return ans
8691
};
87-
function dfs(root: TreeNode | null, u: bigint, depth: number): void {
92+
function dfs(root: TreeNode | null, u: number, depth: number): void {
8893
if (root == null) return
8994
if (!map.has(depth)) map.set(depth, u)
90-
if (u - map.get(depth) + 1n > ans) ans = u - map.get(depth) + 1n
91-
dfs(root.left, u << 1n, depth + 1)
92-
dfs(root.right, u << 1n | 1n, depth + 1)
95+
ans = Math.max(ans, u - map.get(depth) + 1)
96+
u = u - map.get(depth) + 1
97+
dfs(root.left, u << 1, depth + 1)
98+
dfs(root.right, u << 1 | 1, depth + 1)
9399
}
94100
```
95101
* 时间复杂度:$O(n)$

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /