题目链接:https://leetcode.cn/problems/sliding-window-maximum/description/?envType=study-plan-v2&envId=top-100-liked
思路:求滑动窗口内的最大值,自然要维护一个滑动窗口,这里我们使用一个队列来表示滑动窗口,既然要记录最大值,那么我们在添加元素时,自然要如果队尾元素小于当前元素,队尾元素就应该出队,直到没有小于当前元素的存在,然后把当前元素入队,这样队头位置就是滑动窗口内的最大值。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
Queue queue = new Queue();
for (int i = 0; i < k; i++) {
queue.push(nums[i]);
}
int j = 0;
res[j++] = queue.getMax();
for (int i = k; i < nums.length; i++) {
queue.pop(nums[i-k]);
queue.push(nums[i]);
res[j++] = queue.getMax();
}
return res;
}
class Queue {
LinkedList<Integer> stack = new LinkedList<>();
void push(int n) {
while (!stack.isEmpty() && n > stack.getLast()) {
stack.pollLast();
}
stack.addLast(n);
}
void pop(int n) {
if (n == stack.getFirst()) {
stack.pollFirst();
}
}
int getMax() {
return stack.getFirst();
}
}
}
题目链接:https://leetcode.cn/problems/minimum-window-substring/?envType=study-plan-v2&envId=top-100-liked
思路:最小覆盖子串问题,很显然是滑动窗口,如下代码就是滑动窗口的模板。首先是一直扩大窗口,当窗口满足条件后,就开始缩小窗口,直到窗口不再满足条件,即又开始扩大窗口。
class Solution {
public String minWindow(String s, String t) {
Map<Character, Integer> need = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
for(char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0, min = Integer.MAX_VALUE, vaild = 0;
int[] xy = new int[2];
while(right < s.length()) {
char rc = s.charAt(right);
right++;
if(need.containsKey(rc)) {
window.put(rc, window.getOrDefault(rc, 0)+1);
if(window.get(rc).equals(need.get(rc))) {
vaild++;
}
}
while(vaild == need.size()) {
int len = right-left;
if(len < min) {
min = len;
xy[0] = left;
xy[1] = right;
}
char lc = s.charAt(left);
left++;
if(need.containsKey(lc)) {
if(window.get(lc).equals(need.get(lc))) {
vaild--;
}
window.put(lc, window.get(lc)-1);
}
}
}
return min == Integer.MAX_VALUE ? "" : s.substring(xy[0], xy[1]);
}
}
题目链接:https://leetcode.cn/problems/maximum-subarray/description/?envType=study-plan-v2&envId=top-100-liked
思路:本题可以使用贪心算法和动态规划,贪心如下,如果子数组和大于零,就一直加并记录最大值,这个过程中是肯能达到最大值的,只要子数组和小于零,就可以另起一个新子数组了。
贪心算法:
class Solution {
public int maxSubArray(int[] nums) {
int max = Integer.MIN_VALUE, sum = 0;
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
max = Math.max(max, sum);
if(sum < 0) {
sum = 0;
}
}
return max;
}
}
动态规划:定义dp[i]表示区间[0, i]中以nums[i]为结尾的最大子数组和,动态规划就是选择,可以选择是否以讲当前元素加入左边的子数组,不加入就单开一个子数组。dp[i] = Math.max(dp[i-1]+nums[i], nums[i]);
class Solution {
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int max = nums[0];
for(int i = 1; i < nums.length; i++) {
dp[i] = Math.max(dp[i-1]+nums[i], nums[i]);
max = Math.max(max, dp[i]);
}
return max;
}
}
题目链接:https://leetcode.cn/problems/merge-intervals/description/?envType=study-plan-v2&envId=top-100-liked
思路:合并区间也是一个经典题目,只需要把左边界排序,只要有重叠的,就合并,无重叠就保存之前的区间,然后新开一个区间。
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> a[0]-b[0]);
List<int[]> list = new ArrayList<>();
int left = intervals[0][0], right = intervals[0][1];
for(int i = 1; i < intervals.length; i++) {
if(intervals[i][0] <= right) {
right = Math.max(right, intervals[i][1]);
}else {
list.add(new int[]{left, right});
left = intervals[i][0];
right = intervals[i][1];
}
}
list.add(new int[]{left, right});
int[][] res = new int[list.size()][2];
for(int i = 0; i < list.size(); i++) {
res[i] = list.get(i);
}
return res;
}
}
题目链接:https://leetcode.cn/problems/rotate-array/description/?envType=study-plan-v2&envId=top-100-liked
思路:nums = [1,2,3,4,5,6,7], k = 3,要求向右移动数组,且是循环的,很经典的题目,直接全部翻转,然后再分段翻转。
class Solution {
public void rotate(int[] nums, int k) {
k = k % nums.length;
reverse(nums, 0, nums.length-1);
reverse(nums, 0, k-1);
reverse(nums, k, nums.length-1);
}
void reverse(int[] nums, int i, int j) {
while(i < j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++;
j--;
}
}
}