Skip to content

Commit 0a98aa8

Browse files
committed
[Feature] add for new
1 parent 0d38720 commit 0a98aa8

File tree

2 files changed

+391
-0
lines changed

2 files changed

+391
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
---
2+
title: LC1004. 最大连续1的个数 III max-consecutive-ones-iii
3+
date: 2025-08-31
4+
categories: [Leetcode-75]
5+
tags: [leetcode, Leetcode-75, sliding-window]
6+
published: true
7+
---
8+
9+
# LC1004. 最大连续1的个数 III max-consecutive-ones-iii
10+
11+
给定一个二进制数组 nums 和一个整数 k,假设最多可以翻转 k 个 0 ,则返回执行操作后 数组中连续 1 的最大个数 。
12+
13+
14+
15+
示例 1:
16+
17+
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
18+
输出:6
19+
解释:[1,1,1,0,0,1,1,1,1,1,1]
20+
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
21+
示例 2:
22+
23+
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
24+
输出:10
25+
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
26+
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
27+
28+
29+
提示:
30+
31+
1 <= nums.length <= 10^5
32+
nums[i] 不是 0 就是 1
33+
0 <= k <= nums.length
34+
35+
# v1-暴力
36+
37+
## 思路
38+
39+
管他这么多,直接从第一个位置开始,暴力计算。
40+
41+
## 实现
42+
43+
```java
44+
public int longestOnes(int[] nums, int k) {
45+
int res = 0;
46+
47+
int n = nums.length;
48+
int zeroCount = 0;
49+
int count = 0;
50+
for(int i = 0; i < n; i++) {
51+
count = 0;
52+
zeroCount = 0;
53+
54+
for(int j = i; j < n; j++) {
55+
if(nums[j] == 0) {
56+
zeroCount++;
57+
}
58+
if(zeroCount > k) {
59+
break;
60+
}
61+
62+
count++;
63+
}
64+
65+
res = Math.max(res, count);
66+
}
67+
68+
return res;
69+
}
70+
```
71+
72+
## 效果
73+
74+
2654ms 击败 5.03%
75+
76+
## 复杂度
77+
78+
TC:最坏情况是 O(n²)
79+
80+
SC:只用到几个变量(zeroCount, count, res),所以是 O(1)
81+
82+
## 反思
83+
84+
如何进一步优化呢?
85+
86+
最大的问题在于重复的计算,如果可以复用就好了。
87+
88+
# v2-滑动窗口
89+
90+
## 思路
91+
92+
我们按照滑动窗口的思路来解决,只不过这次定的不是长度,而是 0 的总数。
93+
94+
用两个指针 left 和 right 表示窗口的左右边界。
95+
96+
窗口内 0 的个数不能超过 k。
97+
98+
每次右指针 right 右移,如果遇到 0,就增加 zeroCount。
99+
100+
当 zeroCount > k 时,左指针 left 右移,直到窗口内 0 的个数 ≤ k。
101+
102+
每次更新窗口长度,取最大值。
103+
104+
## 实现
105+
106+
```java
107+
public int longestOnes(int[] nums, int k) {
108+
int res = 0;
109+
110+
int n = nums.length;
111+
int zeroCount = 0;
112+
int left = 0;
113+
114+
//k个zero
115+
for(int right = 0; right < n; right++) {
116+
if(nums[right] == 0) {
117+
zeroCount++;
118+
}
119+
120+
while(zeroCount > k) {
121+
if(nums[left] == 0) {
122+
zeroCount--;
123+
}
124+
left++;
125+
}
126+
127+
res = Math.max(res, (right-left+1));
128+
}
129+
130+
return res;
131+
}
132+
```
133+
134+
## 效果
135+
136+
3ms 击败 82.84%
137+
138+
## 反思
139+
140+
为什么不是 100%?
141+
142+
# v3-更精简的写法
143+
144+
## 思路
145+
146+
算法本身已经复杂度最优,剩下的就是分支判断之类的优化。
147+
148+
## 实现
149+
150+
下面的方法避免了 zeroCount 和 maxLen。
151+
152+
```java
153+
public int longestOnes(int[] nums, int k) {
154+
int n = nums.length;
155+
int l = 0, r = 0;
156+
while (r < n) {
157+
k -= 1 - nums[r];
158+
r++;
159+
160+
// 翻转 0 数量不够,左边收缩边界
161+
if (k < 0) {
162+
k += 1 - nums[l];
163+
l++;
164+
}
165+
}
166+
167+
return r - l;
168+
}
169+
```
170+
171+
这个代码需要解释一下
172+
173+
```
174+
k -= 1 - nums[r];
175+
nums[r] 是 1 时:1 - nums[r] = 0 → k 不变
176+
nums[r] 是 0 时:1 - nums[r] = 1 → k 减 1
177+
```
178+
179+
也就是说,k 就是 剩余可翻转 0 的数量。
180+
181+
结果返回:
182+
183+
每次循环,窗口 `[l, r)` 始终满足 0 的数量 ≤ 原 k
184+
185+
所以最终窗口长度就是 最长连续 1(允许翻转 k 个 0)
186+
187+
## 反思
188+
189+
这种解法固然巧妙,不过很容易出错,可以作为进阶的学习对象。
190+
191+
# 参考资料
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
---
2+
title: LC1493. 删掉一个元素以后全为 1 的最长子数组 longest-subarray-of-1s-after-deleting-one-element
3+
date: 2025-08-31
4+
categories: [Leetcode-75]
5+
tags: [leetcode, Leetcode-75, sliding-window]
6+
published: true
7+
---
8+
9+
# LC1493. 删掉一个元素以后全为 1 的最长子数组 longest-subarray-of-1s-after-deleting-one-element
10+
11+
给你一个二进制数组 nums ,你需要从中删掉一个元素。
12+
13+
请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。
14+
15+
如果不存在这样的子数组,请返回 0 。
16+
17+
提示 1:
18+
19+
输入:nums = [1,1,0,1]
20+
输出:3
21+
解释:删掉位置 2 的数后,[1,1,1] 包含 3 个 1 。
22+
示例 2:
23+
24+
输入:nums = [0,1,1,1,0,1,1,0,1]
25+
输出:5
26+
解释:删掉位置 4 的数字后,[0,1,1,1,1,1,0,1] 的最长全 1 子数组为 [1,1,1,1,1]
27+
示例 3:
28+
29+
输入:nums = [1,1,1]
30+
输出:2
31+
解释:你必须要删除一个元素。
32+
33+
34+
提示:
35+
36+
1 <= nums.length <= 10^5
37+
nums[i] 要么是 0 要么是 1 。
38+
39+
# v1-暴力
40+
41+
## 思路
42+
43+
这一题和 LC1004 非常类似。
44+
45+
区别在于翻转0的数量为1,结果是删除,所以需要-1.
46+
47+
管他这么多,直接从第一个位置开始,暴力计算。
48+
49+
## 实现
50+
51+
```java
52+
public int longestOnes(int[] nums, int k) {
53+
int res = 0;
54+
55+
int n = nums.length;
56+
int zeroCount = 0;
57+
int count = 0;
58+
for(int i = 0; i < n; i++) {
59+
count = 0;
60+
zeroCount = 0;
61+
62+
for(int j = i; j < n; j++) {
63+
if(nums[j] == 0) {
64+
zeroCount++;
65+
}
66+
if(zeroCount > k) {
67+
break;
68+
}
69+
70+
count++;
71+
}
72+
73+
res = Math.max(res, count);
74+
}
75+
76+
return res;
77+
}
78+
```
79+
80+
## 效果
81+
82+
1459ms 击败 5.30%
83+
84+
## 复杂度
85+
86+
TC:最坏情况是 O(n²)
87+
88+
SC:只用到几个变量(zeroCount, count, res),所以是 O(1)
89+
90+
## 反思
91+
92+
如何进一步优化呢?
93+
94+
最大的问题在于重复的计算,如果可以复用就好了。
95+
96+
# v2-滑动窗口
97+
98+
## 思路
99+
100+
我们按照滑动窗口的思路来解决,只不过这次定的不是长度,而是 0 的总数。
101+
102+
用两个指针 left 和 right 表示窗口的左右边界。
103+
104+
窗口内 0 的个数不能超过 k。
105+
106+
每次右指针 right 右移,如果遇到 0,就增加 zeroCount。
107+
108+
当 zeroCount > k 时,左指针 left 右移,直到窗口内 0 的个数 ≤ k。
109+
110+
每次更新窗口长度,取最大值。
111+
112+
## 实现
113+
114+
```java
115+
public int longestSubarray(int[] nums) {
116+
int n = nums.length;
117+
118+
int res = 0;
119+
int zeroCount = 0;
120+
int left = 0;
121+
for(int right = 0; right < n; right++) {
122+
if(nums[right] == 0) {
123+
zeroCount++;
124+
}
125+
126+
while(zeroCount > 1) {
127+
if(nums[left] == 0) {
128+
zeroCount--;
129+
}
130+
131+
left++;
132+
}
133+
134+
res = Math.max(res, (right-left));
135+
}
136+
137+
return res;
138+
}
139+
```
140+
141+
## 效果
142+
143+
3ms 击败 42.68%
144+
145+
## 反思
146+
147+
为什么不是 100%?
148+
149+
# v3-更精简的写法
150+
151+
## 思路
152+
153+
算法本身已经复杂度最优,剩下的就是分支判断之类的优化。
154+
155+
## 实现
156+
157+
下面的方法避免了 zeroCount 和 maxLen。
158+
159+
```java
160+
public int longestSubarray(int[] nums) {
161+
int n = nums.length;
162+
int l = 0, r = 0;
163+
164+
int k = 1;
165+
while (r < n) {
166+
k -= 1 - nums[r];
167+
r++;
168+
169+
// 翻转 0 数量不够,左边收缩边界
170+
if (k < 0) {
171+
k += 1 - nums[l];
172+
l++;
173+
}
174+
}
175+
176+
return r - l - 1;
177+
}
178+
```
179+
180+
这个代码需要解释一下
181+
182+
```
183+
k -= 1 - nums[r];
184+
nums[r] 是 1 时:1 - nums[r] = 0 → k 不变
185+
nums[r] 是 0 时:1 - nums[r] = 1 → k 减 1
186+
```
187+
188+
也就是说,k 就是 剩余可翻转 0 的数量。
189+
190+
结果返回:
191+
192+
每次循环,窗口 `[l, r)` 始终满足 0 的数量 ≤ 原 k
193+
194+
`r - l - 1` 所以最终窗口长度就是 最长连续 1(删掉1个0)
195+
196+
## 反思
197+
198+
这种解法固然巧妙,不过很容易出错,可以作为进阶的学习对象。
199+
200+
# 参考资料

0 commit comments

Comments
 (0)