General code template for binary search problem

Posted by marcnyc on Wed, 16 Feb 2022 08:46:17 +0100

The idea of binary search is very simple, but there are many traps in the process of code writing. In particular, binary search has many variants, such as:

  1. Find the first element equal to the target value, and return None if it does not exist
  2. Find the last element equal to the target value, and return None if it does not exist
  3. Find the first element greater than the target value, and return None if it does not exist
  4. Find the first element greater than or equal to the target value, and return None if it does not exist
  5. Find the last element less than the target value, and return None if it does not exist
  6. Find the last element less than or equal to the target value, and return None if it does not exist

Common code templates are as follows:

def BinarySearch(nums:list,target:int)-> int:
    """
        Binary search common codes

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: The subscript of the target value in the array
    """
    
    right = len(nums)-1
    left = 0
    
    while left <= right:
        mid = left + (right-left)//2 # calculate the index of intermediate elements
        if target == nums[mid]:
            return mid  # Exit when found
        elif target < nums[mid]:
            right = mid-1
        else:
            left = mid+1
            
    return None # Null value if not found

In this code, we set the left boundary as the first element and the right boundary as the last element, calculate the index of the intermediate element, and exit when we find an intermediate element equal to the target value. By observing the above code, we can find a problem. Every time we update the value of left or riget, we need to add one or subtract one from Mid. in some problems of finding boundaries, the judgment of mid+1, mid-1 or mid directly will be very bypassed, which makes code writing very easy to make mistakes.

In order to solve this problem, the best way is to put different problems into a unified set of code templates. When facing different variant problems, only a few key steps need to be modified. Therefore, the above code is modified as follows:

def BinarySearch(nums:list,target:int)-> int:
    """
        Binary search common code template
        The first equal was found in the problem target Subscript of element,Sequential ascending arrangement

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: The subscript of the target value in the array
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] < target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if red<len(nums) and nums[red] == target:        #!  Judgment boundary value
        return red
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found

Code thinking analysis

For example, we set the ordered sequence nums (ascending by default) to [1,2,3,4,5,6,6,6,7,8], and set the target value to 6. We will find the subscript of the first element equal to 6.
In order to better understand the binary search problem, we color mark the elements in the sequence as red and blue respectively. In this problem, it is easy to conclude that all elements less than 6 are blue, and elements greater than or equal to 6 are red, as shown below:
[1,2,3,4,5,6,6,6,7,8]

It can be seen that what we are looking for is actually the subscript of the first element whose color is red and whose value is equal to 6.
Therefore, we turn a search problem into the problem of color division of the array.

Set two variables blue and red so that blue always points to the last element in the blue area and red always points to the first element in the red area. Since we do not know the color distribution of the sequence at the beginning of the problem, the initial value of blue is the left side of the first element, that is, blue = -1, and the initial value of red is the right side of the last element, that is, red = len(nums), In this specific problem, the initial value of red is 10.

Next, we enter the loop to divide the color of the whole array. First, when do we exit the loop?

It's easy to think that as long as we color all the data of the whole array, it's time to quit the loop. At this time, the blue interval and the red interval are no longer separated by non stained elements, that is, blue+1 == red. Therefore, corresponding to the while loop, our loop exit condition is always blue + 1= red.

Let's consider the special case. If the array num is empty, then the initial value of blue is - 1 and the initial value of red is 0. At this time, blue plus 1 is exactly equal to red. We will not enter the loop and directly enter the judgment statement for returning the result.

Next, calculate the index of the intermediate element for the first time, that is, mid = blue + (red blue) / / 2. In this specific problem, the first mid value is - 1 + (10 - (- 1)) / / 2 = 4. Then, according to the standard we first dyed the elements: all elements less than 6 are blue, and elements greater than or equal to 6 are red. Let's judge what color mid should be dyed. Since the value of num [4] is 5 and less than 6, mid should be dyed blue here, that is, blue = mid. Here we finish the first cycle operation, and then enter the subsequent cycle similarly until all the data are colored.

After all the data are dyed, we can judge the return value. Since we are looking for the subscript of the first element whose color is red and the value is equal to 6, after the loop is over, we can directly judge whether the element corresponding to red exists and whether the value is equal to 6.

According to the above analysis, we can find that when the values to be searched are different, we only need to modify the coloring standard of the element (that is, the judgment condition of the if judgment statement in the loop) and the judgment standard of the return value (that is, the judgment standard of the return value judgment statement), You don't need to think hard about whether the values of left and right are mid+1 or mid-1, or whether you can use mid directly (or instead of thinking, try to see which can work) 😃).

Well, here we have the solution template for the binary search problem, that is, first find the coloring standard of the array, and then find the judgment standard of the return value we need. Now it's time to go back to the six questions raised at the beginning of the article:

  1. Find the first element equal to the target value, and return None if it does not exist
  2. Find the last element equal to the target value, and return None if it does not exist
  3. Find the first element greater than the target value, and return None if it does not exist
  4. Find the first element greater than or equal to the target value, and return None if it does not exist
  5. Find the last element less than the target value, and return None if it does not exist
  6. Find the last element less than or equal to the target value, and return None if it does not exist

Using the idea just now, we can conclude that the dyeing criteria and return value judgment criteria of these six questions are:

  1. All elements less than the target value are blue, and elements greater than or equal to the target value are red;
    If the element corresponding to red exists and the value is equal to target, red is returned; otherwise, a null value is returned.
  2. All elements less than or equal to the target value are blue, and elements greater than the target value are red;
    If the element corresponding to blue exists and the value is equal to target, blue is returned; otherwise, null value is returned.
  3. All elements less than or equal to the target value are blue, and elements greater than the target value are red;
    If the element corresponding to red exists, it returns red; otherwise, it returns a null value.
  4. All elements less than the target value are blue, and elements greater than or equal to the target value are red;
    If the element corresponding to red exists, it returns red; otherwise, it returns a null value.
  5. All elements less than the target value are blue, and elements greater than or equal to the target value are red;
    If the element corresponding to blue exists, blue is returned; otherwise, null value is returned.
  6. All elements less than or equal to the target value are blue, and elements greater than the target value are red;
    If the element corresponding to blue exists, blue is returned; otherwise, null value is returned.

The corresponding codes of the six questions are:

def BinarySearch(nums:list,target:int)-> int:
    """
       Find the first and target Elements with equal values,If it does not exist, return None

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: The subscript of the target value in the array
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] < target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if red<len(nums) and nums[red] == target:        #!  Judgment boundary value
        return red
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found
def BinarySearch(nums:list,target:int)-> int:
    """
       Find the last and target Elements with equal values,If it does not exist, return None

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: The subscript of the target value in the array
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] <= target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if blue >= 0 and nums[blue] == target:        #!  Judgment boundary value
        return blue
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found
def BinarySearch(nums:list,target:int)-> int:
    """
       Find first greater than target Value element,If it does not exist, return None

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: subscript
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] <= target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if red < len(nums):        #!  Judgment boundary value
        return red
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found
def BinarySearch(nums:list,target:int)-> int:
    """
       Find the first greater than or equal to target Value element,If it does not exist, return None

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: subscript
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] < target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if red < len(nums):        #!  Judgment boundary value
        return red
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found
BinarySearch(nums:list,target:int)-> int:
    """
       Find the last less than target Value element,If it does not exist, return None

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: subscript
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] < target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if blue >= 0:        #!  Judgment boundary value
        return blue
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found
def BinarySearch(nums:list,target:int)-> int:
    """
       Find the last less than or equal to target Value element,If it does not exist, return None

    Args:
        nums (list): Ordered array
        target (int): target value

    Returns:
        _type_: subscript
    """
    
    blue = -1                       # ! Key point 1: modify the value of the initial condition, where blue represents the left half and red represents the right half
    red = len(nums) 
    
    
    while blue + 1 != red:
        mid = blue + (red-blue)//2  #!  Calculate the index of the intermediate element
        
        if nums[mid] <= target:     #!  Judge whether the intermediate element belongs to the blue area or the red area
            # *Different problem variants need to be modified in position i
            blue = mid
        else:
            red  = mid
            
    if blue >= 0:        #!  Judgment boundary value
        return blue
        #*Different problem variants need to be modified in position 2
    else:
        return None  # Null value not found

Topics: Algorithm data structure leetcode