划分和征服算法以找到数组的最大元素

时间:2011-09-06 12:49:51

标签: c++ arrays

我试图了解以下算法的工作原理。

#include <iostream>
using namespace std;
int maxsimum(int a[], int l, int r) {
    if (l == r)  
        return a[l];
    int m = (l+r)/2;
    int u = maxsimum(a,l,m);
    int v = maxsimum(a,m+1,r);
    return u>v?u:v;    
}

int main() {
    int a[] = {34,23,45,56,30,31,57,33,55,10};
    int n = sizeof(a)/sizeof(int);
    cout << maxsimum(a,0,n) << endl;         
    return 0;
}

首先,我感兴趣的是,尽管算法正常工作,但对于我如何找到最大元素却是神秘的。我将展示我如何理解这个算法:

第1步:我们说如果是数组l=0r=10,它会检查当前不存在的if (l>r),因此会计算m=(0+10)/2;。然后再次执行新边界的过程。第一对是(0,5),第二对是(6,10),在最后一次操作之后,它比较两个返回的值,最后返回它们之间的最大元素。

此算法是否始终有效?在每次迭代中,它不做任何比较,只做最后一步。如何确定每次递归迭代的最大元素?它只检查什么。例如:取对(0,5),是(0超过5)?不,所以再次重复并将这些边界分成两个,所以再次得到新的平均值m1 =(0 + 5)/ 2然后再返回一些元素但不是最大值。对于第二个子阵列,我们也可以这样说。

该算法的主要思想是什么?

6 个答案:

答案 0 :(得分:4)

你的困惑是可以理解的:所写的算法包含一些错误。它会在a结束时访问内存,这非常非常糟糕。此外,测试范围是否仅包含一个元素是不正确的。如果没有解决,这会导致堆栈溢出。

调用maxsimum函数的方式表明下限包含在范围内,但上限不包含在范围内。 a[0]有效,但a[n]访问内存超过a的结尾。分割范围时,我们希望第一部分从l运行到但不包括m,第二部分从m开始并运行到但不包括r。换句话说:第一部分的“排他”上限等于第二部分的“包含”下限。对maxsimum的第一次内部调用是正确的。第二个内部呼叫应该是:

int v=maxsimum(a,m,r); 

这给我们留下了检测长度范围1的问题。就目前而言,该算法实际上寻找范围。正确的测试是查看上限和下限之间的差异:

if (r-l == 1) return a[l];

完整的功能如下:

int maxsimum(int a[],int l,int r){
   if (r-l==1)  return a[l];
   int m=(l+r)/2;
   int u=maxsimum(a,l,m);
   int v=maxsimum(a,m,r);
   return u>v?u:v;    
}

现在我们有一个正确的程序,对其工作方式的解释很简单:

  1. 如果范围只包含一个元素,则此元素为最大值。
  2. 如果范围包含多个元素,我们将其分为两部分。我们递归调用函数来计算每个部分的最大值。这两个值中的最大值是整个范围的最大值。

答案 1 :(得分:3)

主要思想是,如果我们将数组分成2个子数组,那么最大值必须位于数组的左侧或右侧;没有其他可能性。

所以我们在左边部分找到最大值,我们在右边部分找到最大值,全局最大值显然是两个最大值之间的最大值,即maxsimum函数的最后一行返回的最大值。 / p>

答案 2 :(得分:2)

您的错误在这里:

  

在每次迭代中,它不做任何比较,只做最后一步。

这是错误的。实际上,它在递归的每个步骤中进行了比较(除了在基本情况下,即数组大小为1)。

答案 3 :(得分:1)

让我为您评论代码的最大部分,尽量不要添加混淆:

if (l==r)  return a[l]; //trivial case, if there is only one value in the array return it
int m=(l+r)/2; //find value halfway into the array
int u=maxsimum(a,l,m); //find the maximum value for the lower part of the array
int v=maxsimum(a,m+1,r); //find the maximum value for the top part of the array
return u>v?u:v; //return the highest value of them.

所以数组0..10被分成0..5和6..10并传递给同一个函数。只有当只有一个值时,递归才会结束,并且单个值将传递给它们的被调用者。然后在第二个最低的情况下,如值a [0]和[1],它将进行第一次比较。这些结果将被传递给更高的案例,直到它将在最后时间退出函数,返回所有案例的最大值。

我希望能为你澄清一点。

答案 4 :(得分:1)

main()函数出错,测试数组有10个元素,应为:

cout << maxsimum(a,0,n-1) << endl;

答案 5 :(得分:1)

这个答案可能会这么晚,但是对于某人掌握递归调用可能是有用的,我修改了上面的代码来追踪函数调用。 在看到输出之后,很容易看到如何制作递归树。

#include <iostream>
using namespace std;
int maxsimum(int a[], int l, int r) {
if (l == r)  
    return a[l];
int m = (l+r)/2;

cout<<"values gonna get computed in 1st recursive call"<< l<<" "<< m<<"\n";
int u = maxsimum(a,l,m);

cout<<"value of u "<<u<<"\n";

cout<<"value gonna get computed in 2nd recursion  call "<<m+1 <<" "<<r<<"\n";
int v = maxsimum(a,m+1,r);

cout<<"value of v : "<<v<<"\n";
cout<<"current u value :"<<u <<"current v value "<<v <<"\n";

return u>v?u:v;    
}

int main() {
int a[] = {5,6,7,8,9};
int n = sizeof(a)/sizeof(int);
cout << maxsimum(a,0,n-1) << endl;         
return 0;
}

这是上面程序的递归树,树首先走向左侧,即第一个递归语句,然后每个调用返回其基值,返回条件确保每次调用中只选择最大元素

                 (9)  
                (0,4)
              /      \
           7 /        \9
        (0,2)          (3,4)
         /  \          /    \
       6/    \7      8/      \9
       (0,1)  (2,2)  (3,3)     (4,4) 
        / \      
      5/   \6
     (0,0) (1,1)