力扣网---滑动窗口--二月打卡

计划把力扣2月打卡徽章拿下,坚持每日打卡学习。这月是滑动窗口月。

这里写目录标题

  • 滑动窗口介绍
  • 简单题
    • 公平的糖果棒交换
    • 子数组最大平均数 I
    • 托普利茨矩阵
  • 中等
    • 尽可能使字符串相等
    • 替换后的最长重复字符
    • 最长湍流子数组
    • 爱生气的书店老板
  • 困难
    • 滑动窗口中位数

滑动窗口介绍

维护一个移动的窗口来处理在数组中运算。
避免反复的重复计算,只需要关注每次窗口移动增加和减少的数值就行了。

简单题

公平的糖果棒交换

 /**
     * 爱丽丝和鲍勃有不同大小的糖果棒:A[i] 是爱丽丝拥有的第 i 根糖果棒的大小,B[j] 是鲍勃拥有的第 j 根糖果棒的大小。
     * 

* 因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。) *

* 返回一个整数数组 ans,其中 ans[0] 是爱丽丝必须交换的糖果棒的大小,ans[1] 是 Bob 必须交换的糖果棒的大小。 *

* 如果有多个答案,你可以返回其中任何一个。保证答案存在。 *

* *

* 示例 1: *

* 输入:A = [1,1], B = [2,2] * 输出:[1,2] * 示例 2: *

* 输入:A = [1,2], B = [2,3] * 输出:[1,2] * 示例 3: *

* 输入:A = [2], B = [1,3] * 输出:[2,3] * 示例 4: *

* 输入:A = [1,2,5], B = [2,4] * 输出:[5,4] */ public static void main(String[] args) { int[] A = {1, 1}; int[] B = {2, 2}; System.out.println(Arrays.toString(fairCandySwap(A, B))); int[] A1 = {1, 2}; int[] B1 = {2, 3}; System.out.println(Arrays.toString(fairCandySwap(A1, B1))); int[] A2 = {1, 2, 5}; int[] B2 = {2, 4}; System.out.println(Arrays.toString(fairCandySwap(A2, B2))); int[] A3 = {35,17,4,24,10}; int[] B3 = {63,21}; System.out.println(Arrays.toString(fairCandySwap(A3, B3))); //24 21 } public static int[] fairCandySwap(int[] A, int[] B) { //哈希表 int sumA = Arrays.stream(A).sum(); int sumB = Arrays.stream(B).sum(); int delta = (sumA - sumB) / 2; Set<Integer> rec = new HashSet<Integer>(); for (int num : A) { rec.add(num); } int[] ans= new int[2]; for (int y:B){ int x=y+delta; if(rec.contains(x)){ ans[0]=x; ans[1]=y; break; } } return ans; }

子数组最大平均数 I

class Solution {
    public double findMaxAverage(int[] nums, int k) {
        int max= 0;
        for (int i = 0; i <k ; i++) {
            max+=nums[i];
        }
        int pre=max;
        for (int i = k; i <nums.length ; i++) {
            pre=pre-nums[i-k]+nums[i];
            max=Math.max(pre,max);
        }

        double m=max*1.0;
        return m/k;
    }
}

托普利茨矩阵

 public static void main(String[] args) {

        int[][] matrix={{1,2,3,4},
                        {5,1,2,3},
                        {9,5,1,2}};
        System.out.println(isToeplitzMatrix(matrix));

    

        int[][] matrix3={{1,2},
                         {1,2}};
        System.out.println(isToeplitzMatrix(matrix3));
    }

    public static boolean isToeplitzMatrix(int[][] matrix) {

        int m=matrix[0].length;
        int n=matrix.length;

        //前一行比后一行大一
        //斜着向上滑动,用一个数组来表示前一位数
        for (int i = -n+1; i <m ; i++) {
            int pre=-1;
            for (int j = 0; j < n; j++) {
                if(j+i>=0&j+i<m){
                   int now=matrix[j][j+i];
                   if(pre==-1){
                       pre=now;
                   }else if (pre!=now){
                       return false;
                   }
                }
            }
        }
        return true;
    }

中等

尽可能使字符串相等

 /**
     * 给你两个长度相同的字符串,s 和 t。
     *
     * 将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),
     * 也就是两个字符的 ASCII 码值的差的绝对值。
     *
     * 用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
     *
     * 如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。
     *
     * 如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。
     *
     *  
     *
     * 示例 1:
     *
     * 输入:s = "abcd", t = "bcdf", cost = 3
     * 输出:3
     * 解释:s 中的 "abc" 可以变为 "bcd"。开销为 3,所以最大长度为 3。
     * 示例 2:
     *
     * 输入:s = "abcd", t = "cdef", cost = 3
     * 输出:1
     * 解释:s 中的任一字符要想变成 t 中对应的字符,其开销都是 2。因此,最大长度为 1。
     * 示例 3:
     *
     * 输入:s = "abcd", t = "acde", cost = 0
     * 输出:1
     * 解释:你无法作出任何改动,所以最大长度为 1。
     *
     */

    public static void main(String[] args) {
        System.out.println(equalSubstring("abcd","bcdf",3)); //3
        System.out.println(equalSubstring("abcd","cdef",3)); //1
        System.out.println(equalSubstring("abcd","acde",0)); //1
        System.out.println(equalSubstring("abcd","cdef",1)); //0
        System.out.println(equalSubstring("krrgw","zjxss",19)); //2
        System.out.println(equalSubstring("krasfsfrgw","zjxasdczss",34));

    }

    public static int equalSubstring(String s, String t, int maxCost) {

        //记录每个位置变化需要的长度
        int n=s.length();
        int[] costList=new int[n];

        for (int i = 0; i <n ; i++) {
            int cost=Math.abs(s.charAt(i)-t.charAt(i));
            costList[i]=cost;
        }


       //双指针,滑动窗口
       int len=0; //窗口长度
       int total=0;
       int left=0,right=0;
       while (right<n){
              //向右扩张
              total+=costList[right];
              if(total<=maxCost){ //还在可操作数内
                  len++;
              }else {
                  total-=costList[left];
                  left++;
              }
              right++;
       }

        return len;
    }

替换后的最长重复字符

/**
     * 给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,
     * 总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
     *
     * 注意:字符串长度 和 k 不会超过 104。
     *
     * 示例 1:
     *
     * 输入:s = "ABAB", k = 2
     * 输出:4
     * 解释:用两个'A'替换为两个'B',反之亦然。
     * 示例 2:
     *
     * 输入:s = "AABABBA", k = 1
     * 输出:4
     * 解释:
     * 将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
     * 子串 "BBBB" 有最长重复字母, 答案为 4。
     *
     */
    public static void main(String[] args) {

    }


    public static int characterReplacement(String s, int k) {

        int[] num = new int[26];
        int n = s.length();
        int maxn = 0;
        int left = 0, right = 0;
        while (right < n) {
            num[s.charAt(right) - 'A']++;
            maxn = Math.max(maxn, num[s.charAt(right) - 'A']);
            if (right - left + 1 - maxn > k) {
                num[s.charAt(left) - 'A']--;
                left++;
            }
            right++;
        }
        return right - left;
        
    }

最长湍流子数组

/**
     * 当 A 的子数组 A[i], A[i+1], ..., A[j] 满足下列条件时,我们称其为湍流子数组:
     *
     * 若 i <= k < j,当 k 为奇数时, A[k] > A[k+1],且当 k 为偶数时,A[k] < A[k+1];
     * 或 若 i <= k < j,当 k 为偶数时,A[k] > A[k+1] ,且当 k 为奇数时, A[k] < A[k+1]。
     * 也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是湍流子数组。
     *
     * 返回 A 的最大湍流子数组的长度。
     *
     * 示例 1:
     *
     * 输入:[9,4,2,10,7,8,8,1,9]
     * 输出:5
     * 解释:(A[1] > A[2] < A[3] > A[4] < A[5])
     * 示例 2:
     *
     * 输入:[4,8,12,16]
     * 输出:2
     * 示例 3:
     *
     * 输入:[100]
     * 输出:1
     *  
     *
     * 提示:
     *
     * 1 <= A.length <= 40000
     * 0 <= A[i] <= 10^9
     *
     * @param args
     */
    public static void main(String[] args) {

        System.out.println(maxTurbulenceSize(new int[]{9,4,2,10,7,8,8,1,9}));

        System.out.println(maxTurbulenceSize(new int[]{4,8,12,16}));

        System.out.println(maxTurbulenceSize(new int[]{4,5}));
    }


    public static int maxTurbulenceSize(int[] arr) {
        if(arr.length==1)return 1;
        int max=arr[0]==arr[1]?1:2;
        int left=0;
        int right=1;
        int k=arr[left]>arr[right]?1:2; //0 开始  1:前大于后   2 后大于前
        while (right<arr.length-1){
            right++;
            if(arr[right-1]==arr[right]){
                 left=right;
            }else if(k==1){
                if(arr[right-1]<arr[right]){
                    k=2;
                }else {
                    left=right-1;
                }
            }else if(k==2){
                if(arr[right-1]>arr[right]){
                    k=1;
                }else {
                    left=right-1;
                }
            }
            max=Math.max(max,right-left+1);
        }
        return max;
    }

爱生气的书店老板


//今天,书店老板有一家店打算试营业 customers.length 分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。
    //
    //在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气,那么 grumpy[i] = 1,否则 grumpy[i] = 0。 当书店老板生气时,那一分钟的顾客就会不满意,不生气则他们是满意的。
    //
    //书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 X 分钟不生气,但却只能使用一次。
    //
    //请你返回这一天营业下来,最多有多少客户能够感到满意的数量。
    // 
    //
    //示例:
    //
    //输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], X = 3
    //输出:16
    //解释:
    //书店老板在最后 3 分钟保持冷静。
    //感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
    // 
    //
    //提示:
    //
    //1 <= X <= customers.length == grumpy.length <= 20000
    //0 <= customers[i] <= 1000
    //0 <= grumpy[i] <= 1
    //

    public static void main(String[] args) {

        int[] customers = {1,0,1,2,1,1,7,5}, grumpy = {0,1,0,1,0,1,0,1};
        System.out.println(maxSatisfied(customers,grumpy,3));

        int[] customers2 = {1}, grumpy2 = {0};
        System.out.println(maxSatisfied(customers2,grumpy2,1));

    }

    public static int maxSatisfied(int[] customers, int[] grumpy, int X) {
        //1,先计算不使用技能情况下会让多少人满意
        int n=customers.length;
        int ans=0;
        for (int i = 0; i <n ; i++) {
            if(grumpy[i]==0) ans+=customers[i];
        }

        //2,滑动窗口,计算在X长区间内因为发脾气而赶走客人的最大数量
        int max=0;
        int now=0;
        for (int i = 0; i <n ; i++) {
            if(i+1<=X){
                now+=customers[i]*grumpy[i];
                max=now;
            }else {
                now=now+customers[i]*grumpy[i]-customers[i-X]*grumpy[i-X];
                max=Math.max(max,now);
            }
        }

        return ans+max;
    }

困难

滑动窗口中位数

public class 滑动窗口中位数 {

//中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
    //
// 例如:
//
//
// [2,3,4],中位数是 3
// [2,3],中位数是 (2 + 3) / 2 = 2.5
//
//
// 给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗
//口中元素的中位数,并输出由它们组成的数组。
//
//
//
// 示例:
//
// 给出 nums = [1,3,-1,-3,5,3,6,7],以及 k = 3。
//
//
//窗口位置                      中位数
//---------------               -----
//[1  3  -1] -3  5  3  6  7       1
// 1 [3  -1  -3] 5  3  6  7      -1
// 1  3 [-1  -3  5] 3  6  7      -1
// 1  3  -1 [-3  5  3] 6  7       3
// 1  3  -1  -3 [5  3  6] 7       5
// 1  3  -1  -3  5 [3  6  7]      6
//
//
// 因此,返回该滑动窗口的中位数数组 [1,-1,-1,3,5,6]。
//
//
//
// 提示:
//
//
// 你可以假设 k 始终有效,即:k 始终小于等于输入的非空数组的元素个数。
// 与真实值误差在 10 ^ -5 以内的答案将被视作正确答案。
    public static void main(String[] args) {

        int[] nums={1,3,-1,-3,5,3,6,7};
        System.out.println(Arrays.toString(medianSlidingWindow(nums,3)));//[1,-1,-1,3,5,6]

        int[] nums2=  {2147483647,2147483647};
        System.out.println(Arrays.toString(medianSlidingWindow(nums2,2)));//[2147483647.0]


        int[] nums3=  {4,1,7,1,8,7,8,7,7,4};
        System.out.println(Arrays.toString(medianSlidingWindow(nums3,4)));//[2.5,4.0,7.0,7.5,7.5,7.0,7.0]
    }


    public static double[] medianSlidingWindow(int[] nums, int k) {
        int n=nums.length;
        double[] ans=new double[n-k+1];
        List<Integer> list=new ArrayList<>();
        for (int i = 0; i < k; i++) {
            list.add(nums[i]);
        }
        Collections.sort(list);
        ans[0]=getMidNum(list,k);
        for (int i = k; i <n ; i++) {
            removeFromList(list,nums[i-k]);
            add2OrderedList(list,nums[i]);
            ans[i-k+1]=getMidNum(list,k);
//            System.out.println(Arrays.toString(ans));
        }
        return ans;
    }

    private static void add2OrderedList(List<Integer> list,int num){
        for (int i = 0; i <list.size() ; i++) {
            if(list.get(i)>num){
                list.add(i,num);
                return;
            }
        }
        list.add(num);
    }

    private static void removeFromList(List<Integer> list,int num){
        for (int i = 0; i < list.size(); i++) {
            if(num==list.get(i)){
                list.remove(i);
                break;
            }
        }
    }



   public static double getMidNum(List<Integer> list,int k){
        if(k%2==0){
//           return  (list.get(k/2)+list.get(k/2-1))/2.0;
            return  list.get(k/2)/2.0+list.get(k/2-1)/2.0;
        }else {
           return list.get(k/2);
        }
   }




}

你可能感兴趣的:(算法学习,算法,leetcode,java)