Search code examples
algorithmrecursiontime-complexitymemoizationcoin-change

What is the time complexity of this coin change algorithm?


I solved the coin changing problem on leetcode https://leetcode.com/problems/coin-change-2/.

This is the code I wrote:

    def change(self, amount: int, coins: List[int]) -> int:
        def helper(amount, coins, ind):
            if amount == 0:
                return 1
            if amount < 0:
                return 0
            if (amount, ind) in dp:
                return dp[(amount, ind)]

            res = 0
            for i in range(ind, len(coins)):
                res  += helper(amount - coins[i], coins, i)

            dp[(amount, ind)] = res
            return res

        coins.sort()
        dp = {}
        return helper(amount, coins, 0)

And I notice I struggle a lot with analyzing the time complexities of algorithms with memoization. For example in this case I think we have amount * number of coins sub-problems --> hence the algorithm runs in O(amount * number of coins * number of coins) the second number of coins in my factor comes from the fact that for each sub-problem we have to go through the loop for i in range(ind, len(coins)): to add up the results.

However it seems that this solution is actually O(amount * number of coins) from what I read (and the bottom up approach is O(amount * number of coins)). What is the right answer ?

I seem to struggle a lot with loops inside recursive functions, it sometimes seem like we count them in the time complexity, and other times they are already "priced in" in the sub-problems like I suspect is the case here.


Solution

  • As Enrico Borba commented:

    Your analysis seems correct to me. You have O(amount * number of coins) cells in your table and to compute any cell in the table you run a loop (number of coins) times. The code you wrote has this complexity. It's likely that there is a different algorithm that has O(amount * number of coins) complexity.

    – Enrico Borba