为什么二元搜索是一种分而治之的算法?

时间:2012-01-13 12:17:41

标签: algorithm data-structures computer-science binary-search divide-and-conquer

我被问到二元搜索是否是考试中的分而治之算法。我的回答是肯定的,因为你将问题分成了较小的子问题,直到你达到了结果。

但考官们询问其中的征服部分在哪里,我无法回答。他们也不赞成它实际上是一种分而治之的算法。

但是我到网上的所有地方都说它是,所以我想知道为什么,征服它的部分在哪里?

17 个答案:

答案 0 :(得分:13)

这本书

Data Structures and Algorithm Analysis in Java, 2nd edtition, Mark Allen Weiss

说D& C算法应该有两个不相交的递归调用。我喜欢QuickSort。二进制搜索没有这个,即使它可以递归实现,所以我想这就是答案。

答案 1 :(得分:9)

我认为这不是分而治之,请参阅http://en.wikipedia.org/wiki/Divide_and_conquer_algorithm

中的第一段
  

递归地将问题分解为两个或更多个子问题   然后组合起来给出解决方案

在二进制搜索中,仍然只有一个问题就是每一步只减少一半的数据,因此不需要征服(合并)阶段的结果。

答案 2 :(得分:4)

分而治之的策略:

1.问题分为几个部分;

2.通过应用手头的算法(主要是递归用于此目的),这些部分中的每一个都被独立攻击/解决;

3.然后将每个分区/分区的解决方案合并/合并在一起,以达到问题的最终解决方案(这是征服

示例,快速排序,合并排序。

基本上,二进制搜索算法在每次迭代中将其工作空间(大小为n的输入(有序)数组)分成两半。因此,它肯定会部署 divide 策略,因此,时间复杂度降低到O(lg n)。因此,这掩盖了它的“鸿沟”部分。

可以注意到,最终的解决方案是从最后的比较中获得的,也就是说,当我们只剩下一个元素进行比较时。 二进制搜索不会合并或组合解决方案。

简而言之,二进制搜索将问题(它必须在其上工作)的大小分成两半但却没有找到解决方案的碎片和碎片。因此无需合并解决方案!

我知道它有点太冗长但我希望它有所帮助:)。

您也可以从https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/running-time-of-binary-search

获得一些想法

我刚刚意识到这个问题很久以前发布了! 我的坏!

答案 3 :(得分:2)

除数部分当然将该组分成两半。

征服部分是确定处理部分中的位置和搜索元素是否存在。

答案 4 :(得分:2)

显然有些人认为二元搜索是一种分而治之的算法,有些则不然。我快速搜索了三个引用(似乎都与学术界有关),称之为D& C算法: http://www.cs.berkeley.edu/~vazirani/algorithms/chap2.pdf http://homepages.ius.edu/rwisman/C455/html/notes/Chapter2/DivConq.htm http://www.csc.liv.ac.uk/~ped/teachadmin/algor/d_and_c.html

我认为D& C算法至少应该至少包含这三个阶段的前两个阶段:

  • 划分,即决定整个问题如何分成子问题;
  • 征服,即独立解决每个子问题;
  • [可选]组合,即将独立计算的结果合并在一起。

第二阶段 - 征服 - 应该递归地应用相同的技术来通过划分甚至更小的子子问题等来解决子问题。然而,在实践中,通常使用一些阈值来限制递归方法,如对于小尺寸问题,不同的方法可能会更快。例如,快速排序实现通常使用例如当要排序的数组部分的大小变小时,冒泡排序。

第三阶段可能是无操作,并且在我看来它不会使算法失去D& C的资格。一个常见的例子是for - 循环的递归分解,所有迭代完全使用独立的数据项(即不减少任何形式)。它看起来可能看起来毫无用处,但事实上它是非常强大的方式,例如并行执行循环,并由Cilk和Intel的TBB等框架使用。

回到最初的问题:让我们考虑一些实现算法的代码(我使用C ++;对不起,如果这不是您熟悉的语言):

int search( int value, int* a, int begin, int end ) {
  // end is one past the last element, i.e. [begin, end) is a half-open interval.
  if (begin < end)
  {
    int m = (begin+end)/2;
    if (value==a[m])
      return m;
    else if (value<a[m])
      return search(value, a, begin, m);
    else
      return search(value, a, m+1, end);
  }
  else // begin>=end, i.e. no valid array to search
    return -1;
}

这里的除法部分是int m = (begin+end)/2;,其余部分都是征服部分。该算法明确地以递归D&amp; C形式编写,即使只采用其中一个分支。但是,它也可以以循环形式编写:

int search( int value, int* a, int size ) {
  int begin=0, end=size;
  while( begin<end ) {
    int m = (begin+end)/2;
    if (value==a[m])
      return m;
    else if (value<a[m])
      end = m;
    else
      begin = m+1;
  }
  return -1;
}

我认为用循环实现二进制搜索是一种很常见的方式;我故意使用与递归示例中相同的变量名称,以便更容易看到通用性。因此,我们可以说,再次计算中点是分界部分,循环体的其余部分是征服部分。

但当然,如果你的考官有不同的想法,可能很难说服他们是D&amp; C.

更新:只是想到如果我要开发D&amp; C算法的通用骨架实现,我肯定会使用二进制搜索作为API适用性测试之一来检查API是否足够强大而且简洁。当然,它没有任何证据:)

答案 5 :(得分:2)

除了@Kenci's post之外,DnC算法还有一些通用/常用属性;它们:

  1. 将原始问题实例划分为一组较小的子实例;
  2. 独立解决每个子实例;
  3. 合并较小/独立的子实例解决方案,为更大/原始实例构建单一解决方案
  4. 二进制搜索的问题在于它确实甚至生成了一组要解决的独立子实例,如步骤1所示;只有通过永久丢弃它不感兴趣的部分来简化原始问题。换句话说,它只会减少问题的大小和#&# 39; s尽可能远。

    DnC算法不仅应该彼此独立地识别/解决原始问题的较小子实例,而且还要使用该组部分独立解决方案来构建&#34;构建&#34;整个问题实例的单一解决方案。

    本书 算法基础,G。Brassard,P。Bratley 表示以下内容(我的重点是粗体,原文中的斜体):

      

    这可能是分而治之的最简单的应用,事实上很简单严格来说这是简化的应用而不是分而治之 :任何足够大的实例的解决方案都会减少到单个较小实例的解决方案,在这种情况下是一半的。

    p.226 上的 7.3二进制搜索

答案 6 :(得分:1)

二元搜索很难用分而治之来描述,因为征服步骤并不明确。该算法的结果是大海捞针的索引,纯D&amp; C实现将在最小的干草堆中返回针的索引(单元素列表中的0),然后递归添加在分裂步骤中划分的较大草垛的偏移量。

伪代码解释:

function binary_search has arguments needle and haystack and returns index
    if haystack has size 1
       return 0
    else 
        divide haystack into upper and lower half
        if needle is smaller than smallest element of upper half
            return 0 + binary_search needle, lower half
        else
            return size of lower half + binary_search needle, upper half

添加(0 +size of lower half)是征服部分。大多数人通过将索引作为参数提供给更大的列表来跳过它,因此它通常不易获得。

答案 7 :(得分:1)

正确的分而治之算法将需要处理这两个部分。

因此,许多人调用二进制搜索分治算法,它会划分问题,但丢弃另一半。

但最有可能的是,您的审查员只是想看看您如何争辩。 (好)考试不是关于事实,而是关于当挑战超出原始材料时你的反应。

恕我直言,正确的答案是:

  

嗯,从技术上讲,它只包含一个除法步骤,但是只需要征服原始任务的一半,另一半只是已经完成了。

BTW:有一个很好的QuickSort变种,名为QuickSelect,它实际上利用这种差异来获得平均O(n)中值搜索算法。它就像QuickSort - 但只下降到它感兴趣的一半。

答案 8 :(得分:1)

Divide and Conquer算法基于以下3个步骤:

  1. 鸿沟
  2. 联合
  3. 二进制搜索问题可以定义为在排序数组A [n]中查找x。 根据这些信息:

    1. 除以:将x与中间
    2. 进行比较
    3. 征服:递归一个子阵列。 (在此数组中查找x)
    4. 结合:没有必要。

答案 9 :(得分:0)

二分搜索是一种减法算法,而不是分而治之

二分搜索是一种减少和征服算法,其中子问题的大小大约是原始大小的一半,历史悠久。另一种古老的减少和征服算法是欧几里得算法,用于计算最大公约数 通过将数字减少到越来越小的等效子问题,可以追溯到公元前几个世纪。

分而治之是解决概念性难题的强大工具:它所需要的只是将问题分解为子问题、解决小问题以及将子问题与原始问题组合的方法。类似地,减少和征服只需要将问题简化为一个更小的问题,例如经典的河内塔谜题,将移动高度为 n 的塔简化为移动高度为 n-1 的塔。

参考文献:link 1link 2

答案 10 :(得分:0)

Merge SortQuick Sort算法使用分而治之技术(因为存在两个子问题),并且Binary Search处于降低并征服(因为存在1个子问题)。

因此,二进制搜索实际上使用的是减少征服技术,而不是分治技术。

来源:https://www.geeksforgeeks.org/decrease-and-conquer/

答案 11 :(得分:0)

不,二元搜索不是分而治之的。是的,二元搜索减少并征服。我认为分而治之算法的效率为O(n log(n)),而减少和征服算法的效率为O(log(n))。不同之处在于您是否需要评估数据中的拆分的两个部分。

答案 12 :(得分:0)

二进制搜索和三元搜索算法基于减少和征服技术。因为,你没有划分问题,你实际上通过除以2(三元搜索中的3)来减少问题。

合并排序和快速排序算法可以作为分而治之技术的示例给出。您将问题分成两个子问题,并再次使用算法对这些子问题进行排序。但是,你在二进制搜索中丢弃了一半的数组。这意味着你减少了数组的大小,而不是除以。

答案 13 :(得分:0)

我认为这是减少和征服。

以下是维基百科的引用。

  

“已经建议改名和征服   单子问题类“

http://en.wikipedia.org/wiki/Divide_and_conquer_algorithms#Decrease_and_conquer

根据我的理解,当您找到二进制搜索的目标元素时,“征服”部分就在最后。 “减少”部分正在减少搜索空间。

答案 14 :(得分:0)

计算机科学中的二分法是指在两种不同的选择之间选择两种对立选择。二分法是将整体划分为恰好两个非重叠部分,这意味着它是一个整体分为两部分的过程。它是整个(或一组)分为两部分(子集)的分区,它们是: 1.共同穷举:一切必须属于一方或另一方,并且 2.互斥:没有任何东西可以同时属于这两个部分。

通过递归地将问题分解为两个或更多相同类型的子问题来划分和征服作品,直到这些问题变得足够简单直接解决。

因此,二进制搜索将每次迭代检查的项目数减半,并确定它是否有机会找到&#34;键&#34;如果它能够确定按键缺失,那一半中的项目或移动到另一半。由于算法本质上是二分法的,因此二进制搜索将相信&#34;键&#34;必须在一个部分,直到它到达退出条件,它返回密钥丢失。

答案 15 :(得分:0)

二进制搜索是一种分而治之的算法:

1)在Divide and Conquer算法中,我们尝试通过解决一个较小的子问题(Divide part)来解决问题,并使用该解决方案为我们更大的问题(Conquer)构建解决方案。

2)这里我们的问题是在排序数组中找到一个元素。我们可以通过解决类似的子问题来解决这个问题。 (我们在这里根据被搜索的元素比中间元素更小或更大的决定创建子问题)。因此,一旦我们知道元素不能确定存在于一半中,我们就会在另一半中解决类似的子问题。

3)这样我们就可以了。

4)这里的征服部分只是将子问题返回的值返回到递归树的顶部

答案 16 :(得分:0)

非正式定义或多或少:将问题分成小问题。然后解决它们并将它们放在一起(征服)。实际上,解决决定下一步(左,右,找到元素)。

来自wikipedia的引用:

  

名称“分而治之”有时也适用于将每个问题减少到一个子问题的算法,例如用于在排序列表中查找记录的二进制搜索算法。

这说明, NOT [更新:误读这句话:)只是分而治之的一部分。

<强>更新 This文章对我说清楚。我很困惑,因为定义说你必须解决每个子问题。但是如果你知道你不必继续搜索,你就解决了这个问题..