重构这个递归方法?

时间:2008-10-29 01:08:25

标签: java recursion

我对递归的想法很新,这实际上是我第一次尝试编写递归方法。

我试图实现一个递归函数Max,它传递一个数组,以及一个保存数组大小的变量,以便打印出最大的元素。

它有效,但它只是感觉正确!

我也注意到我似乎比一般的同学更多地使用静态修饰符......

有人可以提供任何一般提示以及如何改进我的代码的反馈吗?

public class RecursiveTry{

static int[] n = new int[] {1,2,4,3,3,32,100};
static int current = 0;
static int maxValue = 0;
static int SIZE = n.length;

public static void main(String[] args){
    System.out.println(Max(n, SIZE));
}   

public static int Max(int[] n, int SIZE) {
    if(current <= SIZE - 1){
        if (maxValue <= n[current]) {
            maxValue = n[current];
            current++;
            Max(n, SIZE);                       
        }
        else {
            current++;
            Max(n, SIZE);
        }
    }
    return maxValue;
}

}

12 个答案:

答案 0 :(得分:9)

使用静态变量来保持函数外的状态将是一个难点。

伪代码中max()函数的递归实现示例可能是:

function Max(data, size) {
    assert(size > 0)
    if (size == 1) {
        return data[0]
    }
    maxtail = Max(data[1..size], size-1)
    if (data[0] > maxtail) {
        return data[0]
    } else {
        return maxtail
    }
}

这里的关键是对Max()的递归调用,在那里传递除了第一个元素之外的所有内容,以及一个小于大小的内容。一般的想法是这个函数说“这个数据中的最大值是第一个元素,或者是数组其余部分中值的最大值,以较大者为准”。

此实现不需要函数定义之外的静态数据。

递归实现的标志之一是所谓的“终止条件”,它阻止递归永远继续(或者,直到你得到堆栈溢出)。在上述情况下,size == 1的测试是终止条件。

答案 1 :(得分:4)

使您的函数依赖于静态变量不是一个好主意。这里有可能实现递归Max函数:

int Max(int[] array, int currentPos, int maxValue) {
    // Ouch!
    if (currentPos < 0) {
        raise some error
    }
    // We reached the end of the array, return latest maxValue
    if (currentPos >= array.length) {
        return maxValue;
    }
    // Is current value greater then latest maxValue ?
    int currentValue = array[currentPos];
    if (currentValue > maxValue) {
        // currentValue is a new maxValue
        return Max(array, currentPos + 1, currentValue);
    } else {
        // maxValue is still a max value
        return Max(array, currentPos + 1, maxValue);
    }
}
...

int[] array = new int[] {...};
int currentPos = 0;
int maxValue = array[currentPos] or minimum int value;  
    maxValue = Max(array, currentPos, maxValue);

答案 2 :(得分:3)

“max”函数是编写递归函数的错误类型 - 事实上你使用“current”和“maxValue”的静态值会使你的函数不是真正的递归函数。

为什么不做一些更适合递归算法的东西,比如factorial?

答案 3 :(得分:2)

“未功课”?

反正。首先要做的事情。在

static int[] n = new int[] {1,2,4,3,3,32,100};
static int SIZE = n.length;

与他们共享姓名的Max()参数无关。将这些移到main并丢失“静态”说明符。当从main()内部调用Max()的第一个实例时,它们只被使用一次。它们的范围不应超出main()。

Max()的所有调用都没有理由共享一个“当前”索引。 “当前”应该是Max()的本地。但那么Max()的连续复发将如何知道使用“当前”的值? (提示:Max()已经将其他Max()的数据向下传递给某些数据。将“当前”添加到此数据中。)

同样的事情适用于maxValue,尽管这里的情况有点复杂。你不仅需要在行下传递当前的“maxValue”,而且当递归完成时,你必须将它一直传递回第一个Max()函数,它将把它返回到main()。您可能需要查看其他一些递归示例,并花一些时间来处理这个。

最后,Max()本身是静态的。一旦消除了引用外部数据(静态变量)的需要;这并不重要。它只是意味着您可以调用Max()而无需实例化对象。

答案 4 :(得分:2)

正如其他人所观察到的,没有需要来实现Max函数的递归,但使用熟悉的算法来试验新概念可能是有益的。所以,这是简化的代码,下面有一个解释:

public class RecursiveTry
{
    public static void main(String[] args)
    {
        System.out.println(Max(new int[] {1,2,4,3,3,32,100}, 0, 0));
    }   

    public static int Max(int[] n, int current, int maxValue) 
    {
        if(current < n.Length)
        {
            if (maxValue <= n[current] || current == 0))
            {
                return Max(n, current+1, n[current]);
            }
            return Max(n, current+1, maxValue);
        }
        return maxValue;
   }
}

所有静态都不必要了;相反,一切都在堆栈上传递。 Max函数的内部逻辑是简化的,我们以两种不同的方式递归,只是为了好玩

答案 5 :(得分:2)

这是适合您的Java版本。

public class Recursion {

    public static void main(String[] args) {
        int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        System.out.println("Max: " + max(0, data));
    }

    public static int max(int i, int[] arr) {
        if(i == arr.length-1) {
            return arr[i];
        }

        int memo = max(i+1, arr);
        if(arr[i] > memo) {
            return arr[i];
        }
        return memo;
    }
}

重复关系是数组的最大元素是第一个元素,或者是数组其余部分的最大元素。到达阵列末尾时达到停止条件。注意使用memoization将递归调用(大致)减少一半。

答案 6 :(得分:0)

您实际上是在编写迭代版本,但使用尾递归进行循环。此外,通过将如此多的变量设为静态,您实际上使用的是全局变量而不是对象。这是尝试更接近典型的递归实现。当然,在现实生活中,如果您使用的Java语言不能优化尾调用,那么您可以使用循环实现“Max”函数。

public class RecursiveTry{
  static int[] n;

  public static void main(String[] args){
        RecursiveTry t = new RecursiveTry(new int[] {1,2,4,3,3,32,100});
        System.out.println(t.Max());
  }       

  RecursiveTry(int[] arg) {
    n = arg;
  }

  public int Max() {
    return MaxHelper(0);
  }

  private int MaxHelper(int index) {
    if(index == n.length-1) {
      return n[index];
    } else {
      int maxrest = MaxHelper(index+1);
      int current = n[index];
      if(current > maxrest)
        return current;
      else
        return maxrest;
    }
  }
}

答案 7 :(得分:0)

你确实过度使用静态。你也不需要那么多的全局变量。尝试移动

static int [] n = new int [] {1,2,4,3,3,32,100}; static int current = 0; static int maxValue = 0; static int SIZE = n.length;

进入main()函数,使它们成为局部变量。

public static void main(String[] args)
{
  int[] n = new int[] {1,2,4,3,3,32,100};
  int current = 0;
  int maxValue = 0;
  int SIZE = n.length;
  ...other code

}

不是您的主要问题的解决方案,但使用局部变量优于全局变量(一般情况下)

---当我完成这个时,我意识到我只是在激活aib所说的

答案 8 :(得分:0)

在Scheme中,这可以非常简洁地写出来:

(define (max l)
    (if (= (length l) 1)
        (first l)
        (local ([define maxRest (max (rest l))])
          (if (> (first l) maxRest)
              (first l)
              maxRest))))

当然,这使用链接列表而不是数组,这就是为什么我没有传递一个大小元素,但我觉得这将问题的本质提炼出来。这是伪代码定义:

define max of a list as:
    if the list has one element, return that element
    otherwise, the max of the list will be the max between the first element and the max of the rest of the list

答案 9 :(得分:0)

以递归方式获取数组最大值的一种更好方法是实现quicksort(这是一个很好的递归排序算法),然后只返回第一个值。

以下是一些Java code for quicksort

答案 10 :(得分:0)

我能得到的最小代码:

public class RecursiveTry {
    public static void main(String[] args) {
        int[] x = new int[] {1,2,4,3,3,32,100};
        System.out.println(Max(x, 0));
    }   

    public static int Max(int[] arr, int currPos) {
        if (arr.length == 0) return -1;
        if (currPos == arr.length) return arr[0];
        int len = Max (arr, currPos + 1);
        if (len < arr[currPos]) return arr[currPos];
        return len;
    }
}

一些事情:

1 /如果数组为零大小,则返回最大值-1(您可以使用另一个标记值,例如-MAX_INT,或者抛出异常)。我在这里假设代码清晰度假设所有值都是零或更多。否则我会用各种不必要的东西填写代码(关于回答问题)。

2 /在我看来,如果终止案例是无数据而不是最后数据,大多数递归都是“更干净”,因此当我们完成数组时,我返回的值保证小于或等于max 。其他人可能会有不同意见,但不会是他们错误的第一次或最后一次: - 。

3 /递归调用只获得列表其余部分的最大值,并将其与当前元素进行比较,返回两者中的最大值。

4 /'理想'解决方案是在每次递归调用时传递一个修改过的数组,这样你只需要将第一个元素与列表的其余部分进行比较,从而无需使用currPos。但这本来是效率低下的,并且会消除SO的愤怒。

5 /这可能不一定是最好的解决方案。可能是由于灰色物质因过多使用LISP及其CAR,CDR和那些无休止的括号而受到损害。

答案 11 :(得分:-2)

首先,让我们来处理静态范围问题......您的类正在定义一个对象,但实际上并没有实例化它。由于main是静态范围的,首先要做的是获取一个对象,然后执行它的方法:

public class RecursiveTry{

    private int[] n = {1,2,4,3,3,32,100};

    public static void main(String[] args){
        RecursiveTry maxObject = new RecursiveTry();
        System.out.println(maxObject.Max(maxObject.n, 0));
    }

    public int Max(int[] n, int start) {
        if(start == n.length - 1) {
            return n[start];
        } else { 
            int maxRest = Max(n, start + 1);
            if(n[start] > maxRest) {
                return n[start];
            }
            return maxRest;
        }
    }

}

所以现在我们有一个名为maxObject的RecursiveTry对象,它不需要静态范围。我不确定使用递归找到最大值是有效的,因为传统循环方法中的迭代次数大致相等,但使用递归时使用的堆栈量更大。但是对于这个例子,我会把它削减很多。

递归的一个优点是,在重复测试期间,您的状态通常不需要像在迭代中那样持久化。在这里,我承认使用变量来保存起点,因为传递包含除第一个项目之外的所有项目的新int []的CPU密集程度较低。