我试图了解以下算法的工作原理。
#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=0
和r=10
,它会检查当前不存在的if (l>r)
,因此会计算m=(0+10)/2;
。然后再次执行新边界的过程。第一对是(0,5),第二对是(6,10),在最后一次操作之后,它比较两个返回的值,最后返回它们之间的最大元素。
此算法是否始终有效?在每次迭代中,它不做任何比较,只做最后一步。如何确定每次递归迭代的最大元素?它只检查什么。例如:取对(0,5),是(0超过5)?不,所以再次重复并将这些边界分成两个,所以再次得到新的平均值m1 =(0 + 5)/ 2然后再返回一些元素但不是最大值。对于第二个子阵列,我们也可以这样说。
该算法的主要思想是什么?
答案 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 :(得分: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)