力扣hot100第二天

子串

239.滑动窗口最大值

题目

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
力扣hot100第二天_第1张图片

代码

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n=nums.length;
int[] ans = new int[n-k+1];
Deque q= new ArrayDeque<>();
for(int i=0;i while(!q.isEmpty()&&nums[q.getLast()]<=nums[i]){
q.removeLast();
}
q.addLast(i);
if(i-q.getFirst()>=k){//如果当前索引 i 减去队首元素的索引大于或等于窗口大小 k,则说明队首元素已经不在当前窗口内了。
q.removeFirst();
}
if(i>=k-1){//即从索引 0 到 k-1
ans[i-k+1] = nums[q.getFirst()];//对于当前索引 i,滑动窗口的起点是 i - k + 1
}
}
return ans;
}
}

76.最小覆盖子串

题目

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
    力扣hot100第二天_第2张图片### 代码
    class Solution {
    public String minWindow(String s, String t) {
    if (s == null || s == “” || t == null || t == “” || s.length() < t.length()) {
    return “”;
    }
    //维护两个数组,记录已有字符串指定字符的出现次数,和目标字符串指定字符的出现次数
    //ASCII表总长128
    int[] need = new int[128];
    int[] have = new int[128];

      //将目标字符串指定字符的出现次数记录
      for (int i = 0; i < t.length(); i++) {
          need[t.charAt(i)]++;
      }
    
      //分别为左指针,右指针,最小长度(初始值为一定不可达到的长度)
      //已有字符串中目标字符串指定字符的出现总频次以及最小覆盖子串在原字符串中的起始位置
      int left = 0, right = 0, min = s.length() + 1, count = 0, start = 0;
      while (right < s.length()) {
          char r = s.charAt(right);
          //说明该字符不被目标字符串需要,此时有两种情况
          // 1.循环刚开始,那么直接移动右指针即可,不需要做多余判断
          // 2.循环已经开始一段时间,此处又有两种情况
          //  2.1 上一次条件不满足,已有字符串指定字符出现次数不满足目标字符串指定字符出现次数,那么此时
          //      如果该字符还不被目标字符串需要,就不需要进行多余判断,右指针移动即可
          //  2.2 左指针已经移动完毕,那么此时就相当于循环刚开始,同理直接移动右指针
          if (need[r] == 0) {
              right++;
              continue;
          }
          //当且仅当已有字符串目标字符出现的次数小于目标字符串字符的出现次数时,count才会+1
          //是为了后续能直接判断已有字符串是否已经包含了目标字符串的所有字符,不需要挨个比对字符出现的次数
          if (have[r] < need[r]) {
              count++;
          }
          //已有字符串中目标字符出现的次数+1
          have[r]++;
          //移动右指针
          right++;
          //当且仅当已有字符串已经包含了所有目标字符串的字符,且出现频次一定大于或等于指定频次
          while (count == t.length()) {
              //挡窗口的长度比已有的最短值小时,更改最小值,并记录起始位置
              if (right - left < min) {
                  min = right - left;
                  start = left;
              }
              char l = s.charAt(left);
              //如果左边即将要去掉的字符不被目标字符串需要,那么不需要多余判断,直接可以移动左指针
              if (need[l] == 0) {
                  left++;
                  continue;
              }
              //如果左边即将要去掉的字符被目标字符串需要,且出现的频次正好等于指定频次,那么如果去掉了这个字符,
              //就不满足覆盖子串的条件,此时要破坏循环条件跳出循环,即控制目标字符串指定字符的出现总频次(count)-1
              if (have[l] == need[l]) {
                  count--;
              }
              //已有字符串中目标字符出现的次数-1
              have[l]--;
              //移动左指针
              left++;
          }
      }
      //如果最小长度还为初始值,说明没有符合条件的子串
      if (min == s.length() + 1) {
          return "";
      }
      //返回的为以记录的起始位置为起点,记录的最短长度为距离的指定字符串中截取的子串
      return s.substring(start, start + min);
    

    }
    }

普通数组

53.最大子数组和

题目

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
题意:找出所有子数组中元素和最大的值

代码

class Solution {
public int maxSubArray(int[] nums) {
if (nums.length == 1){
return nums[0];
}
int sum = Integer.MIN_VALUE;
int count = 0;
for (int i = 0; i < nums.length; i++){
count += nums[i];
sum = Math.max(sum, count); // 取区间累计的最大值(相当于不断确定最大子序终止位置)
if (count <= 0){
count = 0; // 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
}
}
return sum;
}
}
动态规划思路:遍历数组,每次检查以当前位置为结尾的子数组中的最大子数组和为多少。以dp[i]表示第i个数为结尾的最大子数组和,那么dp[i] = max(dp[i-1]+nums[i], nums[i]),甚至可以优化掉dp数组,因为dp是一维且有序的。
class Solution {
public int maxSubArray(int[] nums) {
int dp = nums[0];
int ans = nums[0];
for (int i = 1; i < nums.length; i++) {
dp = Math.max(dp + nums[i], nums[i]);
ans = Math.max(ans, dp);
}
return ans;
}
}

56.合并区间

题目

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104

题意:给定一系列区间,将这些区间重叠的地方合并
思路:先排序后直接合并即可,难点就是需要先排序,排序之后像打卡题,没有思维的难度

代码

class Solution {
public int[][] merge(int[][] intervals) {
// 排序
Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));//a代表intervals数组中的每个子数组
List ans = new ArrayList<>();

    for (int i = 0; i < intervals.length; i++) {
        // ans 为空则直接添加
        if (ans.isEmpty()) {
            ans.add(intervals[i]);
        } else {
            int lastTo = ans.get(ans.size() - 1)[1];//获取ans中最后一个区间的结束位置lastTo
            int from = intervals[i][0], to = intervals[i][1];
            if (lastTo >= from) // 相交则更新 ans 中的 lastTo
                ans.get(ans.size() - 1)[1] = Math.max(lastTo, to);
            else { // 不相交则添在尾部
                ans.add(intervals[i]);
            }
        }
    }

    return ans.toArray(new int[ans.size()][]);

}

}

第一轮:intervals[0] = {1, 3},因为ans为空,直接添加到ans中。

ans = [{1, 3}]

第二轮:intervals[1] = {2, 6}

lastTo = 3,from = 2,to = 6
因为lastTo >= from,所以更新ans中最后一个区间的结束位置为Math.max(3, 6) = 6
ans = [{1, 6}]

189.轮转数组

题目

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
力扣hot100第二天_第3张图片
题意:将所有数组向右平移k的位置
思路:使用反转代替平移,分别反转整个数组,0k-1,kn-1即可

代码

class Solution {
public void rotate(int[] nums, int k) {
k = k % nums.length;//k等于或大于数组长度,实际上只需要旋转k % nums.length次
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int t = nums[start];
nums[start] = nums[end];
nums[end] = t;
++start;
–end;
}
}
}

你可能感兴趣的:(leetcode,算法,职场和发展)