在排序数组列表中查找2个最接近的先前值和2个最接近的下一个值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在排序数组列表中查找2个最接近的先前值和2个最接近的下一个值相关的知识,希望对你有一定的参考价值。

这是一个修改后的二进制搜索,它将排序数组列表中最接近的元素返回给定值。我如何调整它以便它可以返回2个最接近的前2个和2个最接近的下一个元素?

private static Long search(long value, ArrayList<Long> a) {

    if(value < a.get(0)) {
        return a.get(0);
    }
    if(value > a.get(a.size()-1)) {
        return a.get(a.size()-1);
    }

    int lo = 0;
    int hi = a.size() - 1;

    while (lo <= hi) {
        int mid = (hi + lo) / 2;
        if (value < a.get(mid)) {
            hi = mid - 1;
        } else if (value > a.get(mid)) {
            lo = mid + 1;
        } else {
            return a.get(mid);
        }
    }
    return (a.get(lo) - value) < (value - a.get(hi)) ? a.get(lo) : a.get(hi);
}

例如,让我们考虑我的数组列表由以下元素组成:

[101, 201, 301, 401, 501, 601, 701, 801, 901, 1001]

我被赋予了730的值。我希望搜索返回一个由4个元素组成的数组,其中包含601,701作为前一个值,801和901作为下一个值。

答案

使用非常简单的方法,此代码根据lohimid值之间的差异将元素添加到结果列表中。

在边缘情况下,返回的元素数量可以小于4.例如,如果左侧或右侧没有元素(由于列表边界),返回的列表大小可以是2或3,具体取决于最接近的值的位置。

如果这是你想要的,那么这里是代码:

private static List<Long> search(long value, List<Long> a) {
    if (a.size() < 3) return new ArrayList<>(a);
    List<Long> result = new ArrayList<>();
    if (value < a.get(0)) {
        result.add(a.get(0));
        result.add(a.get(1));
        return result;
    }
    if (value > a.get(a.size() - 1)) {
        result.add(a.get(a.size() - 2));
        result.add(a.get(a.size() - 1));
        return result;
    }

    int lo = 0;
    int hi = a.size() - 1;
    int match = -1;

    while (lo <= hi) {
        int mid = (hi + lo) / 2;
        if (value < a.get(mid)) {
            hi = mid - 1;
        } else if (value > a.get(mid)) {
            lo = mid + 1;
        } else {
            match = mid;
            break;
        }
    }

    if (match >= 0) {
        if (match > 1) result.add(a.get(match - 2));
        if (match > 0) result.add(a.get(match - 1));
        if (match < a.size() - 1) result.add(a.get(match + 1));
        if (match < a.size() - 2) result.add(a.get(match + 2));
    } else if (a.get(lo) < value) {
        result.add(a.get(hi));
        result.add(a.get(lo));
        if (lo < a.size() - 1) result.add(a.get(lo + 1));
        if (lo < a.size() - 2) result.add(a.get(lo + 2));
    } else if (a.get(hi) > value) {
        if (hi > 1) result.add(a.get(hi - 2));
        if (hi > 0) result.add(a.get(hi - 1));
        result.add(a.get(hi));
        result.add(a.get(lo));
    } else {
        if (hi > 0) result.add(a.get(hi - 1));
        result.add(a.get(hi));
        result.add(a.get(lo));
        if (lo < a.size() - 1) result.add(a.get(lo + 1));
    }

    return result;
}
另一答案

有一种简单的方法来扩展原始代码以适应返回四个值的列表,并且它相对容易:

    private static List<Long> search(long value, ArrayList<Long> a) {

    List<Long> returnList = new ArrayList<>();
    if (null == a) {
        return null;
    }
    if (a.isEmpty()) {
        return a;
    }
    if (a.size()==1) {
        return a;
    }
    if(value < a.get(0)) {
        returnList.add(a.get(0));
        returnList.add(a.get(1));
        return returnList;
    }
    if(value > a.get(a.size()-1)) {
        returnList.add(a.get(a.size()-1));
        returnList.add(a.get(a.size()-2));
        return returnList;
    }

    int lo = 0;
    int hi = a.size() - 1;

    while (lo <= hi) {
        int mid = (hi + lo) / 2;
        if (value < a.get(mid)) {
            hi = mid - 1;
        } else if (value > a.get(mid)) {
            lo = mid + 1;
        } else {
            if (mid>1) returnList.add(a.get(mid-2));
            if (mid>0) returnList.add(a.get(mid-1));
            if (mid<=a.size()-1) returnList.add(a.get(mid+1));
            if (mid<=a.size()-2) returnList.add(a.get(mid+2));
            return returnList;
        }
    }
    if ((a.get(lo) - value) < (value - a.get(hi))) {
        if (lo > 0) returnList.add(a.get(lo-1));
        returnList.add(a.get(lo));
        if (lo<=a.size()-1) returnList.add(a.get(lo+1));
        if (lo<=a.size()-2) returnList.add(a.get(lo+2)); 
    } else {
        if (lo > 1) returnList.add(a.get(hi-2));
        if (lo > 0) returnList.add(a.get(hi-1));
         ReturnList.add(a.get(hi));
        if (hi<=a.size()-1) returnList.add(a.get(hi+1));
    }
    return returnList;
}

但是,如果我们扩展代码,那么可能是进行一些小型重构的最佳时机,以使其更具可读性。

    private static List<Long> addNumbersAroundIndex (int index, Long searchedNum, int numOfElements, List<Long> list) {
    List<Long> returnList = new ArrayList<Long>();
    int before = numOfElements/2;
    int after = numOfElements - before;

    if (list.get(index)>searchedNum) {
        after --;

    } else if(list.get(index)<searchedNum) {

        before--;
    }
    for (int i=before; i>0; i--) {

        if (index>i+1) returnList.add(list.get(index-i));
    }
    if (!list.get(index).equals(searchedNum)) {
        returnList.add(list.get(index));
    }
    for (int i=1; i< after+1; i++) {

        if (index+i<=list.size()-1) returnList.add(list.get(index+i));
    }
    return returnList;
}

private static List<Long> search(long value, int numOfElements, ArrayList<Long> a) {

    if (null == a) {
        return null;
    }
    if (a.isEmpty()) {
        return a;
    }
    if (a.size()==1) {
        return a;
    }
    if(value < a.get(0)) {
        return addNumbersAroundIndex(0, value, numOfElements,a);
    }
    if(value > a.get(a.size()-1)) {
        return addNumbersAroundIndex(a.size()-1, value,numOfElements,a);
    }

    int lo = 0;
    int hi = a.size() - 1;

    while (lo <= hi) {
        int mid = (hi + lo) / 2;
        if (value < a.get(mid)) {
            hi = mid - 1;
        } else if (value > a.get(mid)) {
            lo = mid + 1;
        } else {
            return addNumbersAroundIndex(mid,value,numOfElements,a);
        }
    }
    if ((a.get(lo) - value) < (value - a.get(hi))) {
        return addNumbersAroundIndex(lo,value,numOfElements,a);
    } else {
        return addNumbersAroundIndex(hi,value,numOfElements,a);
    }
}

使用您的一些原始列表进行测试:

public static void main(String[] args) {
    ArrayList<Long> list = new ArrayList(Arrays.asList(101l, 201l, 301l, 401l, 501l, 601l, 701l, 801l, 901l, 1001l));
    System.out.println(search(1020l,5,list));

}

[801, 901, 1001]

System.out.println(search(730l,4,list));

[601, 701, 801, 901]

System.out.println(search(601l,5,list));

[401, 501, 701, 801, 901]

以上是关于在排序数组列表中查找2个最接近的先前值和2个最接近的下一个值的主要内容,如果未能解决你的问题,请参考以下文章

leetcode-658 找到K个最接近的元素

获取列表中某个给定数字的3个最接近的值?

2个最接近0的数字

leetcode 658找到k个最接近的元素

LeetCode 1460. 通过翻转子数组使两个数组相等 / 658. 找到 K 个最接近的元素 / 1464. 数组中两元素的最大乘积

LeetCode 1460. 通过翻转子数组使两个数组相等 / 658. 找到 K 个最接近的元素 / 1464. 数组中两元素的最大乘积