acc*_*_so 7 java algorithm recursion divide-and-conquer
如果函数中只有一个递归调用,我就能轻松理解递归.但是,当我在同一个函数中看到两个或多个递归调用时,我真的很困惑.例:
int MaximumElement(int array[], int index, int n)
{
int maxval1, maxval2;
if ( n==1 ) return array[index];
maxval1 = MaximumElement(array, index, n/2);
maxval2 = MaximumElement(array, index+(n/2), n-(n/2));
if (maxval1 > maxval2)
return maxval1;
else
return maxval2;
}
Run Code Online (Sandbox Code Playgroud)
我理解在每次递归调用期间n会减半的一件事.我只是不明白下一个递归调用是如何工作的.它变得混乱和我的理解,直到那一点崩溃,我放弃了.如果有人可以用一个简洁的例子手动说明,我真的很感激.我已经完成了编程,并打印了输出.但是,我不明白这项工作背后的计算方法.这是我的理解,直到一切都变得一无所获:
int a [] = {1,2,10,15,16,4,8}
初始调用:MaximumElement(a,0,7)
该函数开始:第一次调用:MaximumElement(a,0,7/2)n现在变为7/2 = 3
第二次调用:MaximumElement(2,0,3/2)n现在变为3/2 = 1
满足基本条件,max1得到[0] = 1
这里是所有地狱破裂的地方:第二次递归调用从索引0开始,n =索引+ n/2 = 0 + 1/2 = 0?当我打印值时,程序在第二次调用时显示3作为n的值.
我已经进行了广泛的编程,但我真的对此有一个噩梦.非常感谢有人可以为我打破这个!
这是上面的伪代码,但是看到我编写的java代码(如果你试图运行它可能会让你更容易):
public int MAXIMUMELEMENT(int a[], int i, int n)
{
int max1, max2;
System.out.println("1: " + i + " 2: " + n);
if(n == 1)
{
System.out.println("Returning " + a[i]);
return a[i];
}
max1 = MAXIMUMELEMENT(a, i, n/2);
System.out.println("Index: "+i+" "+" Variable: "+max1+" n value: "+n);
max2 = MAXIMUMELEMENT(a, i + (n/2), n - (n/2));
System.out.println("Index2: " + i + " " + "Variable2: " + max2);
if(max1 > max2)
{
System.out.println("Returning.... " + max1 );
return max1;
}
else
{
System.out.println("Returning.... " + max2);
return max2;
}
}
Run Code Online (Sandbox Code Playgroud)
Ray*_*oal 14
听起来你已经理解了基本情况并知道递归是如何工作的,所以理解你的特定例子的关键是要注意给定的初始数组
a = [1,2,10,15,16,4,8]
Run Code Online (Sandbox Code Playgroud)
你是在"顶级"计算两件事:
maxval1 = MaximumElement(array, 0, 3);
maxval2 = MaximumElement(array, 3, 4);
Run Code Online (Sandbox Code Playgroud)
这说
maxval1
从在范围从数组的最大值从大小3的索引0maxval2
从该范围中的阵列的最大值从大小为4的指数3所以
maxval1
确实会是10岁maxval2
确实会是16岁你的答案是16.
关于递归的好处是你不必担心过于广泛地追踪事物.如果您信任您的基本案例以及您了解基本案例的方式,那么理解一个级别就足够了.
我认为你被困在你说"所有地狱都松散"的地方,因为第二次递归调用以起始索引为0开始.它没有.它从索引3开始.(也就是说,假设您的第二个递归调用是一个计算maxVal2
).
这里有一个关于计算如何运作的缩略图.我已经冒昧地将你的功能重命名为m
并且假设maxVal1
并且maxVal2
计算得更多"功能性".
a = [1,2,10,15,16,4,8]
m(a, 0, 7)
= m(m(a, 0, 3), m(a, 3, 4))
= m(m(m(a, 0, 1), m(a, 1, 2)), m(a, 3, 4))
= m(m(a[0], m(a, 1, 2)), m(a, 3, 4))
= m(m(1, m(a, 1, 2)), m(a, 3, 4))
= m(m(1, m(m(a, 1, 1), m(a, 2, 1)), m(a, 3, 4))
= m(m(1, m(a[1], a[2])), m(a, 3, 4))
= m(m(1, m(2, 10)), m(a, 3, 4))
= m(m(1, 10), m(a, 3, 4))
= m(10, m(a, 3, 4))
= …
= 16
Run Code Online (Sandbox Code Playgroud)
我不确定我是否能够很好地解释它,但我会用斐波纳契来解释它.计算斐波纳契数的递归方法是:
public static int getFib(int n) {
if(n <= 2) return 1;
return getFib(n-1)+getFib(n-2);
}
Run Code Online (Sandbox Code Playgroud)
在代码中实际发生的事情是它显然会在方法调用之前进行第一次返回.所以getFib(n-1)
将继续调用,直到n <= 2
那时它将返回方法堆栈,因为它现在有一个getFib(n-1)的值,它将调用getFib(n-2).所以说我们的初始调用是4,会发生什么:
getFib(4) //Initial call
getFib(4-1=3) //Left hand recursive call level 1
getFib(3-1=2) //Left hand recursive call level 2
return 1 //This would be level 3
getFib(3-2=1) //Right hand recursive call level 2
return 1 //level 3
getFib(4-2=2) //Right hand recursive call level 1
return 1
Run Code Online (Sandbox Code Playgroud)
不确定这是否有意义,这个图像可能会形象化: 递归斐波纳契树http://www.fortystones.com/wp-content/uploads/2011/08/fibonacci-recursion-tree.png
上面的代码基本上会使深度优先(将左边的孩子放在第一位)遍历该树.
在我看来,您混淆了递归调用的运行顺序。请记住,在第一次调用 (maxval1) 完成之前,不会调用第二次调用 (maxval2)。maxval1 调用本身还有两个递归调用,依此类推。所以如果没有完成所有这些内部递归调用,程序就不会到达 maxval2 行。
尝试调试而不是运行代码(例如在 Eclipse 中)并逐步移动以查看它实际上如何处理每个递归调用。