Find the nearest/closest value in a sorted List

前端 未结 8 1624
我寻月下人不归
我寻月下人不归 2020-12-05 10:59

I was wondering if it is possible to find the closest element in a List for a element that is not there.

For example if we had the valu

8条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-05 11:39

    Just thinking off the top of my head, if you need to find all closest values in a sorted list, you can find a closest value, then find all values with the same distance away from the target. Here, I use binary search 3 times:

    • First to find a closest value
    • Second to find the left-most closest value
    • Third to find the right-most closest value

    In Python:

    def closest_value(arr, target):
      def helper(arr, target, lo, hi, closest_so_far):
        # Edge case
        if lo == hi:
          mid = lo
          if abs(arr[mid] - target) < abs(arr[closest_so_far] - target):
            closest_so_far = mid
          return closest_so_far
    
        # General case
        mid = ((hi - lo) >> 1) + lo
    
        if arr[mid] == target:
          return mid
    
        if abs(arr[mid] - target) < abs(arr[closest_so_far] - target):
          closest_so_far = mid
    
        if arr[mid] < target:
          # Search right
          return helper(arr, target, min(mid + 1, hi), hi, closest_so_far)
        else:
          # Search left
          return helper(arr, target, lo, max(mid - 1, lo), closest_so_far)
    
    
      if len(arr) == 0:
        return -1
      return helper(arr, target, 0, len(arr) - 1, arr[0])
    
    
    arr = [0, 10, 14, 27, 28, 30, 47]
    
    attempt = closest_value(arr, 26)
    print(attempt, arr[attempt])
    assert attempt == 3
    
    attempt = closest_value(arr, 29)
    print(attempt, arr[attempt])
    assert attempt in (4, 5)
    
    
    def closest_values(arr, target):
      def left_helper(arr, target, abs_diff, lo, hi):
        # Base case
        if lo == hi:
          diff = arr[lo] - target
          if abs(diff) == abs_diff:
            return lo
          else:
            return lo + 1
    
        # General case
        mid = ((hi - lo) >> 1) + lo
        diff = arr[mid] - target
        if diff < 0 and abs(diff) > abs_diff:
          # Search right
          return left_helper(arr, target, abs_diff, min(mid + 1, hi), hi)
        elif abs(diff) == abs_diff:
          # Search left
          return left_helper(arr, target, abs_diff, lo, max(mid - 1, lo))
        else:
          # Search left
          return left_helper(arr, target, abs_diff, lo, max(mid - 1, lo))
    
    
      def right_helper(arr, target, abs_diff, lo, hi):
        # Base case
        if lo == hi:
          diff = arr[lo] - target
          if abs(diff) == abs_diff:
            return lo
          else:
            return lo - 1
    
        # General case
        mid = ((hi - lo) >> 1) + lo
        diff = arr[mid] - target
        if diff < 0 and abs(diff) > abs_diff:
          # Search right
          return right_helper(arr, target, abs_diff, min(mid + 1, hi), hi)
        elif abs(diff) == abs_diff:
          # Search right
          return right_helper(arr, target, abs_diff, min(mid + 1, hi), hi)
        else:
          # Search left
          return right_helper(arr, target, abs_diff, lo, max(mid - 1, lo))
    
    
      a_closest_value = closest_value(arr, target)
      if a_closest_value == -1:
        return -1, -1
    
      n = len(arr)
      abs_diff = abs(arr[a_closest_value] - target)
      left = left_helper(arr, target, abs_diff, 0, a_closest_value)
      right = right_helper(arr, target, abs_diff, a_closest_value, n - 1)
      return left, right
    
    
    arr = [0, 10, 14, 27, 27, 29, 30]
    
    attempt = closest_values(arr, 28)
    print(attempt, arr[attempt[0] : attempt[1] + 1])
    assert attempt == (3, 5)
    
    attempt = closest_values(arr, 27)
    print(attempt, arr[attempt[0] : attempt[1] + 1])
    assert attempt == (3, 4)
    

提交回复
热议问题