查找数组中介于两个值之间的元素

0 投票
2 回答
5283 浏览
提问于 2025-04-17 17:12

问题是这样的:

首先,我在用Python编程。我有一个有序的自然数数组(是Numpy数组,不过如果有帮助的话我可以把它改成列表),叫做“givenY”。我想找到并指向在两个指定值之间的第一个和最后一个元素,a=Y[i]b=Y[i+1]。我写了代码,但我觉得我写得很糟糕,而且不确定代码的效率如何。所以如果能得到一些评论或者建议我从头开始写会很高兴。重要的是,有很多特殊情况,比如在 Y[i]Y[i+1] 之间没有元素,这种情况我用 -1 来表示起始位置。我的代码是:

startRes=binSearch(givenY,Y[i]);
endRes=binSearch(givenY,Y[i+1]);        
start=startRes[1]
end=endRes[1];        
if(givenY.size==0 or (givenY.size>0 and givenY[start]<=Y[i])):
    start=startRes[1]+1;
if(endRes[0]):
    end=endRes[1]-1;
if end<start or (givenY.size>0 and (givenY[end]>Y[i+1] or givenY[start]>=Y[i+1])) or givenY[end]<=Y[i]:
    start=-1;

startRes=binSearch(givenY,a);
endRes=binSearch(givenY,b);        
start=startRes[1]
if startRes[0]:
    start=start+1;
end=endRes[1]-1;        

这是二分查找的实现:

def binSearch(arr,element):
left=0
right=arr.size;
mid=(left+right)/2
while left<right:
    mid=(left+right)/2
    if(arr[mid]<element):
        left=mid+1;
    elif (arr[mid]>element):
        right=mid;
    else: 
        return True,mid;
return False,left;

一些简单的输入和输出:

对于 givenY=[2,5,8,10]:

  • a=3,b=4,输出:没有中间值。(在我的代码中,start=-1)
  • a=2,b=5,输出:没有中间值。(在我的代码中,start=-1)
  • a=2,b=9,输出:start=1,end=2
  • a=1,b=10,输出:start=0,end=2
  • a=1,b=11,输出:start=0,end=3
  • a=11,b=12,输出:没有中间值。(在我的代码中,start=-1)
  • a=0,b=2,输出:没有中间值。(在我的代码中,start=-1)
  • a=3,b=3,输出:没有中间值。(在我的代码中,start=-1)
  • a=5,b=5,输出:没有中间值。(在我的代码中,start=-1)

在我目前工作的情况下,b总是大于a。

非常感谢。

2 个回答

1

先把列表排序,然后再进行线性搜索。

去掉分号,它们不是必须的,也没有必要...

3

我对返回的索引有点不太明白。比如说,如果 givenY 是一个空列表,那么 startend 都会是 -1。而且,你发的代码没有处理列表中的重复值。

与其自己手动写二分查找,不如使用 bisect 模块。想了解更多,可以查看API文档:

  1. Python 3.3 - 8.6. bisect — 数组二分算法
  2. Python 2.7.3 - 8.5. bisect — 数组二分算法

下面是一个实现,它返回 startend,以确保以下几点:

  1. end-start 等于在给定范围内的元素数量。
  2. list[start:end] 返回包含所有在给定范围内的值的切片。
  3. end-start 等于找到的元素数量。
  4. 当没有找到任何值时,start==end

代码:

import unittest

from bisect import bisect_left, bisect_right


def find_range(array, a, b):
    start = bisect_right(array,a)
    end = bisect_left(array,b)
    return (start, end)


class TestCase(unittest.TestCase):
    Y = [1, 3, 5, 10, 15]
    givenY = [3, 4, 5, 6, 7, 8, 9, 10, 11]

    def test_empty_array(self):
        self.assertEqual( (0, 0), find_range([], 1, 2) )

    def test_all_values_larger(self):
        self.assertEqual( (0, 0), find_range([4,5,6], 1, 3) )

    def test_all_values_larger_or_equal(self):
        self.assertEqual( (0, 0), find_range(self.givenY, self.Y[0], self.Y[1]) )

    def test_both_endpoints_inside_list(self):
        self.assertEqual( (1, 2), find_range(self.givenY, self.Y[1], self.Y[2]))
        self.assertEqual( [4], self.givenY[1:2])

    def test_2(self):
        self.assertEqual( (3, 7), find_range(self.givenY, self.Y[2], self.Y[3]) )
        self.assertEqual( [6, 7, 8, 9], self.givenY[3:7])

    def test_no_values_larger_or_equal_to_upper_limit(self):
        self.assertEqual( (8, 9), find_range(self.givenY, self.Y[3], self.Y[4]) )
        self.assertEqual( [11], self.givenY[8:9])


if __name__=="__main__":
    unittest.main()

注意:返回的起始和结束位置如果需要,可以很容易地调整为你当前的值,只要确保它们是一致的。

编辑

下面是返回所请求值的代码,尽我所能理解给出的示例。逻辑在 find_range() 的文档字符串中有描述。原始代码保持不变,因为我觉得在Python编程时这样更自然。

import unittest

from bisect import bisect_left, bisect_right


def find_range(array, a, b):
    """Find elements that are greater than a and less than b.
    Returns a tuple (start,end) where array[start] is the first
    value and array[end] is the last value.
    If no value is found, returns start=end=-1.
    """
    start = bisect_right(array,a)
    end = bisect_left(array,b)
    if start==end:
        return (-1,-1)
    else:
        return (start, end-1)


class TestCase(unittest.TestCase):
    Y = [1, 3, 5, 10, 15]
    givenY = [3, 4, 5, 6, 7, 8, 9, 10, 11]

    def test_empty_array(self):
        self.assertEqual( (-1, -1), find_range([], 1, 2) )

    def test_all_values_larger(self):
        self.assertEqual( (-1, -1), find_range([4,5,6], 1, 3) )

    def test_all_values_larger_or_equal(self):
        self.assertEqual( (-1, -1), find_range(self.givenY, self.Y[0], self.Y[1]) )

    def test_both_endpoints_inside_list(self):
        self.assertEqual( (1, 1), find_range(self.givenY, self.Y[1], self.Y[2]))

    def test_2(self):
        self.assertEqual( (3, 6), find_range(self.givenY, self.Y[2], self.Y[3]) )

    def test_no_values_larger_or_equal_to_upper_limit(self):
        self.assertEqual( (8, 8), find_range(self.givenY, self.Y[3], self.Y[4]) )

    def test_sample(self):
        self.assertEqual( (3,3), find_range([1,3,5,7], 5, 8)  )
        self.assertEqual( (3,3), find_range([1,3,5,7], 6, 8)  )


if __name__=="__main__":
    unittest.main()

撰写回答