我正在使用数组,其中集合已被排序。任务是找到一个值的范围。
让我们假设我们有这个排序的数组:
int[] array = {1,1,1,3,3,9,10}
示例 我们必须找到元素1的范围最小值和最大值。我们很快发现元素1的第一个索引是0,范围max是2.
我们现在知道0到2之间的范围都是值1.如果搜索到的元素是3,我们看到范围是3-4,而对于元素9,范围是6-6。
我已经使用线性搜索来做到这一点,但是如果有更快的方法可以做到这一点会听到吗?
答案 0 :(得分:3)
您可以使用两种版本的二分查找来找到最左侧和最右侧的位置:
int[] array = { 1, 1, 1, 3, 3, 9, 10 }
int value = ...
int leftmost(int min, int max)
{
if (min == max) return min
int mid = (min + max) / 2
if (array[mid] < value) return leftmost(mid + 1, max)
else return leftmost(min, mid)
}
int rightmost(int min, int max)
{
if (min == max) return min
int mid = (min + max + 1) / 2
if (array[mid] > value) return rightmost(min, mid - 1)
else return rightmost(mid, max)
}
只需使用min=0
和max=array.length-1
拨打电话即可。时间复杂度为O(logn)
。
您将获得这样的输出(0-based
索引):
value=1 -> left=0, right=2
value=3 -> left=3, right=4
value=9 -> left=5, right=5
value=10 -> left=6, right=6
答案 1 :(得分:0)
二进制搜索会快得多。您可以对此here
有所了解答案 2 :(得分:0)
假设一个更大的数组使用二进制然后是线性的。
根据数组的大小,更快的方式可能是线性的。 如果您正在使用7个数字的数组重复进行此搜索,那么您将无法看到很多性能差异。事实上它可能会更慢。
然而,扩展该数组虽然你有更多,你最好先做二进制搜索然后进行线性搜索。
执行一次并抓住范围的底部,然后执行线性以找到范围的终点。
你可以为终点做另一个二进制,但是随着时间的推移,应该使用普通线性函数进行平均洗涤。
答案 3 :(得分:0)
虽然@Arturo Menchaca的答案是正确的,但我认为递归解决方案可能很难理解。因此,对于那些喜欢 迭代 方法的人...
public int[] FindRange(int[] arr, int value)
{
int[] upperLowerBounds = new int[2];
int startIndex = 0;
int endIndex = arr.Length - 1;
while (startIndex < endIndex)
{
int midIndex = (startIndex + endIndex) / 2;
if (value <= arr[midIndex])
{
endIndex = midIndex;
}
else
{
startIndex = midIndex + 1;
}
}
// saving startIndex in the result array that will be returned...
upperLowerBounds[0] = startIndex;
endIndex = arr.Length - 1;
while (startIndex < endIndex)
{
int midIndex = (startIndex + endIndex) / 2 + 1;
if (value < arr[midIndex])
{
endIndex = midIndex -1;
}
else
{
startIndex = midIndex;
}
}
upperLowerBounds[1] = endIndex;
return upperLowerBounds;
}
和单元测试。
[TestCase(new int[] { -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, -3, 1, 9)] // search element = -3
[TestCase(new int[] { -4, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, 6, 4, 4)] // search element = 6
[TestCase(new int[] { -4, -3, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, 10, 6, 11)] // search element = 10
[TestCase(new int[] { -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -1, -1, -1, -1, -1, -1, 1, 6, 8, 10, 10, 10, 10, 10, 10, 25 }, -1, 10, 17)] // search element = -1
[TestCase(new int[] { 1, 1, 1, 3, 3, 9, 10 }, 3, 3, 4)] // search element = 3
[TestCase(new int[] { 1, 1, 1, 3, 3, 9, 10 }, 1, 0, 2)] // search element = 1
public void FindRangeTest(int[] arr, int value, int expectedStartIndex, int expectedEndIndex)
{
int[] range = runner.FindRange(arr, value);
// Assert.
Assert.AreEqual(expectedStartIndex, range[0]);
Assert.AreEqual(expectedEndIndex, range[1]);
}
我用过C#和nunit 3.10