3

I've been working on my data structure/algorithm skills, and I encountered the following question (LeetCode 416. Partition Equal Subset Sum):

"Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal."

EX:

Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

My solution is as follows:

class Solution:
    def canPartition(self, nums: List[int]) -> bool:        
        if sum(nums) % 2 == 1:
            return False
        memo = {}
        
        def check_all(i, target):
            if (i, target) in memo:
                return memo[(i, target)]
            if target == 0:
                return True
            if i == len(nums) and target != 0:
                return False
            
            if nums[i] > target:
                not_included = check_all(i+1, target)
                memo[(i, target)] = not_included
                return not_included
            
            memo[(i, target)] = check_all(i+1, target) or check_all(i+1, target-nums[i])
            return memo[(i, target)]
        
        return check_all(0, sum(nums)//2)

This solution is accepted with a runtime of 5276 ms and memory usage of 469 MB. After submitting my solution, I looked around at other solutions, and I found the following code:

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        target = sum(nums)
        if target % 2 != 0:
            return False
        
        return self.helper({} ,nums, target//2, 0)
        
    def helper(self, dp, nums, target, i):
    # Using memoization, if we have already computed result for given i and target, we return that
        if (i,target) in dp:
            return dp[(i,target)]
            
        # if target becomes 0 that means we found a subset that has sum == target
        if target == 0:
            return True
        # if we have checked all numbers in nums but we can't find a subset whose sum is equal to target, we return False
        if i == len(nums) and target != 0:
            return False
        
        #checking wether to include curr num or not
        if nums[i] > target:
            dp[(i,target)] = self.helper(dp,nums, target, i+1)
            return dp[(i,target)]
        
        # if either selecting curr num returns True or not selcting it returns True we return True
        dp[(i,target)] = self.helper(dp,nums,target-nums[i],i+1) or self.helper(dp,nums,target,i+1)
        return dp[(i,target)]

This second solution is accepted with a runtime of 1922 ms and memory usage of 140 MB. I cannot for the life of me figure out why the second solution is so much more efficient than the first. I'd appreciate any insight that someone could provide!

2
  • @jasonharper Wow! That was it! I swapped the order of the recursive calls, and now the runtimes and memory usages are near equivalent. I beat my head against the wall for an hour and a half over this haha. Thank you very much! Commented Aug 24, 2022 at 13:39
  • I hadn't considered that the order of the recursive calls would make such a difference. That's good to know moving forward! Commented Aug 24, 2022 at 13:40

1 Answer 1

2

The main difference lies here:

# your approach
memo[(i, target)] = check_all(i+1, target) or check_all(i+1, target-nums[i])

# more efficient approach
dp[(i,target)] = self.helper(dp,nums,target-nums[i],i+1) or self.helper(dp,nums,target,i+1)

In the second approach, the first recursive call (self.helper(dp,nums,target-nums[i],i+1)) reduces the search space (since target is decremented and i is incremented), and goes to the second recursive call (which only increments i) only if the first call doesn't find a solution.

In your approach, you use the "inefficient" recursive call first, which searches in a larger space than required. This why your approach takes more time/memory.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.