嵌套循环递归

时间:2016-09-15 12:55:11

标签: java recursion

我正在学习递归,我想用一些" 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;使用递归的数字

请帮助我,我真的想要解决它应该写的方式和递归的逻辑

2 个答案:

答案 0 :(得分:1)

看看你的循环,即它们如何相互关联。举个例子,让我们来看第一个和第二个:

for (int a = 0; a < N; a++) {      
    for (int b = a + 1; b < N; b++) {      

正如您所看到的,每个循环使用3个值/变量:

  • 循环变量(ab),让我们称之为x
  • 最大值N
  • 初始值(0a + 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,20,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效率不高:)。