[LeetCode]209. Minimum Size Subarray Sum

209. Minimum Size Subarray Sum

题意:找到和为给定值的最小连续区间

O(n)

暴力

TLE

class Solution(object):
    def minSubArrayLen(self, s, nums):
        """
        :type s: int
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        res = 1e9
        left, right = 0, 0
        while True:
            r = sum(nums[left:right])
            if r < s:
                right += 1
            else:
                res = min(res, right - left)
                left += 1
                right = left + 1
            if left == len(nums)-1:
                break
        if res == 1e9:
            res = 0
        return res

滑动窗口

于是,我想到了滑动窗口,窗口中始终维护着比给定值要小的元素,但还是TLE:

class Solution(object):
    def minSubArrayLen(self, s, nums):
        """
        :type s: int
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        res = 1e9
        left, right = 0, 1
        while left <= right <= len(nums):
            cur_sum = sum(nums[left:right])
            if cur_sum >= s:
                res = min(res, right-left)
                left += 1
            else:
                right += 1
        if res == 1e9:
            res = 0
        return res

于是我在想是不是sum这个操作消耗了时间,其实并不需要每次计算和,只需要维护一个当前和的变量,如果right往右则加上,left往右则减去即可,最后AC:

class Solution(object):
    def minSubArrayLen(self, s, nums):
        """
        :type s: int
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        res = 1e9
        left, right = 0, 0
        cur_sum = nums[left]
        while left <= right < len(nums):
            if cur_sum >= s:
                res = min(res, right-left+1)
                cur_sum -= nums[left]
                left += 1
            else:
                right += 1
                if right >= len(nums):
                    break
                cur_sum += nums[right]
        if res == 1e9:
            res = 0
        return res

O(nlogn)

除了O(n)的做法,题目中还要求了O(nlogn)的做法,首先可以猜到是排序(因为是连续子数组,所以排除),再猜是二分,既然是二分,那就要求数组是有序的,我们可以对数组做个累加和,然后遍历数组以i为起点,找到第一个和i构成的区间和大于给定值的下标(注意减去累加数组的偏移量)。

nums = [2,3,1,2,4,3], k = 7
=> sums = [2,5,6,8,12,15]
=> 2, end = 8, sum = 8 - 0 = 8, window = 4
=> 5, end = 12, sum = 12 - 2 = 10, window = 4
=> 6, end = 12, sum = 12 - 5 = 7, window = 3
=> 8, end = 15, sum = 15 - 6 = 9, window = 3
=> 12, end = 15, sum = 15- 8 = 7, window =  2

代码如下:

class Solution(object):
    def minSubArrayLen(self, s, nums):
        def findWindowEnd(start):
            i, j = start, len(nums)-1
            offset = 0 if start == 0 else sums[start-1]
            while i <= j:
                mid = (i + j) // 2
                range_sum = sums[mid] - offset
                if range_sum < s:
                    i = mid+1
                else:
                    j = mid-1
            return i
        res = 1e9
        sums = [0] * len(nums)
        for i in range(len(nums)):
            sums[i] = nums[0] if i == 0 else sums[i-1] + nums[i]
        for i in range(len(nums)):
            j = findWindowEnd(i)
            if j == len(nums):
                break
            res = min(res, j-i+1)
        return 0 if res == 1e9 else res
posted @ 2017-09-02 20:30  banananana  阅读(109)  评论(0)    收藏  举报