C# 二进制搜索算法的扩展,用于查找数组中要搜索的键值的第一个和最后一个索引

C# 二进制搜索算法的扩展,用于查找数组中要搜索的键值的第一个和最后一个索引,c#,c++,c,algorithm,binary-search,C#,C++,C,Algorithm,Binary Search,问题是扩展二进制搜索算法,以最有效的方式查找排序数组中目标值的所有匹配项。 具体地说,该算法的输入是(1)一个整数排序数组,其中一些数字可能出现多次,以及(2)要搜索的目标整数。算法的输出应该是一对索引值,指示数组中第一次和最后一次出现的整数(如果确实出现)。 源代码可以在C,C,C++中。 P>也就是说,对于C++,我们可能需要找到的索引的最大值和最小值是多少?,可以查找 STD::等量范围()/CUT>及其复杂性要求。只要您对基本算法感兴趣,那么无论实现使用何种语言,都应该应用相同的一般规

问题是扩展二进制搜索算法,以最有效的方式查找排序数组中目标值的所有匹配项。 具体地说,该算法的输入是(1)一个整数排序数组,其中一些数字可能出现多次,以及(2)要搜索的目标整数。算法的输出应该是一对索引值,指示数组中第一次和最后一次出现的整数(如果确实出现)。 源代码可以在C,C,C++中。
<> P>也就是说,对于C++,我们可能需要找到的索引的最大值和最小值是多少?

,可以查找<代码> STD::等量范围()/CUT>及其复杂性要求。只要您对基本算法感兴趣,那么无论实现使用何种语言,都应该应用相同的一般规则。

我想普通算法中会有这样的内容:

if(value == test) return;
if(value < test) min = i;
if(value > test) max = i;

等等。

如果你有点聪明,你可以定义两个不同的二进制搜索函数。一个将返回搜索值的第一次出现的索引,另一个将返回搜索值的最后一次出现的索引。根据您对二进制搜索的了解,您应该能够确定最大和最小比较次数

在我看来,平均而言,使用两个二进制搜索应该是最快的方法。例如,如果只使用一个二进制搜索来查找第一项,然后线性搜索,最坏的情况是整个函数的值相同。对于长度为10000的数组,这将在最坏情况下进行10013次比较,而对于同一数组,使用两次二进制搜索将在最坏情况下进行28次比较。当然,使用相同大小的数组时,二进制/线性搜索方法的最佳情况是14次比较,而两次二进制搜索方法的最佳情况是26次比较

**更新

好的,这里是一个二进制搜索,用于查找数组中元素的第一个外观。我将给您一个递归函数(当然,您可以使其迭代并以其他方式对此进行优化)。这将在int数组中搜索int val。另外,我在寻找中点时也不小心(如果数组真的很大,可能会有问题)

但是,在返回索引后,应该检查它是否实际引用了正确的值,因为如果数组中没有val,则返回的索引将对应于比val大的下一个元素


对其进行一些小的更改将生成一个查找最后一个元素的函数。做到这一点的关键是正确使用比较器,并记住整数除法总是截断。

这非常容易做到,无需编写自己的二进制搜索算法,只需反复调用标准算法

// some curly-bracket language:

// int BinarySearch(sortedList, searchIndex, searchLength, valueToFind)
// returns the zero-based index of the item in the list, or a negative value
// if the item is not found

int inner = BinarySearch(list, 0, listSize, value);
if(inner < 0){
    // handle case where value is not found in list
}

int bottom = inner, top = inner;
while(true){
    int i = BinarySearch(list, 0, bottom, value);
    if(i < 0)
        break;
    bottom = i;
}
while(true){
    int i = BinarySearch(list, top + 1, listSize - top - 1, value);
    if(i < 0)
        break;
    top = i;
}

// bottom and top now hold the bounds of all instances of value in list
//一些花括号语言:
//int二进制搜索(sortedList、searchIndex、searchLength、valueToFind)
//返回列表中项目的从零开始的索引或负值
//如果找不到该项目
int inner=BinarySearch(list,0,listSize,value);
如果(内部<0){
//处理在列表中找不到值的情况
}
内部底部=内部,顶部=内部;
while(true){
int i=二进制搜索(列表,0,底部,值);
if(i<0)
打破
底部=i;
}
while(true){
inti=BinarySearch(list,top+1,listSize-top-1,value);
if(i<0)
打破
top=i;
}
//底部和顶部现在保持列表中所有值实例的边界
这与使用自定义算法获得的效率非常接近,只是有更多的函数调用开销

至于比较的数量,我必须想得更清楚一点,但我认为它只是2*log2N,其中N是列表中的项目数


编辑

呸!它不是2*log2N,因为与自定义算法不同,它不会递增地排除列表中的部分。看起来最大比较计数为(log2N-0.5)*log2N。对于一个包含230个元素的列表来说,这仍然是885次比较(220N为390次比较,210N为95次比较),但我们可以做得更好

// int Compare(a, b)
// returns 0 if a and b are equal,
//         a negative value if a < b, or
//         a positive value if a > b

int start = 0, end = listSize, inner;

while(true){
    if(end == start){
        // handle case where value is not found in list
    }
    inner = (start + end) / 2;
    int cmp = Compare(list[inner], value);
    if(cmp == 0)
        break;
    if(cmp < 0)
        start = inner + 1;
    else end = inner;
}

int top = inner, bottom = inner;

while(true){
    if(start >= bottom)
        break;
    inner = (start + bottom) / 2;
    int cmp = Compare(list[inner], value);
    if(cmp == 0)
        bottom = inner;
    else start = inner + 1;
}

while(true){
    if(end - 1 <= top)
        break;
    inner = (top + 1 + end) / 2;
    int cmp = Compare(list[inner], value);
    if(cmp == 0)
        top = inner;
    else end = inner;
}
//整数比较(a,b)
//如果a和b相等,则返回0,
//如果ab,则为正值
int start=0,end=listSize,内部;
while(true){
如果(结束==开始){
//处理在列表中找不到值的情况
}
内部=(开始+结束)/2;
int cmp=比较(列表[内部],值);
如果(cmp==0)
打破
if(cmp<0)
开始=内部+1;
else端=内部;
}
int顶部=内部,底部=内部;
while(true){
如果(开始>=底部)
打破
内部=(开始+底部)/2;
int cmp=比较(列表[内部],值);
如果(cmp==0)
底部=内部;
else start=内部+1;
}
while(true){

如果(end-1对于问题的最有效部分没有明确的答案。这取决于预期有多少个具有相同值的条目。如果是少数元素,则在找到一个元素后在数组的两个方向上进行线性搜索将是最快的选择,但如果预期有许多具有相同值的条目,则可以d进行某种二进制搜索,以找到起始-结束索引

免责声明:未经测试;它旨在展示想法,而不是直接用作生产代码

int org = binarySearch(array,value) //do the binary search and find on element
int min = org-delta; //delta is some constant based on how many elemts are to be expected
int max = org;
min = min < 0 ? 0 : min;
int search= min;
bool latestWasHit = false;
while(search > 0)
{
  if(search+1 == max)
     return max;
  if(array[search] != value)
  {
     min = search;
     search = search + (max-search)/2
  }
  else
  {
     max = search;
     search = (search-min)/2;
  } 
}
int org=binarySearch(数组,值)//执行二进制搜索并在元素上查找
int min=org delta;//delta是一个基于预期元素数量的常数
int max=org;
min=min<0?0:min;
int search=min;
bool latestWasHit=false;
while(搜索>0)
{
如果(搜索+1==最大值)
返回最大值;
if(数组[搜索]!=值)
{
min=搜索;
搜索=搜索+(最大搜索)/2
}
其他的
{
max=搜索;
烧焦
int bs1(int a[], int val, int left, int right)
{
    if(right == left) return left;
    int mid = (right+left)/2;

    if(val > a[mid]) return bs1(a, val, mid+1, right);
    else return bs1(a, val, left, mid);
}
// some curly-bracket language:

// int BinarySearch(sortedList, searchIndex, searchLength, valueToFind)
// returns the zero-based index of the item in the list, or a negative value
// if the item is not found

int inner = BinarySearch(list, 0, listSize, value);
if(inner < 0){
    // handle case where value is not found in list
}

int bottom = inner, top = inner;
while(true){
    int i = BinarySearch(list, 0, bottom, value);
    if(i < 0)
        break;
    bottom = i;
}
while(true){
    int i = BinarySearch(list, top + 1, listSize - top - 1, value);
    if(i < 0)
        break;
    top = i;
}

// bottom and top now hold the bounds of all instances of value in list
// int Compare(a, b)
// returns 0 if a and b are equal,
//         a negative value if a < b, or
//         a positive value if a > b

int start = 0, end = listSize, inner;

while(true){
    if(end == start){
        // handle case where value is not found in list
    }
    inner = (start + end) / 2;
    int cmp = Compare(list[inner], value);
    if(cmp == 0)
        break;
    if(cmp < 0)
        start = inner + 1;
    else end = inner;
}

int top = inner, bottom = inner;

while(true){
    if(start >= bottom)
        break;
    inner = (start + bottom) / 2;
    int cmp = Compare(list[inner], value);
    if(cmp == 0)
        bottom = inner;
    else start = inner + 1;
}

while(true){
    if(end - 1 <= top)
        break;
    inner = (top + 1 + end) / 2;
    int cmp = Compare(list[inner], value);
    if(cmp == 0)
        top = inner;
    else end = inner;
}
int org = binarySearch(array,value) //do the binary search and find on element
int min = org-delta; //delta is some constant based on how many elemts are to be expected
int max = org;
min = min < 0 ? 0 : min;
int search= min;
bool latestWasHit = false;
while(search > 0)
{
  if(search+1 == max)
     return max;
  if(array[search] != value)
  {
     min = search;
     search = search + (max-search)/2
  }
  else
  {
     max = search;
     search = (search-min)/2;
  } 
}
public static void main(String[] args) {
    int a[] ={1,2,2,2,2,2,5,5,6,8,9,10};

    System.out.println(5+" first = "+first(a, 5, 0, a.length-1));
    System.out.println(5+" last = "+right(a, 5, 0, a.length-1));

    System.out.println(1+" first = "+first(a, 1, 0, a.length-1));
    System.out.println(1+" last = "+right(a, 1, 0, a.length-1));

    System.out.println(2+" first = "+first(a, 2, 0, a.length-1));
    System.out.println(2+" last = "+right(a, 2, 0, a.length-1));

    System.out.println(10+" first = "+first(a, 10, 0, a.length-1));
    System.out.println(10+" last = "+right(a, 10, 0, a.length-1));

    System.out.println(8+" first = "+first(a, 8, 0, a.length-1));
    System.out.println(8+" last = "+right(a, 8, 0, a.length-1));

    System.out.println(11+" first = "+first(a, 11, 0, a.length-1));
    System.out.println(11+" last = "+right(a, 11, 0, a.length-1));


}

private static int first(int [] a, int x, int l, int h){
    if(l>h){
        return -1;
    }
    int mid = (h-l)/2+l;
    if(a[mid] == x && (mid==0 || a[mid-1] != x) ){
        return mid;
    }else if(a[mid] == x){
        return first(a, x, l, mid-1);
    }else if(a[mid]>x){
        return first(a, x, l, mid-1);
    }else{
        return first(a, x, mid+1, h);
    }
}


private static int right(int [] a, int x, int l, int h){
    if(l>h){
        return -1;
    }
    int mid = (h-l)/2+l;
    if(a[mid] == x && (mid==a.length-1 || a[mid+1] != x) ){
        return mid;
    }else if(a[mid] == x){
        return right(a, x, mid+1, h);
    }else if(a[mid]>x){
        return right(a, x, l, mid-1);
    }else{
        return right(a, x, mid+1, h);
    }
}

Output:
    1 first = 0
    1 last = 0
    2 first = 1
    2 last = 5
    10 first = 11
    10 last = 11
    8 first = 9
    8 last = 9
    11 first = -1
    11 last = -1