从有序序列中求最大 不大于目标 的数的下标的二分查找怎么写比较优雅?

发布于 2022-09-01 07:39:48 字数 1616 浏览 15 评论 0

本身这个题挺简单的,但是如果增加这样一个要求怎么写:
如果序列中有多个等于目标的数,则可以传入一个flag参数,来决定返回等于目标的数最大下标还是最小下标

举个例子:
binsearch([1, 3, 3], 3, "max") 返回 2
binsearch([1, 3, 3], 3, "min") 返回 1
binsearch([1, 1], 2, "max") 和 binsearch([1, 1], 2, "min") 都返回 1
就是只有当数列中有多个等于目标的数时flag参数才有意义。

我写的只考虑flag=="min"的代码,我觉得最后几行if已经很不优雅了,flag=="max"怎么也写不对

def binsearch(a, target, flag="min"):
    ''' Find the index of the max one not greater than target'''
    l, r = 0, len(a)-1
    while (l < r-1):
        mid = l + (r-l) / 2
        if a[mid] >= target:
            r = mid
        else:
            l = mid+1
    if a[l] == target: return l
    if a[r] == target: return r
    if a[r] < target: return r
    if a[l] < target: return l
    return -1

assert(binsearch([1, 3], 1) == 0)
assert(binsearch([1, 3], 3) == 1)
assert(binsearch([1, 3], 2) == 0)
assert(binsearch([1, 1], 2) == 1)
assert(binsearch([1, 1, 3], 1) == 0)
assert(binsearch([2, 2], 1) == -1)
assert(binsearch([1, 3, 5], 1) == 0)
assert(binsearch([1, 3, 5], 5) == 2)
assert(binsearch([1, 3, 5, 5], 5) == 2)
assert(binsearch([1, 1, 1, 2, 2, 2, 3, 4, 5, 5], 2) == 3)


# assert(binsearch([1, 3], 1, "max") == 0)
# assert(binsearch([1, 3], 3, "max") == 1)
# assert(binsearch([1, 3], 2, "max") == 0)
# assert(binsearch([1, 1], 2, "max") == 0)
# assert(binsearch([1, 1, 3], 1, "max") == 1)
# assert(binsearch([2, 2], 1, "max") == -1)
# assert(binsearch([1, 3, 5], 1, "max") == 0)
# assert(binsearch([1, 3, 5], 5, "max") == 2)
# print binsearch([1, 3, 5, 5], 5, "max")
# assert(binsearch([1, 3, 5, 5], 5, "max") == 3)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

人生戏 2022-09-08 07:39:48

我后来又想了想,同时参考了楼上@brayden 的思想,把两种功能的实现完全分开,重新写了一个

def binsearchmax(a, target):
    l, r = 0, len(a)-1
    while (l < r-1):
        mid = l + (r-l) / 2
        if a[mid] <= target:
            l = mid
        else:
            r = mid-1
    if a[r] <= target: return r
    if a[l] <= target: return l
    return -1

def binsearchmin(a, target):
    l, r = 0, len(a)-1
    while (l < r-1):
        mid = l + (r-l) / 2
        if a[mid] < target:
            l = mid
        else:
            r = mid
    if a[l] == target: return l
    if a[r] == target: return r
    if a[r] < target: return r
    if a[l] < target: return l
    return -1

def binsearch(a, target, flag="min"):
    if flag == "min": 
        return binsearchmin(a, target)
    else: 
        return binsearchmax(a, target)

assert(binsearch([1, 3], 1) == 0)
assert(binsearch([1, 3], 3) == 1)
assert(binsearch([1, 3], 2) == 0)
assert(binsearch([1, 1], 2) == 1)
assert(binsearch([1, 1, 3], 1) == 0)
assert(binsearch([2, 2], 1) == -1)
assert(binsearch([1, 3, 5], 1) == 0)
assert(binsearch([1, 3, 5], 5) == 2)
assert(binsearch([1, 3, 5, 5], 5) == 2)
assert(binsearch([1, 1, 1, 2, 2, 2, 3, 4, 5, 5], 2) == 3)


assert(binsearch([1, 3], 1, "max") == 0)
assert(binsearch([1, 3], 3, "max") == 1)
assert(binsearch([1, 3], 2, "max") == 0)
assert(binsearch([1, 1], 2, "max") == 1)
assert(binsearch([1, 1, 3], 1, "max") == 1)
assert(binsearch([2, 2], 1, "max") == -1)
assert(binsearch([1, 3, 5], 1, "max") == 0)
assert(binsearch([1, 3, 5], 5, "max") == 2)
assert(binsearch([1, 3, 5, 5], 5, "max") == 3)

我自己构造的测试数据全部通过了。总结一下,我觉得我一开始试图把两种功能的逻辑混到一起的这个思想是导致我写不出来的一个重要原因。再次感谢楼上的@brayden

别忘他 2022-09-08 07:39:48

嗯要是偷点儿懒的话开始之前往 a 的最前面插 -无穷,最后面插 +无穷,不过这个要求确实很难精简最后四句 if。

update. 想了想可以写一起。。。

def binsearch(a, target, flag="min"):
    if a[0] > target:
        return -1
    a = [-999099] + a + [999099]
    ismin = (flag == "min")
    l, r = 0, len(a) - 1
    while (l < r - 1):
        mid = (l + r) / 2
        if a[mid] > target or ismin and a[mid] == target:
            r = mid
        else:
            l = mid
    if ismin:
        return r - 1 if a[r] == target else l - 1
    else:
        return l - 1
雨后彩虹 2022-09-08 07:39:48

把两种需求写在一个函数里, 逻辑不清楚也给自己找麻烦. 我写的话, 两个函数分别计算max, min. 根据 输入决定去调用哪一个. java代码:

private int findLower(int[] A, int target) {
    int len = A.length;
    int low = 0, high = len - 1;
    while(low <= high){
        int mid = ((high - low) >> 1) + low;
        if((A[mid] < target) && (mid == len - 1 || A[mid + 1] >= target))
            return mid;
        if(A[mid] < target)
            low = mid + 1;
        else // A[mid] >= target
            high = mid - 1;
    }
    return -1;
}

private int findHigher(int[] A, int target) {
    int len = A.length;
    int low = 0, high = len - 1;
    while(low <= high){
        int mid = ((high - low) >> 1) + low;
        if((mid == 0 || A[mid - 1] <= target) && (A[mid] > target))
            return mid;
        if(A[mid] <= target)
            low = mid + 1;
        else // A[mid] > target
            high = mid - 1;
    }
    return len;
}

public int binSearch(int[] A, int target, boolean lower){
    return lower ? findLower(A, target) : findHigher(A, target);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文