2020.12.17

java学习 专栏收录该内容
3 篇文章 0 订阅

2020.12.17

1.无重复字符的最长子串(leetcode3)

思路:使用滑动窗口机制

设置右指针移动,其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!

如何移动?我们只要把队列的左边的元素移出就行了,直到满足题目要求!

一直维持这样的队列,找出队列出现最长的长度时候,求出解!

public int lengthOfLongestSubstring(String s) {
        //HashMap<Character, Integer> map = new HashMap<>();
    int[] map = new int[128];//使用int数组可以当做map使用
    int maxLen = 0;//用于记录最大不重复子串的长度
    int left = 0;//滑动窗口左指针
    for (int i = 0; i < s.length() ; i++)
    {
            /**
            1、首先,判断当前字符是否包含在map中,如果不包含,将该字符添加到map(字符,字符在数组下标),
             此时没有出现重复的字符,左指针不需要变化。此时不重复子串的长度为:i-left+1,与原来的maxLen比较,取最大值;

            2、如果当前字符 ch 包含在 map中,此时有2类情况:
             1)当前字符包含在当前有效的子段中,如:abca,当我们遍历到第二个a,当前有效最长子段是 abc,我们又遍历到a,
             那么此时更新 left 为 map.get(a)+1=1,当前有效子段更新为 bca;
             2)当前字符不包含在当前最长有效子段中,如:abba,我们先添加a,b进map,此时left=0,我们再添加b,发现map中包含b,
             而且b包含在最长有效子段中,就是1)的情况,我们更新 left=map.get(b)+1=2,此时子段更新为 b,而且map中仍然包含a,map.get(a)=0;
             随后,我们遍历到a,发现a包含在map中,且map.get(a)=0,如果我们像1)一样处理,就会发现 left=map.get(a)+1=1,实际上,left此时
             应该不变,left始终为2,子段变成 ba才对。

             为了处理以上2类情况,我们每次更新left,left=Math.max(left , map.get(ch)+1).
             另外,更新left后,不管原来的 s.charAt(i) 是否在最长子段中,我们都要将 s.charAt(i) 的位置更新为当前的i,
             因此此时新的 s.charAt(i) 已经进入到 当前最长的子段中!
             */
        
        //if(map.containsKey(s.charAt(i)))
        if (map[s.charAt(i)] != -1)
        {
            left = Math.max(left, map[s.charAt(i)]+1);
            //left = Math.max(left , map.get(s.charAt(i))+1);
        }
            //不管是否更新left,都要更新 s.charAt(i) 的位置!
        map.put(s.charAt(i) , i);
        maxLen = Math.max(maxLen , i-left+1);
    }
        
    return maxLen;
}

2.最小覆盖子串(leetcode76)

思路:使用滑动窗口机制

  1. 设置右指针right移动,使滑动窗口增大,直到窗口包含了T的所有元素。
  2. 不断增加左指针left使滑动窗口缩小,并查看窗口是否包含所有元素。如果满足则记录此时滑动窗口的长度,并保存最小值
  3. left再增加一个位置,这个时候滑动窗口肯定不满足条件了,那么继续从步骤一开始执行,寻找新的满足条件的滑动窗口,如此反复,直到right超出了字符串S范围。

其中,需要注意的是:判断是否包含所有元素的方法是利用两个hashmap集合实现(也可以直接用数组表示),key为字符,value为字符的次数。满足条件对应的是window集合中元素个数要大于等于need集合中元素个数,在这里利用valid来判断是否满足条件。


class Solution {
    public String minWindow(String s, String t) {
        if (s == null || s.length() < t.length())
            return "";
        HashMap<Character, Integer> need = new HashMap<>();
        HashMap<Character, Integer> window = new HashMap<>();
        for (Character ch : t.toCharArray()) {
            need.put(ch, need.getOrDefault(ch,0)+1);
        }
        int left = 0;
        int valid = 0;
        int len = Integer.MAX_VALUE;
        int start = 0;
        for (int right=0;right<s.length();right++) {
            Character c1 = s.charAt(right);
            if (need.containsKey(c1)) {
                window.put(c1, window.getOrDefault(c1,0)+1);
                if (need.get(c1).equals(window.get(c1))) {
                    valid++;
                }
            }
            while (valid == need.size() && left<=right) {
                if (right-left<len) {
                    start = left;
                    len = right-left+1;
                }
                Character c2 = s.charAt(left);
                if (need.containsKey(c2)) {
                    window.put(c2, window.getOrDefault(c2,0)-1);
                    if (window.get(c2) < need.get(c2)) {
                        valid--;
                    }
                }
                left++;
            }
        }
        return len==Integer.MAX_VALUE ? "" : s.substring(start, start+len);
    }
}

3.滑动窗口最大值(leetcode239)

思路:使用单调递减栈实现,即双端队列。

  1. 遍历数组,将数组对应的下标存入队列中。
  2. 数组需要按照数(即下标对应数组中的值)从大到小排列,如果当前遍历的数比队尾的值大,则需要弹出队尾值,直到队列重新满足从大到小的要求。
  3. 每次取队列头部即窗口最大值。如果头部位置已经超出窗口左边界的话,则移除头部。
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length < 2) return nums;
        // 双向队列 保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
        LinkedList<Integer> queue = new LinkedList();
        // 结果数组
        int[] result = new int[nums.length-k+1];
        // 遍历nums数组
        for(int i = 0;i < nums.length;i++){
            // 保证从大到小 如果前面数小则需要依次弹出,直至满足要求
            while(!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            // 添加当前值对应的数组下标
            queue.addLast(i);
            // 判断当前队列中队首的值是否有效
            if(queue.peek() <= i-k){
                queue.poll();   
            } 
            // 当窗口长度为k时 保存当前窗口中最大值
            if(i+1 >= k){
                result[i+1-k] = nums[queue.peek()];
            }
        }
        return result;
    }
}


  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值