Find the nearest/closest value in a sorted List

前端 未结 8 1524
我寻月下人不归
我寻月下人不归 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:23

    Andrey's answer is correct. Just expanding on it a bit.
    No need to reinvent the wheel when you can use the built in binary search.

    You can find the indices with:

    int leftIndex = (-Collections.binarySearch(allItems, key) - 2);
    int rightIndex = (-Collections.binarySearch(allItems, key) - 1);
    

    The item in the list will need to implement Comparable. Simple types like String and Integer already implement this. Here's an example https://www.javatpoint.com/Comparable-interface-in-collection-framework.

    Depending on your use case you may want to do index = Math.max(0, index) after the binary search just to be safe.

    0 讨论(0)
  • 2020-12-05 11:24

    Another O(log n) easy to understand solution using binary search:

    public class Solution {
        static int findClosest(int arr[], int n, int target)
        {
            int l=0, h=n-1, diff=Integer.MAX_VALUE, val=arr[0];
            while(l<=h)
            {
                int mid=l+(h-l)/2;
                if(Math.abs(target-arr[mid])<diff)
                {
                    diff= Math.abs(target-arr[mid]);
                    val=arr[mid];
                }
                if(arr[mid]<target)
                    l=mid+1;
                else
                    h=mid-1;
            }
            return val;
    
        }
    
        public static void main(String[] args) {
            System.out.println(findClosest(new int[]{1,3,6,7}, 4, 3));
        }
    }
    
    0 讨论(0)
  • 2020-12-05 11:28

    Considering using NavigableSet, in particular higher and lower.

    0 讨论(0)
  • 2020-12-05 11:30

    There are two methods to achieve the result-

    1. lower_bound
    2. binary search

    I prefer to use the lower_bound method, it's short :)

    int pos=lower_bound(v.begin(),v.end(),value);
    if(pos!=0&&target!=v[pos])
        if(abs(v[pos]-value)>abs(value-v[pos-1]))
            pos=pos-1;
    

    For binary search, you can refer to the above answers, as my code will seem like a copy paste of their code. Note that I have declared the array globally here.

    binarysearch(int low,int high,int val)
    {
        if(val<=arr[0])
            return 0;
        if(val>=arr[n-1])
            return arr[n-1];
        while(low<=high)
        {
            int mid=(low+high)/2;
            if(v[mid]>val)
                return binarysearch(low,mid-1,val);
            else if(v[mid]<val)
                return binarysearch(mid+1,high,val);
            else return mid;
        }
        if(abs(val-v[low])>=abs(val-v[high]))
            return high;
        else 
            return low;
    }
    
    0 讨论(0)
  • 2020-12-05 11:32

    If the array is sorted you can do a modified binary search in O( log n ) :

        public static int search(int value, int[] a) {
    
            if(value < a[0]) {
                return a[0];
            }
            if(value > a[a.length-1]) {
                return a[a.length-1];
            }
    
            int lo = 0;
            int hi = a.length - 1;
    
            while (lo <= hi) {
                int mid = (hi + lo) / 2;
    
                if (value < a[mid]) {
                    hi = mid - 1;
                } else if (value > a[mid]) {
                    lo = mid + 1;
                } else {
                    return a[mid];
                }
            }
            // lo == hi + 1
            return (a[lo] - value) < (value - a[hi]) ? a[lo] : a[hi];
        }
    
    0 讨论(0)
  • 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)
    
    0 讨论(0)
提交回复
热议问题