前綴和

523. 连续的子数组和

难度中等

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

  • 子数组大小 至少为 2 ,且
  • 子数组元素总和为 k 的倍数。

如果存在,返回 true ;否则,返回 false 。

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。0 始终视为 k 的一个倍数。

 

示例 1:

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。

示例 2:

输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。 
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。

示例 3:

输入:nums = [23,2,6,4,7], k = 13
输出:false


解题思路:使用前缀和+ 哈希。
class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        if(nums.length <= 1) {
            return false;
        }
        Map<Integer, Integer> numPos = new HashMap<>();
        numPos.put(0, -1);
        int total = 0;

        for(int i = 0; i < nums.length; i++) {
            total += nums[i];           
            int rest = total % k;
            if(numPos.containsKey(rest)) {
                if(i - numPos.get(rest) >= 2) {
                    return true;
                }
                
            } else {
                numPos.put(rest, i);
            }
        }

        return false;
    }
}

 

525. 连续数组

难度中等

给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。

 

示例 1:

输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。

示例 2:

输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。

class Solution {
    public int findMaxLength(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        int count = 0;
        map.put(0, -1);
        int maxLen = 0;
        for(int i = 0; i < nums.length; i++) {          
            count += nums[i] == 0 ? -1 : 1;
            if(map.containsKey(count)) {
                maxLen = Math.max(maxLen, i - map.get(count));
            } else {
                map.put(count, i);
            }
        }
        
        return maxLen;

    }
}

 

这里有 n 个航班,它们分别从 1 到 n 进行编号。

有一份航班预订表 bookings ,表中第 i 条预订记录 bookings[i] = [firsti, lasti, seatsi] 意味着在从 firsti 到 lasti (包含 firsti 和 lasti )的 每个航班 上预订了 seatsi 个座位。

请你返回一个长度为 n 的数组 answer,其中 answer[i] 是航班 i 上预订的座位总数。

 

示例 1:

输入:bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
输出:[10,55,45,25,25]
解释:
航班编号        1   2   3   4   5
预订记录 1 :   10  10
预订记录 2 :       20  20
预订记录 3 :       25  25  25  25
总座位数:      10  55  45  25  25
因此,answer = [10,55,45,25,25]

解题思路:此题用暴力解法思路很简单,单可能会超时。使用前缀和的巧妙解法。比如区间[2,3,20],标记2号航班为20,标记4号航班为-20. 这样通过前缀后就是航班2,3位20,航班4为0.

    public int[] corpFlightBookings(int[][] bookings, int n) {
        // int[] ans = new int[n];

        // for(int[] bookSeat : bookings) {
        //     int start = bookSeat[0];
        //     int end = bookSeat[1];
        //     int num = bookSeat[2];
        //     for(int i = start-1; i < end; i++) {
        //         ans[i] += num;
        //     }
        // }

        // return ans;

        int [] ans = new int[n];

        for(int[] bookSeat : bookings) {
            ans[bookSeat[0] - 1] += bookSeat[2];
            if(bookSeat[1] < n) {
                ans[bookSeat[1]] -= bookSeat[2];
            }
        }

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

        return ans;
    }

 

给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。

例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。

也就是说,选取下标 i 的概率为 w[i] / sum(w) 。

 

示例 1:

输入:
["Solution","pickIndex"]
[[[1]],[]]
输出:
[null,0]
解释:
Solution solution = new Solution([1]);
solution.pickIndex(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。

class Solution {
    int total;
    int[] preSum;

    public Solution(int[] w) {
        preSum = new int[w.length];
        preSum[0] = w[0];
        for(int i = 1; i  < w.length; i++) {
            preSum[i] = preSum[i-1] + w[i];
        }
        total = preSum[preSum.length - 1];
    }
    
    public int pickIndex() {
        int random = new Random().nextInt(total) + 1;
        return binarySerach(random);
    }

    private int binarySerach(int target) {
        int left = 0;
        int right = preSum.length - 1;

        while(left < right) {
            int mid = left + (right - left) / 2;
            if(target == preSum[mid]) {
                return mid;
            }
            if(target > preSum[mid]) {
                left = mid+1;
            } else {
                right = mid;
            }
        }

        return left;
    }
}

 

posted @ 2021-06-05 21:06  闪闪的星光  阅读(60)  评论(0)    收藏  举报