CP #6 II : Maximum Subarray Divide and Conquer

Daha once O(n) ile cozdugumuz max subarray probleminin, leetcode'da bir de follow up'i var. Problemi divide and conquer ile cozebilir miyiz?

Ilk bakista, verilen dizeyi sol ve sag olarak ikiye ayirip, ayri ayri max sub-array'larini bulmak kolay gozukuyor. Recursive bir fonksiyona bakiyor:

class Solution:
    def maxSubarraySum(self, nums, start, end):
        # base case: sadece tek eleman varsa, max olarak onu dondur
        if start == end:
            return nums[start]
        else:
            # orta noktayi bularak dizeyi ikiye ayirip
            # hangisinin max sub array'i buyukse onu donduruyoruz
            mid = start + (end-start)//2
            return max(self.maxSubarraySum(nums, start, mid), 
                               self.maxSubarraySum(nums, mid+1, end))
    
    def maxSubArray(self, nums):
        return self.maxSubarraySum(nums, 0, len(nums)-1)


Bu cozumde tabi ki bir eksik var. Ya max sub array, bizim boldugumuz yerden geciyorsa? Zaten [-2,1,-3,4,-1,2,1,-5,4] icin calistirdigimizda 4 sonucunu alacagiz cunku suanda sadece tek tek elemanlara bakiyoruz.

[-1, 2, 1, -5]
[-1, 2] vs [1, -5]
([-1] vs [2] ) vs ([1] vs [-5])
(-1 vs 2) vs (1 vs -5]
(2) vs (1)
return 2

Seklinde dizedeki en buyuk elemani getirecektir. Cunku bolme noktalarini da hesaba katmiyoruz. 

Bolunme noktasindan gecen max sub-arrayi nasil bulabiliriz? 

Bolunme noktasini biliyoruz. Bu noktadan sola ve saga, start ve end noktalarina kadar giderek maks sum'i bulabiliriz.

def crossingSubarraySum(self, nums, start, mid, end):
    # sola git
    left_sum = float("-Infinity")
    sm = 0
    for t in range(mid, start-1, -1):
        sm += nums[t]
        left_sum = max(left_sum, sm)
    
    # saga git
    sm = 0
    right_sum = float("-Infinity")
    for k in range(mid+1, end+1):
        sm += nums[k]
        right_sum = max(right_sum, sm)
    
    # nums[crossing] 2 kere saydik
    ret = max(left_sum, right_sum, left_sum+right_sum)
    return ret


Seklinde, bolme noktasindan sola ve saga gitmek suretiyle bir max sub array daha elde etmis oluyoruz. Bu iki metodu birlestirirsek,

class Solution:
    def maxSubarraySum(self, nums, start, end):
        # base case: sadece tek eleman varsa, max olarak onu dondur
        if start == end:
            return nums[start]
        else:
            # orta noktayi bularak dizeyi ikiye ayiriyoruz
            # soldaki max sub array, sagdaki max sub array ve de 
            # bolunme noktasindan gecen max sub array'den buyuk olani donduruyoruz
            mid = start + (end-start)//2
            return max(self.maxSubarraySum(nums, start, mid), 
                               self.maxSubarraySum(nums, mid+1, end),
                               self.crossingSubarray(nums, start, mid, end))
    
    def maxSubArray(self, nums):
        return self.maxSubarraySum(nums, 0, len(nums)-1)

Analiz
Onceki cozum O(n) olmasi itibari ile elbette ki daha performansli. Her adimda dizeyi ikiye boldugumuzden dolayi O(nlogn) olmaktadir. 

Yorumlar

Bu blogdaki popüler yayınlar

Python'da Multithreading ve Multiprocessing

SD #1: Scalability

Threat Modeling 1