题目
滑动窗口
public class Main {
public static void main(String[] args) {
Main main = new Main();
int [] nums = new int[] {0, 0, 0, 1, 0, 1, 1, 0};
main.minKBitFlips(nums, 3);
}
public int minKBitFlips(int[] A, int K) {
// 利用队列判断当前位置的元素是否需要翻转
Queue<Integer> queue = new LinkedList<>();
int count = 0;
// 遍历A数组
for (int i = 0; i < A.length; i ++) {
if (queue.size() > 0) {
// 如果队列中有元素,就表明队列的头部元素的值(对应数组中的索引), 加上K包含当前i, 说明当前位置i被翻转过一次
// 那么在头部元素之后的值, 肯定就包含当前i
// 所以队列的长度就是当前位置i被翻转过的次数
if ((A[i] + queue.size()) % 2 == 0 ) {
// 翻转一次就代表当前数字加1, 如果最后为偶数, 表示当前位置需要翻转
if (i + K <= A.length) {
queue.offer(i);
count ++;
} else {
return -1;
}
}
// 判断队列的头部元素是否需要被移除
if (queue.peek() + K == i + 1) {
// 弹出顶部元素
queue.poll();
}
} else {
// 队列如果为空 只跟当前值有关
if (A[i] == 0) {
if (i + K <= A.length) {
if (K > 1) {
// 只有K > 1才有使用队列的必要
queue.offer(i);
}
count ++;
} else {
return -1;
}
}
}
}
return count;
}
}
基本思路
-
以长度K为单位进行翻转
-
每个位置的翻转情况, 只和当前位置之前元素的翻转有关, 和之后的无关, 因此可以从左向右遍历数字, 逐个判断每个位置的翻转情况
-
以某个索引下标为起始的翻转只应该进行一次, 因为如果某个索引开头的子数组被翻转两次, 那么该索引位置的元素相当于没翻转. 例如[0, 0, 1]这个数组, 当K为2时, 以索引1开头的数组, 只会进行一次翻转, 也就是翻转[0, 1], 但是不代表索引1的元素只会被翻转一次, 因为如果我翻转[0, 0]索引1被翻转了一次, 我再翻转索引1开始的子数组, 索引1就相当于翻转了两次.
-
按顺序从左到右翻转, 和你乱序翻转, 最后翻转次数是一致的
-
如果你遵循条件3, 那么你得到的翻转次数就是最小次数
-
并不真正的去翻转元素, 而是记录每个元素曾经被翻转的次数, 再根据当前元素的值来判断该位置的元素是否需要被翻转(例如队列来实现判断)