【LeetCode 每日一题】2021.02

2021-02-01 【简单】 888. 公平的糖果棒交换

展开代码
class Solution {
    public int[] fairCandySwap(int[] A, int[] B) {
        int[] res = new int[2];
        Set<Integer> set = new HashSet<>();
        int sumA = 0, sumB = 0;
        for(int a: A){
            set.add(a);
            sumA += a;
        }
        for(int b: B){
            sumB += b;
        }
        int t = (sumA - sumB)/2;
        for(int b: B){
            if(!set.contains(b+t)){
                continue;
            }
            res[0] = b+t;
            res[1] = b;
        }
        return res;
    }
}

2021-02-02 【中等】 424. 替换后的最长重复字符

展开代码
// 模板题:双指针(蠕动法)
// 以每个元素为右边界,逐个找到最长的符合条件的子串长度,并记录其中的最大值
class Solution {
    public int characterReplacement(String s, int k) {
        int len = s.length();
        if(len<2 || k>=len-1)
            return len;
        char[] arr = s.toCharArray();
        int left = 0, right = 0, res = 0;
        int[] bucket = new int[26];
        int maxSub = 0;
        // 新的右边界不能超出范围,否则结束
        while(right<len){
            // 将新的右边界计数
            bucket[arr[right]-'A']++;
            // 判断新范围,是否改变了最长相同字符子序列
            maxSub = Math.max(maxSub, bucket[arr[right]-'A']);
            // 如果此时子串中去掉最长相同字符子序列,即需要更改的次数,大于最大可以更改的次数
            // 则子串不满足要求,需要移动左边界
            while(right - left + 1 - maxSub > k){
                bucket[arr[left]-'A']--;
                left++;
            }
            // 判断以当前右边界结束的符合要求的最长子串长度是否更新记录
            res = Math.max(res, right - left + 1);
            // 开始探寻新的右边界
            right++;
        }
        return res;
    }
}

2021-02-03 【困难】 480. 滑动窗口中位数

展开代码
/**
    大小顶堆 + 延迟删除
    延迟删除:当我们需要移出优先队列中的某个元素时,我们只将这个删除操作「记录」下来,而不去真的删除这个元素。当这个元素出现在堆顶时,我们再去将其移出对应的优先队列。
    保证在insert(涉及tar和堆顶的比较)、detele(涉及tar和堆顶的比较)、getMid(涉及取堆顶值)操作前,堆顶的元素都是不需要删除的。
  */
class Solution {
    public double[] medianSlidingWindow(int[] nums, int k) {
        Window window = new Window();
        double[] res = new double[nums.length - k + 1];
        for(int i = 0; i<k; i++){
            window.insert(nums[i]);
        }
        res[0] = window.getMid();
        for(int i = k; i<nums.length; i++){
            window.insert(nums[i]);
            window.delete(nums[i-k]);
            res[i-k+1] = window.getMid();
        }
        return res;
    }
}

class Window{
    // 大顶堆,维护较小的一半元素
    private PriorityQueue<Integer> small;
    // 小顶堆,维护较大的一半元素
    private PriorityQueue<Integer> large;
    // small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素
    private int smallSize, largeSize;
    // 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数
    private Map<Integer, Integer> delayed;

    public Window(){
        // 默认是小顶堆        
        large = new PriorityQueue<>();
        // 重写Comparator设置为大顶堆
        small = new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer x, Integer y){
                // 注意这里不能用y-x,否则会出现超出范围
                return y.compareTo(x);
            }
        });
        delayed = new HashMap<>();
        smallSize = 0;
        largeSize = 0;
    }

    public void insert(int tar){
        if(small.isEmpty() || tar <= small.peek()){
            small.offer(tar);
            smallSize++;
        }
        else{
            large.offer(tar);
            largeSize++;
        }
        balance();
    }

    public void delete(int tar){
        delayed.put(tar, delayed.getOrDefault(tar, 0) + 1);
        if(tar <= small.peek()){
            smallSize--;
            tryClear(small);
        }
        else{
            largeSize--;
            tryClear(large);
        }
        balance();
    }

    /**
        尝试清理。发生在:
        1. 刚刚产生新的延迟删除对象时,可以尝试是否能直接清理。
        2. 在任何一个队列有poll操作后(balance与tryClear本身中出现),
           顶都有可能是需要延迟删除的元素,因此需要尝试清理。
      */ 
    private void tryClear(PriorityQueue<Integer> q){
        int tar;
        while(!q.isEmpty()){
            tar = q.peek();
            if(delayed.containsKey(tar)){
                q.poll();
                delayed.put(tar, delayed.get(tar)-1);
                if(delayed.get(tar) == 0)
                    delayed.remove(tar);
            }
            else
                break;
        }
    }

    public double getMid(){
        if(largeSize == 0 || (smallSize + largeSize)%2==1){
            return (double)small.peek();
        }
        return ((double)small.peek() + large.peek())/2;
    }

    private void balance(){
        if(smallSize == largeSize || smallSize == largeSize+1)
            return;
        while(smallSize > largeSize + 1){
            large.offer(small.poll());
            smallSize--;
            largeSize++;
            tryClear(small);
        }
        while(largeSize > smallSize){
            small.offer(large.poll());
            smallSize++;
            largeSize--;
            tryClear(large);
        }
    }
}

2021-02-04 【简单】 643. 子数组最大平均数 I

展开代码
class Solution {
    public double findMaxAverage(int[] nums, int k) {
        double res = Integer.MIN_VALUE;
        Window window = new Window();
        for(int i = 0; i<k; i++){
            window.insert(nums[i]);
        }
        res = Math.max(res, window.getAvg());
        for(int i = k; i<nums.length; i++){
            window.insert(nums[i]);
            window.delete(nums[i-k]);
            res = Math.max(res, window.getAvg());
        }
        return res;
    }
}

class Window{
    int count;
    double sum;
    public Window(){
        count = 0;
        sum = 0;
    }
    public void insert(int tar){
        sum += tar;
        count++;
    }
    public void delete(int tar){
        sum -= tar;
        count--;
    }
    public double getAvg(){
        return sum/count;
    }
}

2021-02-05 【中等】 1208. 尽可能使字符串相等

展开代码
class Solution {
    public int equalSubstring(String s, String t, int maxCost) {
        int len = s.length();
        int[] diff = new int[len];
        for(int i = 0; i<len; i++){
            diff[i] = Math.abs(s.charAt(i) - t.charAt(i));
        }

        int l = 0, r = 0, sum = 0, maxLen = 0;
        while(r<len){
            sum += diff[r];
            while(sum > maxCost){
                sum -= diff[l];
                l++;
            }
            maxLen = Math.max(r-l+1, maxLen);
            r++;
        }
        return maxLen;
    }
}

2021-02-06 【中等】 1423. 可获得的最大点数

展开代码
// 将左右两个滑动窗口之和最大值的问题,变成中间滑动窗口最小值的问题
class Solution {
    public int maxScore(int[] cardPoints, int k) {
        int n = cardPoints.length;
        int size = n - k;
        int sum = 0;
        for (int i = 0; i < size; i++) {
            sum += cardPoints[i];
        }
        int minSum = sum, total = sum;
        for (int i = size; i < n; i++) {
            total += cardPoints[i];
            sum += cardPoints[i] - cardPoints[i - size];
            minSum = Math.min(minSum, sum);
        }
        return total - minSum;
    }
}

2021-02-07 【简单】 665. 非递减数列

展开代码
class Solution {
    public boolean checkPossibility(int[] nums) {
        int len = nums.length;
        int count = 1;
        if(len<=2){
            return true;
        }
        if(nums[0]>nums[1]){
            nums[0] = nums[1];
            count--;
        }
        for(int i = 2; i<len; i++){
            if(nums[i-1] <= nums[i])
                continue;
            if(count==0)
                return false;
            if(nums[i] >= nums[i-2])
                nums[i-1] = nums[i-2];
            else
                nums[i] = nums[i-1];
            count--;
        }
        return true;
    }
}

2021-02-08 【中等】 978. 最长湍流子数组

展开代码
// 双指针蠕动。本题左边界不是步进的,而且直接更新到right-1/right处。
class Solution {
    public int maxTurbulenceSize(int[] arr) {
        int len = arr.length;
        if(len<=1)
            return len;
        int res = 1, left = 0, right = 1, cur = 1;
        int flag = 0;
        while(right < len){
            if(arr[right] == arr[right-1]){
                left = right;
                cur = 1;
                flag = 0;
            }
            else if((arr[right]>arr[right-1] && flag < 0) || (arr[right]<arr[right-1] && flag > 0)){
                left = right - 1;
                cur = 2;
            }
            else { 
                if(flag == 0)
                    flag = arr[right] > arr[right-1] ? -1: 1;
                else
                    flag = flag > 0 ? -1: 1;
                cur++;
                res = Math.max(right - left + 1, res);
            }
            right++;
        }
        return res;
    }
}

2021-02-09 【困难】 992. K 个不同整数的子数组

展开代码
// 恰好k 转换成 (最多k)-(最多k-1)
// 最多k使用双指针蠕动
class Solution {
    public int subarraysWithKDistinct(int[] A, int k) {
        int a = atMostKDistinct(A, k);
        int b = atMostKDistinct(A, k-1);
        return a-b;
    }
    private int atMostKDistinct(int[] A, int k){
        int len = A.length;
        int res = 0, l = 0, r = 0, d = 0;
        int[] bucket = new int[len+1];
        while(r<len){
            if(bucket[A[r]]==0)
                d++;
            bucket[A[r]]++;

            while(d>k){
                bucket[A[l]]--;
                if(bucket[A[l]]==0)
                    d--;
                l++;
            }

            res += r - l;
            r++;
        }
        return res;
    }
}
posted @ 2021-02-03 15:29  BWSHOOTER  阅读(52)  评论(0)    收藏  举报