我正在学习递归,我想用一些" n"来重写下面的代码。递归方面的嵌套循环数
for (int a = 0; a < N; a++) { //first loop
for (int b = a + 1; b < N; b++) { //second loop
for (int c = b + 1; c < N; c++) { // and for example n-th loop
str = " ( " + Integer.toString(a)
+ " , " + Integer.toString(b)
+ " , " + Integer.toString(c) +" ) ";
stringList.add(str);
}
}
}
当我打电话给
时System.out.println(stringList);
输出应如下
[ ( 0 , 1 , 2 ) , ( 0 , 1 , 3 ) , ( 0 , 2 , 3 ) , ( 1 , 2 , 3 ) ]
对于N = 4和3个嵌套循环或
[ ( 0 , 1 , 2 ) , ( 0 , 1 , 3 ) , ( 0 , 1 , 4 ) , ( 0 , 2 , 3 ) , ( 0 , 2 , 4 ) , ( 0 , 3 , 4 ) , ( 1 , 2 , 3 ) , ( 1 , 2 , 4 ) , ( 1 , 3 , 4 ) , ( 2 , 3 , 4 ) ]
表示N = 5和3个嵌套循环
我曾尝试写过代码
public static void recursionLoop(int i, int N, int M){ //M is the number of loops
//N is the highest number the programm should reach
List<Integer> list = new ArrayList<>();
for (int a = i; a < N - 1; a++) {
if (M > 0) {
smartIter(i + 1, N, M - 1);
list.add(a);
}
}
我无法弄清楚如何重现我的代码来控制循环次数和&#34; N&#34;使用递归的数字
请帮助我,我真的想要解决它应该写的方式和递归的逻辑
答案 0 :(得分:1)
看看你的循环,即它们如何相互关联。举个例子,让我们来看第一个和第二个:
for (int a = 0; a < N; a++) {
for (int b = a + 1; b < N; b++) {
正如您所看到的,每个循环使用3个值/变量:
a
,b
),让我们称之为x
N
0
,a + 1
),让我们称之为start
因此两个循环都可以重写为
for (int x = start; x < N; x++ ) { ... }
现在将其放入方法中并为递归添加条件:
void runInnerLoop( int start, int N) {
for (int x = start; x < N; x++ ) {
runInnerLoop( x + 1, N );
}
}
请注意,在您的情况下,您还需要将字符串列表作为参数传递,并在递归之前向其添加字符串。
另请注意,我没有包含任何额外的检查是否进行递归调用。这样你最终会以start == N
结束,但循环条件会采取这种方式。添加像if( x < N - 1 )
这样的条件可以防止以额外的条件评估为代价的不必要的递归调用。哪个选项更快还有待观察,但由于这可能不太相关,我选择更容易阅读的版本。
但是,由于您当前正在学习递归:请记住始终检查是否可以在所有情况下达到中止条件。否则你会遇到StackOverflowError
等等 - 如果你的N
太大,这种情况也可能发生,在这种情况下,你需要采用不同的方法。
修改强>
我必须承认,我忽略了你问题中的一条信息,即你称之为M
的递归深度有限。因此,我将使用该信息扩展我的答案,并且我还将解释如何获得&#34;列表&#34;你想打印。
基本上处理递归深度很简单:你将每个调用的当前深度+1传递给最大深度,然后在进行递归调用之前检查depth < maxDepth
- 如果这是真的,你做一个递归call,如果是false,则打印当前分支(即&#34; list&#34; of integer)。
处理列表并不难,但有效地进行操作需要一些思考。基本上每次迭代都会添加一个新列表,即0,1,2
和0,2,3
是不同的列表。
如果您使用列表,则必须将当前列表传递给递归调用,并在循环内创建新列表,将顶部列表的所有值添加到其中,最后添加当前列表循环变量(例如List<Integer> list = new ArrayList<>( topList ); list.add(x);
)。
但是,您只需要最深呼叫中的列表,即打印或收集时。这意味着所有这些中间列表都浪费时间和内存。因此,您可能希望使用Stack
来跟踪当前循环值,即在迭代开始时将当前值推送到堆栈并在迭代结束时删除最高值。这样,您可以重用相同的堆栈并仅打印当前循环变量(或将它们收集到新列表中供以后使用)。
这里是使用递归深度和堆栈的顶级示例:
void runInnerLoop( int start, int N, int depth, int maxDepth, Stack<Integer> stack) {
for (int x = start; x < N; x++ ) {
stack.push( x ); //add the current value to the stack
if( depth < maxDepth ) { //max depth not reached, do another recursion
runInnerLoop( x + 1, N, depth + 1, maxDepth, stack );
} else {
System.out.println( stack ); //max depth reached, print now
}
stack.pop(); //remove the top value from the stack
}
}
第一个例子(N = 4,maxDepth = 3)的初始调用如下所示:
runInnerLoop( 0, 4, 1, 3, new Stack<>() );
答案 1 :(得分:0)
更新4
static List<List<Integer>> f(int init, int N, int depth, List<Integer> ls) {
List<List<Integer>> all = new ArrayList<>();
for(int i = init; i < N; i++) {
List<Integer> ls_ = new ArrayList<Integer>();
ls_.addAll(ls);
ls_.add(i);
if(depth == 0) {
all.add(ls_);
} else {
all.addAll(f(i + 1, N, depth-1, ls_));
}
}
return all;
}
<强>原始强>
如果没有太多损坏,这可能对您有所帮助:
for (int i = 0; i < 10; i++)
System.out.println(i);
在语义上与:
相同public static void f(final int i) {
if (i < 10) {
System.out.println(i);
For.f(i + 1);
}
}
您还会注意到:
while(i < 10) {
System.out.println(i);
i++;
}
基本上是相同的东西,可以以相同的方式转换为递归。
我不建议像这样编码,但你可以用这种方式将每个for循环重写为递归函数。 for循环不会消耗尽可能多的堆栈空间。
当您嵌套for循环时,您可以使用相同的算法翻译它们:
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
System.out.println(x + "," + y);
成为两个函数f,g
:
public static void f(final int x) {
if (x < 3) {
For.g(x, 0);
For.f(x + 1);
}
}
public static void g(final int x, final int y) {
if (y < 3) {
System.out.println(x + "," + y);
For.g(x, y + 1);
}
}
<强>更新强>
回答评论:
嵌套了多少个for循环并不重要。每个for循环都可以重写为递归函数。如果你有N
嵌套的for循环,你可以将它们重写为N
递归函数。方法如下:
如果您有for(TYPE i = INIT; CONDITION; P-EXPRESSION) { CODE }
,可以将其翻译为:
function NAME(TYPE i) {
if(CONDITION) {
CODE
P-EXPRESSION
NAME(i); //this is the recursive call
}
}
NAME(INIT)
如果你有嵌套的for循环,那么方法是相同的,但你必须将局部变量作为参数传递。
for(int a = INIT_A; CONDITION_A; P-EXPRESSION_A) {
CODE_A
for(int b = INIT_B; CONDITION_B; P-EXPRESSION_B) {
CODE_B
//^- if you use a in this you have to pass a as a parameter
for(int c = INIT_C; CONDITION_C; P-EXPRESSION_C) {
CODE_C
//^- same deal
}
}
}
大致变成(伪代码):
function f(int a) {
if(CONDITION_A) {
CODE_A
g(a, INIT_B);
P-EXPRESSION_A
f(a);
}
}
function g(int a, int b) {
if(CONDITION_B) {
CODE_B
h(a,b, INIT_C);
P-EXPRESSION_B
g(a, b);
}
}
function h(int a, int b, int c) {
if(CONDITION_C) {
CODE_C
P-EXPRESSION_C
h(a,b,c);
}
}
f(INIT_A)
更新2
使用上述方法直接1:1翻译为您提供:
public static List<String> f(int a, int N, List<String> stringList) {
if(a < N) {
g(a, a + 1, N, stringList);
f(a + 1, N, stringList);
}
return stringList;
}
public static List<String> g(int a, int b, int N, List<String> stringList) {
if(b < N) {
h(a, b, b+1, N, stringList);
g(a, b+1, N, stringList);
}
return stringList;
}
public static List<String> h(int a, int b, int c, int N, List<String> stringList) {
if(c < N) {
String str = " ( " + Integer.toString(a)
+ " , " + Integer.toString(b)
+ " , " + Integer.toString(c) +" ) ";
stringList.add(str);
h(a, b, c+1, N, stringList);
}
return stringList;
}
它不是漂亮/最佳代码,但它是每个for循环到递归函数的转换。
更新3
更好的翻译形式如下:
h a b c n
|c < n = (a,b,c) : h a b (succ c) n
|otherwise = []
g a b n
|b < n = h a b (succ b) n ++ g a (succ b) n
|otherwise = []
f a n
|a < n = g a (succ a) n ++ f (succ a) n
|otherwise = []
main = print $ f 0 4
但这会将java转换为:
public static List<String> f(int a, int N) {
if(a < N) {
List<String> ls = new ArrayList<String>();
ls.addAll(g(a, a + 1, N));
ls.addAll(f(a + 1, N));
return ls;
}
return new ArrayList<String>();
}
public static List<String> g(int a, int b, int N) {
if(b < N) {
List<String> ls = new ArrayList<String>();
ls.addAll(h(a,b,b+1,N));
ls.addAll(g(a,b+1,N));
return ls;
}
return new ArrayList<String>();
}
public static List<String> h(int a, int b, int c, int N) {
if(c < N) {
String str = " ( " + Integer.toString(a)
+ " , " + Integer.toString(b)
+ " , " + Integer.toString(c) +" ) ";
List<String> ls = new ArrayList<String>();
ls.add(str);
ls.addAll(h(a,b,c+1,N));
return ls;
}
return new ArrayList<String>();
}
哪种Java效率不高:)。