【问题标题】:end index in binary search二分查找中的结束索引
【发布时间】:2015-07-25 04:01:29
【问题描述】:

我有几个关于二分搜索的问题。

  1. 在下面的代码中,我从 main 传递 fun(array, 0, len-1)。是否也可以将“len”作为结束索引传递?我认为没关系,也有经过验证的答案。

我觉得唯一的区别是在中间元素之后的数组部分会少计算一次,而在中间元素之前的数组部分会额外计算一次。

  1. 此外,在执行 mid = (s+e)/2 时,通常整数除法采用底值。如果我们做 mid = (ceiling)(s+e)/2。同样,我觉得唯一的区别是在中间元素之后的数组部分将少计算一次,而在中间元素之前的数组部分将多计算一次。

下面是我的代码

#include <stdio.h>    
int key = 0;
int len = 0;

int main(void)
{
 int a[] = {1,2,3,4,5,6,7,8,9};

 printf("Enter key : ");
 scanf("%d",&key);

 len = sizeof(a)/sizeof(*a) - 1;

 printf("Array : ",print(a));
 //printf("Element found at index : %d",fun(a,0,len-1));
 printf("Element found at index : %d",fun(a,0,len));

 printf("\n");
 return 0;
}

int fun(int a[], int s, int e)
{
 int mid = 0;
 mid = (s+e)/2;

 if(a[mid] == key)
  return mid;

 if(s == e)
  return -1;

 if(a[mid] > key)
  fun(a,s,mid-1);
 else
  fun(a,mid+1,e);
}
int print(int a[])
{
 int i = 0;
 for(i=0;i<len;i++)
  printf("%d ",a[i]);
 printf("\n");
}

【问题讨论】:

  • 你到底想用 printf("Array : ",print(a)); 做什么?您需要使用 for 循环来打印数组中的所有值。此外,您已经静态初始化了数组。那你为什么要计算 len 呢?
  • @abhishek_naik,忘记复制打印功能。请参阅上面的编辑。
  • 请注意,print() 的输出将出现在函数printf() 的任何输出之前,该函数通过(但不使用)来自print() 的不存在的返回值。这不会给您想要的结果;标签将出现在数据之后,而不是之前。
  • 如果你尝试搜索一个空数组怎么办?
  • 另外,如果len 不代表数组的长度,那会非常令人困惑。定义清晰的语义:你希望它代表数组长度,还是希望它代表最后一个有效索引?

标签: c arrays binary-search


【解决方案1】:

是的,传递lenlen-1 只会改变递归调用的次数,对于中间元素之前的元素和中间元素之后的元素,它会有所不同。

使用ceil 也是如此。因此,是的,根据数组中的index,不同元素的递归调用次数可能会有所不同。

这两种变化都会给你正确的结果。

【讨论】:

  • 谢谢。但是对于通过 len 和 len-1 的两种情况,在计算次数方面是否存在可以观察到的模式?例如,传递 len 之类的东西会导致 mid 右侧的计算量减少,但 mid 左侧的计算量增加。我觉得无法观察到这样的模式,因为如果左侧的计算确实说 (2+3)/2 = 2 而不是 (2+2),那么通过传递 len 在右侧节省的额外计算可能会丢失/2 = 2。所以在这两种情况下都是 2。请注意我的原始代码 len = sizeof(a)/sizeof(*a);没有-1。
  • @PepperBoy 看看你是否想在这个数组中搜索4,那么当你通过len时,递归调用将是3,因为它是在中间,但是如果你通过len-1然后递归调用的数量将是0。您可以轻松地让您的程序计数调用次数。这将清除您对此的想法。
  • 谢谢,是的,我明白了。最后一个问题是,作为最终索引传递的最佳方法是什么?连?伦-1?别的东西? ----->(正如我上面所说,请注意在我的原始代码中 len = sizeof(a)/sizeof(*a); 最后没有-1。所以,在你之前的帖子中,通过 len -2 将给出 0 个递归调用,传递 len-1 将给出 3 个递归调用。)
  • @PepperBoy 在您发布的代码中,len = sizeof(a)/sizeof(*a) -1 存在。
【解决方案2】:
  1. 如果你通过len 代替len-1 它不会有太大变化。如果len 是奇数,那么len-1 将是偶数并且两者都会给出相同的mid 值。
    如果len 是偶数,那么您在中间位置有两个元素。这里len-1 将给我们第一个中点,len 将给我们第二个。
  2. 如果你使用ceil((s+e)/2)获得mid位置,那么即使len也不会影响更多,即使你通过lenlen-1它也会给第二个中间位置.但是在奇数len 的情况下,如果您将len 作为e 传递,则它不会生成偶数分区。如果你传递len-1 也没关系。

【讨论】:

  • 有人可以解释一下投票这个答案的原因吗?等我收到回复后,我会支持它。
  • 如果他有正当理由,他会在否决答案之前发表评论。
【解决方案3】:

您可以同时传递lenlen-1,而不会对结果进行任何更改,除非它不会在最后一个索引处找到值。

如果您更改此行len = sizeof(a)/sizeof(*a) - 1;

len = sizeof(a)/sizeof(*a);

那么lenlen-1 都可以正常工作。

您还应该返回您的fun 函数以获取值并在主函数中打印之前检查它。

【讨论】:

  • len = sizeof(a)/sizeof(*a); 朋友这是错误的。它分配len = 9。而且a[9] 不存在。
  • 你是对的,但是你错过了一件事,那就是在递归调用中,如果键在最后一个索引中(即 8),那么该递归调用中 mid 的值将为 8,则 mid = (s+e)/2 将是 (8+9)/2,即 17/2 即 8,然后是 if(s==e) return -1; 语句。因此,mid=9 的条件永远不会达到。这就是为什么我提到要检查每次调用以及主函数中的返回值。该代码适用于所有奇数和偶数长度。
  • 也可以代替sizeof(*a)sizeof(int) 可能会更好,但这里没问题。
  • @RahulBhartari sizeof(*a)sizeof(int) 在这种情况下是相同的,sizeof *a 通常更可取,因为它不易出错。
  • @GajendraBagali 公平地说,sizeof a / sizeof *a 是一种计算数组长度的常用方法。真正的问题是len在原始代码中不代表数组长度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多