Return immediately for recursive calls

Question:

Problem Statement:
You are given an integer array nums. You are initially positioned at the array’s first index, and each element in the array represents your maximum jump length at that position. Return true if you can reach the last index, or false otherwise.

How can I change my code so that it returns immediately when I have found a path that works for this problem instead of going through all the recursive calls that I have made previously

def canJump(self, nums: List[int]) -> bool:
    solve = [False]
    def backtrack(i):
        if solve[0] == True:
            return
        
        if i == len(nums)-1:
            solve[0] = True
            return
        
        if i >= len(nums) or nums[i] == 0:
            return
        
        for x in range(1, nums[i]+1):
            backtrack(i+x)
    
    backtrack(0)
    return solve[0]
Asked By: Michael Xia

||

Answers:

General Form of a Recursive Function

The general form of a recursive function has two mutually exclusive types of conditions that can be met on each iteration of the recursion. These are either:

  1. terminal conditions, or
  2. non-terminal conditions.

Both types of condition contain a return statement.

Terminal Conditions

The return statement in terminal conditions typically takes the form return <value>.

The solution to the problem you are trying to solve requires two possible terminal conditions:

  1. The case where you know you can reach the last index. return True
  2. The case where you know you can NOT reach the last index. return False

Non-Terminal Conditions

The non-terminal condition will occur on iterations where neither of the terminal cases are met. In this situation, you will call the recursive function and return what it returns.

This answer covers terminal and non-terminal conditions in more detail.

Example

Consider a recursive function that sums the numbers of an array.

def sum(position, array, end):
    if(position == end): # terminal condition
        return 0 
    else: # non-terminal condition
        return array[position] + sum(position+1, array, end) 

Another Example

Depending on any constraints to your problem that I might have missed, a solution might be the following:

def jump(current_position, nums, finish_line):
    """
    greedy algorithm: 
    choose the next position with the largest sum of (jump_range + index)    
    """
    jump_range = nums[current_position]
    choice = current_position + jump_range

    if(jump_range == 0): # terminal condition
        return False 
    if(choice >= finish_line): # terminal condition
        return True 
    else: # non-terminal condition
        utility = 0
        
        for next_position in range(current_position+1, jump_range+1):
            next_jump_range = nums[next_position]
            
            if(utility <= next_position + next_jump_range):
                utility = next_position + next_jump_range
                choice = next_position
        
        return jump(choice, nums, finish_line)


input1 = [2,0,0,10,3]
input2 = [2,3,0,10,3]
current_position = 0

finish_line = len(input1)
print(jump(0, input1, finish_line)) # False

finish_line = len(input2)
print(jump(0, input2, finish_line)) # True

The most noteworthy difference from your solution is that return statements always return a value.

Answered By: Alex Oakley

How can I change my code so that it returns immediately when I have found a path that works for this problem instead of going through all the recursive calls that I have made previously

One particularly straightforward way is to throw an exception, which will immediately unwind the stack.

def can_jump(nums: list[int]) -> bool:
    if not nums:
        return False

    class _Success(Exception):
        pass

    def backtrack(i):
        if i >= len(nums):
            return

        if i == len(nums) - 1:
            raise _Success()

        for x in range(1, nums[i] + 1):
            backtrack(i + x)
    
    try:
        backtrack(0)
        return False
    except _Success:
        return True

We create a local exception type called _Success that the backtracking search can throw to indicate that it found a solution.

If it never finds a solution, the backtrack function will simply return and the return False line will run. Otherwise, it will raise _Success() and then the return True line will run.

Answered By: Alex Reinking