滑动窗口(单调双端队列实现)
滑动窗口是想象出来的一种数据结构:
- 滑动窗口有左边界L和右边界R
- 在数组或字符串或一个序列上,记为S,窗口就是S[L…R]这一部分
- R往右滑,意味着一个样本进了窗口,L往右滑意味着一个样本处了窗口
- L和R只能往右滑
-
- R往右 —> R++ —> 一个样本进了窗口
- L往右 —> L++ —> 一个样本出窗口
- L <= R
- 双端队列中只放位置
- 数据状况和问题本身建立单调性
/**
* 给的一个数组arr,窗口大小为w,求每个窗口中的最大值
*
* @param arr
* @param w
* @return
*/
public int[] slidwindow(int[] arr, int w) {
if (arr == null || w < 1 || w < arr.length) {
return null;
}
//其中放的是位置,头代表 (大->小)尾
int[] res = new int[arr.length - w + 1];
int index = 0;
LinkedList<Integer> qmax = new LinkedList<>();
// L...R
// 当前让 R 进窗口
for (int R = 0; R < arr.length; R++) {
// R -> 值 可以放在比他大的数后,或者空
// 当双端队列不为空的并且双端队列的最后一个数 <= arr[R] 的时候,弹出元素
while (!qmax.isEmpty() && qmax.peekLast() <= arr[R]) {
qmax.pollLast();
}
// arr[R]进队列
qmax.addLast(arr[R]);
// 如果窗口没有形成W的长度之前,不弹出数字的
if (qmax.peekFirst() == R - w) {
qmax.pollFirst();
}
// 以上窗口更新做完了,更新数组
if (R >= w - 1) {
res[index++] = arr[qmax.peekFirst()];
}
}
return res;
}
/**
* LeetCode3. 无重复字符的最长子串
* 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
* <p>
* 示例 1:
* <p>
* 输入: "abcabcbb"
* 输出: 3
* 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
* <p>
* 示例 2:
* <p>
* 输入: "bbbbb"
* 输出: 1
* 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
* <p>
* 示例 3:
* <p>
* 输入: "pwwkew"
* 输出: 3
* 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
* 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
* <p>
* 来源:力扣(LeetCode)
* 链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*
* @param s
* @return
*/
public int lengthOfLongestSubstring(String s) {
if (s == null) {
return 0;
}
char[] chars = s.toCharArray();
int max = 0;
int L = -1;
Deque deque = new ArrayDeque();
for (int R = 0; R < chars.length; R++) {
//如果当前的窗口L位置不为0,则弹出当前位置的元素,进行尝试下一个位置
if (R != 0) {
deque.pollFirst();
}
// 判断当前窗口的右+1位置,是否存在于双端队列中,如果存在,说明重复,
// 当前位置的不重复长度即为 双端对列中元素的个数
while (L + 1 < chars.length && !deque.contains(chars[L + 1])) {
deque.addLast(chars[L + 1]);
L++;
}
//对比最大值
max = max > deque.size() ? max : deque.size();
}
return max;
}