在排序列表中查找最近/最近的值

时间:2015-05-14 18:56:25

标签: java logic

我想知道是否有可能在List中找到中非的元素中最接近的元素。

例如,如果我们有值[1,3,6,7]并且我们正在寻找最接近4的元素,它应该返回3,因为3是数组中的最大数字,小于4

我希望这是有道理的,因为英语不是我的母语。

8 个答案:

答案 0 :(得分:25)

如果数组已排序,您可以在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];
    }

答案 1 :(得分:11)

您需要Array.binarySearchdocs

  

返回:搜索键的索引(如果它包含在数组中);   否则,( - (插入点) - 1)。插入点定义为   密钥插入数组的点:索引   大于键的第一个元素,或者所有元素的a.length   在数组中小于指定的键。

答案 2 :(得分:5)

考虑使用NavigableSet,特别是higherlower

答案 3 :(得分:0)

似乎最简单的方法就是遍历排序列表,检查每个项目。

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(3);
ints.add(6);
ints.add(7);

Collections.sort(ints);

int target = 4;
int nearest = 0;

for (int i : ints)
{
    if (i <= target) {
        nearest = i;
    }
}

System.out.println(nearest);

这将输出列表中最大项目,该项目小于或等于target

答案 4 :(得分:0)

如果您需要在排序列表中查找所有最接近的值,您可以找到 a 最接近的值,然后查找距离相同距离的所有值。目标。在这里,我使用二次搜索3次:

  • 首先找到 a 最接近的值
  • 第二个找到最左边的最近值
  • 第三个找到最接近的值

在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)

答案 5 :(得分:0)

使用二进制搜索的另一个O(log n)易于理解的解决方案:

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));
    }
}

答案 6 :(得分:0)

安德烈的答案是正确的。只是扩大一点。
使用内置的二进制搜索时无需重新发明轮子。

您可以通过以下方式找到索引:

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

列表中的项目将需要实现ComparableStringInteger之类的简单类型已经实现了此目的。这是一个示例https://www.javatpoint.com/Comparable-interface-in-collection-framework

根据您的用例,为了安全起见,您可能希望在二元搜索之后执行index = Math.max(0, index)

答案 7 :(得分:0)

有两种方法可以得到结果-

  1. lower_bound
  2. 二进制搜索

我更喜欢使用lower_bound方法,这很简短:)

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;

对于二进制搜索,您可以参考上面的答案,因为我的代码看起来像是其代码的复制粘贴。请注意,我已经在此处全局声明了数组。

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;
}