leetcode327(樹狀數組/歸併排序)

來源:互聯網
上載者:User

標籤:ret   bisect   lowbit   長度   init   參考   range   on()   構建   

問題描述:

給定一個整數數組nums,返回其所有落在[low, upper]範圍內(包含邊界)的區間和的數目。
區間和sums(i, j)的定義為所有下標為i到j之間(i ≤ j)的元素的和,包含邊界。

思路:

歸併排序

問題可描述為,對每個sums[i],滿足lower<=sums[i] - sums[j]<=upper的j的個數,並進行求和。

一個很巧妙的方法是,對sums進行排序得到ocums,然後求ocums[m] - ocums[n] = sums[i] - sums[j]落在上下界的個數,這裡想強調的是ocums[m],ocums[n]分別對應排序前的sums[i],sums[j]。 這樣做的好處是排序後求ocums區間內元素個數要方便很多,比如說對ocums[m],只需找到剛好滿足上界的ocums[y],和剛好滿足下界的ocums[x],x-y即為個數(注意ocums是升序的,所以x一定大於y),找到上下界的方法如果不用迴圈線性尋找(就是從小到大一個個試),就可以用演算法複雜度更低的二分尋找,這種思路用于歸並排序。

歸併排序的思路是,把區間劃分為上下兩個半空間x和y,然後對兩個空間的數一個一個取x[i]和y[j],比較這兩個數,如果x[i]<y[j]就讓i+1,否則j+1,並把較小的數加入到新序列中,這樣實現排序。當前排序的空間是從小到大的,也就是說需要先把區間劃分到最小,然後慢慢合并。

用歸併排序的思路求所有的區間和,就是先把區間sums劃分到最小,然後求滿足區間和上下界的個數,然後對區間進行排序,然後合并兩個小區間,求滿足區間和上下界的個數,然後對合并後區間進行排序,然後又合并排序後的兩個區間。。。迴圈下去;從一棵樹的角度來說,就是從根節點開始劃分區間,從最底層的區間開始計算個數並排序,並向上排序,因此這是一個遞迴的過程,求個數的過程是在合并的過程中計算的。

# 歸併排序# 讓人不解的兩個點# 1. 為什麼最小隻劃分區間到兩個數sums[i, i+1],而不是一個數?# 這是因為操作的對象是和,同時要遵循下半區間減上半區間# 當只有兩個數的時候,實質是看nums[i] = sums[i + 1] - sums[i]是否在上下界內# 最小區間劃分不會重疊,也就不會出現sums[i - 1, i],那還有一個數nums[i - 1]呢# 這個數會在相鄰兩個最小區間合并的過程中被考慮(想象一棵二分樹)# 2. 對sums進行排序後得到ocums,選擇區間和ocums[i] - ocums[j]在上下界的,這個合理嗎# 問題也說了,區間和就等於sums[m] - sums[n],本質上還是求源序列的區間和在[m, n]上是否滿足上下界# 這個方法真的很妙啊# 最後要說這個的演算法複雜度是N方的,真不知道怎麼就通過了class Solution(object):    def countRangeSum(self, nums, lower, upper):        """        :type nums: List[int]        :type lower: int        :type upper: int        :rtype: int        """        size = len(nums)        sums = [0] * (size + 1)        for x in range(1, size + 1):            sums[x] += nums[x - 1] + sums[x - 1]        INF = max(sums)        def mergeSort(lo, hi):            if lo == hi: return 0            mi = int((lo + hi) / 2)            cnt = mergeSort(lo, mi) + mergeSort(mi + 1, hi)            x = y = lo            for i in range(mi + 1, hi + 1):                while x <= mi and sums[i] - sums[x] >= lower:                    x += 1                while y <= mi and sums[i] - sums[y] > upper:                    y += 1                cnt += x - y            part = sums[lo : hi + 1]            l, h = lo, mi + 1            for i in range(lo, hi + 1):                x = part[l - lo] if l <= mi else INF                y = part[h - lo] if h <= hi else INF                if x < y: l += 1                else: h += 1                sums[i] = min(x, y)            return cnt        return mergeSort(0, size)nums = [-2, 5, 1, 3, 1, -4, 2]lower = -2upper = 2X =Solution()print(X.countRangeSum(nums, lower, upper))

樹狀數組

還可以對問題就行轉換,即求滿足sums[i] - upper<=sums[j]<=sums[i] - lower的j的個數,最樸素的方法就是對每一個sums[i]遍曆所有的(j,j<i)

# leetcode 327:求所有區間和在[lower, upper]之間的個數# 解法1 kk...複雜度為N方,逾時了class Solution(object):    def countRangeSum(self, nums, lower, upper):        """        :type nums: List[int]        :type lower: int        :type upper: int        :rtype: int        """        ans = 0        sum = [0 for _ in nums]        for i, num in enumerate(nums):            if i == 0:                sum[i] = num            else:                sum[i] = sum[i - 1] + num            if sum[i] >= lower and sum[i] <= upper:                ans += 1            for j in range(i):                if sum[j] >= (sum[i] - upper) and sum[j] <= (sum[i] - lower):                    ans += 1        return ansnums = [-2, 5, 1, 3, 1, -4, 2]lower = -2upper = 2X =Solution()print(X.countRangeSum(nums, lower, upper))

但用更直白的描述就是,求位於(sums[i] - upper, sums[i] - lower)的sums[j]的個數,希望通過排序知道每個值取了多少次,都排在哪個位置,當然前提是每次排序都只有序列的前i個數。也就是說,對sums[i]求個數的時候,只需把sums[i]的值插入到前面已經排好序的新序列中(這個可以用二分法做)。由於我們的目的是對個數求和,可以讓序列的值為個數,然後序列的索引為sums[i],這樣,樹狀數組的結構就出來了。

# 基於上一思路,利用樹狀數組來降低複雜度# 同樣是為了求落在區間[sumi - upper, sumi - lower]的sumj的個數,# 前一樸素法是對每個sumi,直接for j in range(0, i),致使演算法複雜度為O(N^2)# 樹狀數組的思想是先對sums進行去重排序得到長為m的序列,然後構建一個與該序列等長的樹狀數組來記錄每個值出現的次數# 於是,要計算落在區間[sumi - upper, sumi - lower]的sumj的個數,只需對該數組求區間和# 可行性的原因是,在for sumi in sums迴圈中,樹狀數組是隨著sumi不斷產生的,當前樹狀數組只記錄了(x, x<=i)的sumx# 對於每個sumi,演算法複雜度為O(logN),總的演算法複雜度為O(NlogN)import bisectclass Solution(object):    def countRangeSum(self, nums, lower, upper):        """        :type nums: List[int]        :type lower: int        :type upper: int        :rtype: int        """        ans = 0        sums = [0 for _ in nums]        for i, num in enumerate(nums):            if i == 0:                sums[i] = num            else:                sums[i] = sums[i - 1] + num        oscums = sorted(set(sums))        ft = FenwickTree(len(oscums))        for sumi in sums:            left = bisect.bisect_left(oscums, sumi - upper)            right = bisect.bisect_right(oscums, sumi - lower)            ans += ft.getSum(right) - ft.getSum(left) + (lower <= sumi <= upper)            ft.add(bisect.bisect_right(  oscums, sumi), 1)        return ansclass FenwickTree(object):    def __init__(self, n):        # n 為數組的長度        self.n = n        self.c = [0 for _ in range(n + 1)]    def lowBit(self, x):        return x & (x ^ (x - 1))    def add(self, i, val):        while i <= self.n:            self.c[i] += val            i += self.lowBit(i)    def getSum(self, i):        sum = 0        while i > 0:            sum += self.c[i]            i -= self.lowBit(i)        return sumnums = [-2, 5, -1]lower = -2upper = 2X =Solution()print(X.countRangeSum(nums, lower, upper))

 參考博文:

http://bookshadow.com/weblog/2016/01/11/leetcode-count-of-range-sum/

leetcode327(樹狀數組/歸併排序)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.