Je me demandais s'il était possible de trouver l'élément le plus proche dans un List
pour un élément ce n'est pas là.
Par exemple, si nous avions les valeurs [1,3,6,7] et que nous recherchons l'élément le plus proche de 4, il devrait renvoyer 3, car 3 est le plus grand nombre du tableau, c'est-à-dire inférieur à 4.
J'espère que cela a du sens, car l'anglais n'est pas ma langue maternelle.
Si le tableau est trié, vous pouvez effectuer une recherche binaire modifiée dans 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];
}
Vous avez besoin Array.binarySearch
, documents .
Renvoie: index de la clé de recherche, si elle est contenue dans le tableau; sinon, (- (point d'insertion) - 1). Le point d'insertion est défini comme le point auquel la clé serait insérée dans le tableau: l'indice du premier élément supérieur à la clé, ou a.length si tous les éléments du tableau sont inférieurs à la clé spécifiée.
Envisager d'utiliser NavigableSet
, en particulier higher
et lower
.
Une autre solution O (log n) facile à comprendre utilisant la recherche binaire:
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));
}
}
En pensant du haut de ma tête, si vous avez besoin de trouver toutes les valeurs les plus proches dans une liste triée, vous pouvez trouver a la valeur la plus proche, puis trouver toutes les valeurs à la même distance de la cible. Ici, j'utilise la recherche binaire 3 fois:
En 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)
Il semble que le moyen le plus simple consiste simplement à parcourir la liste triée en vérifiant chaque élément.
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);
Cela génère le plus grand élément de la liste qui est inférieur ou égal à target
.
La réponse d'Andrey est correcte. Je développe juste un peu.
Pas besoin de réinventer la roue lorsque vous pouvez utiliser la recherche binaire intégrée.
Vous pouvez trouver les indices avec:
int leftIndex = (-Collections.binarySearch(allItems, key) - 2);
int rightIndex = (-Collections.binarySearch(allItems, key) - 1);
L'élément de la liste devra implémenter comparable . Des types simples comme String
et Integer
implémentent déjà cela. Voici un exemple https://www.javatpoint.com/Comparable-interface-in-collection-framework .
Selon votre cas d'utilisation, vous voudrez peut-être faire index = Math.max(0, index)
après la recherche binaire juste pour être sûr.