目录
- 一、题目
- 二、思路
- 2.1 解题思路
- 2.2 代码尝试
- 2.3 疑难问题
- 三、解法
- 代码逻辑回顾
- 示例运行过程
- 初始状态:
- 遍历过程:
- 最终结果
- 总结
- 四、收获
- 4.1 心得
- 4.2 举一反三
一、题目
二、思路
2.1 解题思路
就是滑动窗口一个一个遍历,遇到情况就翻转。最后检查所有的是否还出现0.
2.2 代码尝试
class Solution {
public:int minKBitFlips(vector<int>& nums, int k) {int sum=0;int t=k;for(int i=0;i<=nums.size()-k;i++){if(nums[i]==1){continue;}else{if(k==1){nums[i]=1;}else{t=k;while(t>0){nums[i+t-1] & 1;t--;}}sum++;}}if(nums[nums.size()-1]==0){return -1;}return sum;}
};
翻转操作未正确实现:
你在 while(t>0) 循环中对 nums[i+t-1] 进行了按位与操作 & 1,但这个操作并没有实际改变 nums[i+t-1] 的值。你应该是想对数组元素进行翻转(0变1,1变0),但你没有实现这个逻辑。
边界条件处理不足:
你在最后检查了 nums[nums.size()-1] 是否为0,但如果在数组的末尾有多个0,你的代码可能无法正确处理。
这种就没法判断
时间复杂度较高:
你的代码在最坏情况下时间复杂度为 O(n*k),这在某些情况下可能会导致性能问题。
2.3 疑难问题
感觉思路没问题,但有边界问题
如何进行翻转0-1操作?如何减小时间复杂度?for循环太多了,感觉思路上还可以优化
三、解法
class Solution {
public:int minKBitFlips(vector<int>& nums, int k) {int n = nums.size();vector<int> diff(n + 1);int ans = 0, revCnt = 0;for (int i = 0; i < n; ++i) {revCnt += diff[i];if ((nums[i] + revCnt) % 2 == 0) {if (i + k > n) {return -1;}++ans;++revCnt;--diff[i + k];}}return ans;}
};作者:力扣官方题解
链接:https://leetcode.cn/problems/minimum-number-of-k-consecutive-bit-flips/solutions/607416/k-lian-xu-wei-de-zui-xiao-fan-zhuan-ci-s-bikk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
好的!我们以数组 nums = [0, 0, 0, 1, 0, 1, 1, 0]
和 k = 3
为例,逐步展示代码中主要变量的变化过程。
代码逻辑回顾
-
diff
数组:- 用于记录翻转操作的影响范围。
diff[i]
表示从位置i
开始的翻转操作的影响。
-
revCnt
:- 记录当前位置
i
的翻转次数(即当前处于多少次翻转的影响下)。
- 记录当前位置
-
ans
:- 记录总的翻转次数。
-
核心逻辑:
- 遍历数组,检查当前元素是否需要翻转。
- 如果需要翻转,则更新
revCnt
和diff
,并增加ans
。
示例运行过程
初始状态:
nums = [0, 0, 0, 1, 0, 1, 1, 0]
n = 8
k = 3
diff = [0, 0, 0, 0, 0, 0, 0, 0, 0] // 大小为 n + 1
ans = 0
revCnt = 0
遍历过程:
-
i = 0:
revCnt += diff[0]
→revCnt = 0
nums[0] + revCnt = 0 + 0 = 0
,0 % 2 == 0
,需要翻转。- 检查
i + k = 3
是否越界 →3 <= 8
,可以翻转。 - 更新:
ans = 1
revCnt = 1
diff[3] = -1
- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, 0, 0] ans = 1 revCnt = 1
-
i = 1:
revCnt += diff[1]
→revCnt = 1 + 0 = 1
nums[1] + revCnt = 0 + 1 = 1
,1 % 2 != 0
,不需要翻转。- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, 0, 0] ans = 1 revCnt = 1
-
i = 2:
revCnt += diff[2]
→revCnt = 1 + 0 = 1
nums[2] + revCnt = 0 + 1 = 1
,1 % 2 != 0
,不需要翻转。- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, 0, 0] ans = 1 revCnt = 1
-
i = 3:
revCnt += diff[3]
→revCnt = 1 + (-1) = 0
nums[3] + revCnt = 1 + 0 = 1
,1 % 2 != 0
,不需要翻转。- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, 0, 0] ans = 1 revCnt = 0
-
i = 4:
revCnt += diff[4]
→revCnt = 0 + 0 = 0
nums[4] + revCnt = 0 + 0 = 0
,0 % 2 == 0
,需要翻转。- 检查
i + k = 7
是否越界 →7 <= 8
,可以翻转。 - 更新:
ans = 2
revCnt = 1
diff[7] = -1
- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, -1, 0] ans = 2 revCnt = 1
-
i = 5:
revCnt += diff[5]
→revCnt = 1 + 0 = 1
nums[5] + revCnt = 1 + 1 = 2
,2 % 2 == 0
,需要翻转。- 检查
i + k = 8
是否越界 →8 <= 8
,可以翻转。 - 更新:
ans = 3
revCnt = 2
diff[8] = -1
- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, -1, -1] ans = 3 revCnt = 2
-
i = 6:
revCnt += diff[6]
→revCnt = 2 + 0 = 2
nums[6] + revCnt = 1 + 2 = 3
,3 % 2 != 0
,不需要翻转。- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, -1, -1] ans = 3 revCnt = 2
-
i = 7:
revCnt += diff[7]
→revCnt = 2 + (-1) = 1
nums[7] + revCnt = 0 + 1 = 1
,1 % 2 != 0
,不需要翻转。- 状态:
diff = [0, 0, 0, -1, 0, 0, 0, -1, -1] ans = 3 revCnt = 1
最终结果
- 总翻转次数
ans = 3
。 - 最终数组状态(如果实际翻转):
nums = [1, 1, 1, 1, 1, 1, 1, 1]
总结
通过这个例子,我们可以看到:
diff
数组用于记录翻转操作的影响范围。revCnt
用于动态计算当前位置的翻转次数。- 每次翻转操作都会更新
diff
和revCnt
,从而高效地处理区间翻转。
希望这个详细的例子能帮助你理解代码的运行过程!如果还有疑问,欢迎继续提问!
四、收获
4.1 心得
0-1翻转可以使用异或操作
^= 1
太难了,这道题
用一个标记符号和差分数组。差分数组的累加代表当前元素值
当前元素与标记符号的和,如果是偶数,代表需要翻转。标记符号是1,代表已经翻转的状态。如果元素为1,。标记符号为0,代表未翻转,若元素为0,那么就需要进行翻转的操作,并将范围之外的第一个元素的差分数组置为-1。因为置为-1,这样范围之外的第一个元素与标记符相加,才能重新使得标记符变为0,回到未翻转的状态。
这么精妙的思路,第一次做完全想不到。我只会暴力贪心。但是感觉标记符号和差分数组也是一种模板的公式化解题思路,可能做多了就知道。
4.2 举一反三
遇到0-1翻转的这种需要频繁更新值的,使用标记符号和差分数组