数组

79 阅读2分钟

1. 前缀和

1.1 和为 K 的子数组

  • 方法一:暴力枚举
   public int subarraySum(int[] nums, int k) {
       // 暴力枚举
       int cnt = 0;
       int n = nums.length;

       for(int end=0;end<n;end++){
           int sum = 0;
           for(int start=end;start>=0;start--){
               sum += nums[start];
               if(sum == k) cnt++;
           }
       }

       return cnt;
    }
  • 方法二:前缀和+哈希表

key:前缀和

value:该前缀和出现的次数


    // k = pre[i]-pre[j-1]
    // pre[j-1] = pre[i] - k
    public int subarraySum(int[] nums, int k) {
       int n = nums.length;
       HashMap<Integer, Integer> map = new HashMap<>();

       map.put(0, 1);
       int cnt = 0;
       int pre = 0;

       // 计算前缀和数组
       for(int i=0;i<n;i++){
           pre += nums[i];
           // 当前的前缀和已知,判断是否含有pre-k的前缀和,那么我们就知道某一区间的和为k了
           if(map.containsKey(pre - k)) cnt += map.get(pre - k);

           // 更新
           map.put(pre, map.getOrDefault(pre,0) + 1);
       }

       return cnt;
    }

1.2 连续数组

    public int findMaxLength(int[] nums) {
        // 把0看成-1,题目转换为:求前缀和为0的一段最长数组
        // 0 = pre[i] - pre[j-1]
        // 当pre[i] == pre[j-1]时满足条件
        // 要求i-j+1的最大值

        // key代表前缀和,value代表位置
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int n = nums.length;

        map.put(0, 0);
        int pre = 0;
        int maxn = 0;

        for(int i=0;i<n;i++){
            if(nums[i] == 0) pre--;
            else pre++;

            if(map.containsKey(pre)){
                int j = map.get(pre);   // 这个前缀和出现的下标最小的位置
                if(i-j+1 > maxn) maxn = i-j+1;
            }else{
                map.put(pre, i+1);  // 如果这个前缀和数值没出现过,则记录
            }
        }

        return maxn;
    }

1.3 连续的子数组和

    public boolean checkSubarraySum(int[] nums, int k) {
        // pre[i] - pre[j-1] = k*n
        // pre[i]/k - pre[j-1]/k = n
        // 要保证为整数,则pre[i]和pre[j-1]对k取余的结果要相同
        int n = nums.length;

        // key放per[i]%k的值, value放下标
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        map.put(0, 0);  // 一定要垫

        int pre = 0;
        for(int i=0;i<n;i++){
            pre += nums[i];
            int temp = pre%k;

            if(map.containsKey(temp)){
                int end = i+1;  // 当前位置
                int start = map.get(temp);  // 出现相同余数位置的下标
                if(end-start >= 2) return true;
            }
            else map.put(temp, i+1);
        }

        return false;
    }

2. 差分

2.1 航班预订统计

    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] diff = new int[n+2];
        int[] ans = new int[n];

        int r = bookings.length; // 行数
    
        for(int i=0;i<r;i++){
            diff[bookings[i][0]] += bookings[i][2];
            diff[bookings[i][1]+1] -= bookings[i][2];
        }

        ans[0] = diff[1] + diff[0];        
        for(int i=1;i<n;i++){
            ans[i] = ans[i-1] + diff[i+1];
        }
        
        return ans;
    }