在编写递归算法时使用'static'是否作弊?

时间:2016-05-20 20:46:18

标签: c algorithm recursion

作为编程分配的一部分,我需要编写一个递归函数来确定数组中的最大整数。引用确切的任务:

  

编写递归函数,找到给定列表中的最大数字   整数。

我提出了两个解决方案,第一个解决方案是两个递归调用:

int largest(int arr[], int length){
    if(length == 0)
        return 0;
    else if(arr[length - 1] > largest(arr,length -1))
        return arr[length];
    else return largest(arr,length -1);
}

第二个只使用一个,但它使用静态变量n

int largest(int arr[], int length){
    static int n = -1;
    if(length == 0)
        return n;
    else if (arr[length - 1] > n)
        n = arr[length - 1];
    return largest(arr, length - 1);
}

我想知道是否会将作弊用于此类任务的静态变量。无论哪种方式,哪一种被认为是更好的形式?是否存在一种顶生于两者的递归方法?

5 个答案:

答案 0 :(得分:8)

我不会说以这种方式使用import lxml变量是作弊的 - 我会说这是不正确的。 : - )

想象一下,您在许多不同的阵列上多次调用此函数。引入静态变量后,static的值永远不会在调用之间重置,因此最终可能会返回错误的值。一般来说,设置这样的编码风格通常很差,因为它很容易得到错误的答案。此外,如果您的数组仅包含负值,则可以返回-1作为答案,即使-1实际上更大而不是数组中的所有内容。

我认为第二个版本比第一个版本有一个很好的优势 - 它更快,更快,因为它只进行一次递归调用而不是两次。考虑使用第一个版本,但更新它以便缓存递归调用返回的值,这样就不会进行两次调用。这将以指数方式加速代码;初始版本需要时间Θ(2 n ),而更新版本需要时间Θ(n)。

答案 1 :(得分:5)

没有任何作弊使用静态内部函数,递归或其他方式。

为什么这样做有很多充分的理由,但在你的情况下,我怀疑你提出了一个错误的解决方案 - 因为largest只会在程序运行的一生中工作一次它

考虑以下(伪)代码;

main() {
    largest([ 9, 8, 7]) // would return 9 -- OK
    largest([ 1, 2, 3]) // would return 9 ?? bad
}

原因是你的largest无法区分这两个电话,但如果这就是你想要的,那就没关系。

修改 在回答你的评论时,像这样的东西会比你的初始代码有更好的大写符号;

int largest(int arr[], int length){
    int split, lower,upper;
    switch (length) {
      case 1: return arr[0];
      case 2: if (arr[1]>arr[0]) return arr[1]; else return arr[0];
      default:
              if (len <= 0) throw error;
              split = length/2;
              lower = largest(arr,split);
              upper = largest(arr+split,length-split);
              if (lower > upper) return lower; else return upper;
    }
}

或者,明显的解决方案是;

int largest(int arr[], int length){
    if (length <= 0) thor error;
    int max = arr[0];
    for (int i=1; i<length; i++)
       if (arr[i] > max) max = arr[i];
    return max;
}

根本没有递归

答案 2 :(得分:3)

它实际上是一个糟糕的设计,因为在第二次执行该函数时不会返回正确的结果。

如果错误的话,我认为你不需要辩论它是否在作弊。

第一个版本也不正确,因为您返回的是arr[length]而不是arr[length-1]。您可以消除第二次递归调用。您可以做什么而不是使用相同的参数调用相同的函数(没有副作用)两次?

答案 3 :(得分:1)

除了前三个答案中的优点之外,你应该练习更多基于递归的思维。 (1)处理琐碎的案件。 (2)对于一个非平凡的案例,减少任务并重复(较小的)剩余问题。

我建议你的正确基础案例是一个项目的列表:返回该项目。空列表没有最大元素。

对于递归情况,检查第一个元素与列表其余部分的最大值;返回更大。在近代码形式中,这看起来如下所示。它只进行一次递归调用,并且只有一个显式局部变量 - 这就是作为递归结果的别名。

int largest(int arr[], int length){
    if(length == 1)
        // if only one element, return it
        return arr[0];
    else n = largest(arr,length-1))
        // return the larger of the first element or the remaining largest.
        return arr[length-1] > n ? arr[length-1] : n
}

答案 4 :(得分:1)

  

是否存在一个超过两者的递归方法?

使用/** * Draws a route on a map. */ public class MapRouteDrawer { private static final SplineInterpolator splineInterpolator = new SplineInterpolator(); /** * Draws the route to the screen, does nothing if null. */ public static void drawRoute(final Graphics2D graphics, final RouteDescription routeDescription, final MapPanel view, final MapData mapData, final String movementLeftForCurrentUnits) { if (routeDescription == null) { return; } final Route route = routeDescription.getRoute(); if (route == null) { return; } final Point[] points = getRoutePoints(routeDescription, mapData); final int xOffset = view.getXOffset(); final int yOffset = view.getYOffset(); final int jointsize = 10; final int numTerritories = route.getAllTerritories().size(); //set thickness and color of the future drawings graphics.setStroke(new BasicStroke(3.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); graphics.setPaint(Color.red); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if(Arrays.asList(points).contains(null)){//If the Array is null at some point return; } if(numTerritories <= 1 || points.length <= 2){ drawLineWithTranslate(graphics, new Line2D.Float(routeDescription.getStart(), routeDescription.getEnd()), xOffset, yOffset); graphics.fillOval((routeDescription.getEnd().x - xOffset) - jointsize / 2, (routeDescription.getEnd().y - yOffset) - jointsize / 2, jointsize, jointsize); } else{ drawCurvedPath(graphics, points, view); } } } private static double[] getIndex(Point[] points) { final double[] index = new double[points.length]; for(int i = 0; i < points.length; i++){ index[i] = i; } return index; } private static void drawLineWithTranslate(Graphics2D graphics, Line2D line2D, double translateX, double translateY) { final Line2D line = (Line2D) line2D; final Point2D point1 = new Point2D.Double(line.getP1().getX() - translateX, line.getP1().getY() - translateY); final Point2D point2 = new Point2D.Double(line.getP2().getX() - translateX, line.getP2().getY() - translateY); graphics.draw(new Line2D.Double(point1, point2)); } private static Point[] getRoutePoints(RouteDescription routeDescription, MapData mapData){ final List<Territory> territories = routeDescription.getRoute().getAllTerritories(); final int numTerritories = territories.size(); final Point[] points = new Point[numTerritories]; for (int i = 0; i < numTerritories; i++) { points[i] = mapData.getCenter(territories.get(i)); } if (routeDescription.getStart() != null) { points[0] = routeDescription.getStart(); } if (routeDescription.getEnd() != null && numTerritories > 1) { points[numTerritories - 1] = new Point(routeDescription.getEnd()); } return points; } private static double[] pointsXToDoubleArray(Point[] points){ double[] result = new double[points.length]; for(int i = 0; i < points.length; i++){ result[i] = points[i].getX(); } return result; } private static double[] pointsYToDoubleArray(Point[] points){ double[] result = new double[points.length]; for(int i = 0; i < points.length; i++){ result[i] = points[i].getY(); } return result; } private static double[] getCoords(PolynomialSplineFunction curve, float stepSize){ final double[] coords = new double[(int) (curve.getN() / stepSize)]; for(int i = 0; i < curve.getN() / stepSize; i++){ coords[i] = curve.value(i * stepSize); } return coords; } private static void drawCurvedPath(Graphics2D graphics, Point[] points, MapPanel view){ final double[] index = getIndex(points); final float stepSize = 0.01f;//TODO calculating a step size that makes sense final PolynomialSplineFunction xcurve = splineInterpolator.interpolate(index, pointsXToDoubleArray(points)); final PolynomialSplineFunction ycurve = splineInterpolator.interpolate(index, pointsYToDoubleArray(points)); final double[] xcoords = getCoords(xcurve, stepSize); final double[] ycoords = getCoords(ycurve, stepSize); for(int i = 1; i < xcoords.length; i++){ //TODO maybe a line is not the best way to draw this... drawLineWithTranslate(graphics, new Line2D.Double(xcoords[i-1], ycoords[i-1], xcoords[i], ycoords[i]), view.getXOffset(), view.getYOffset()); } } } 元素导致递归深度NN一样

时,递归会出错

为避免这种情况,请确保每次递归的长度减半 最大递归深度为return largest(arr,length -1);

O(log2(N))

一个偷偷摸摸的快速方法几乎不使用递归,因为找到最大值是一个微不足道的循环。

int largest(int arr[], int length) {
  if (length <= 0) return INT_MIN;
  int big = arr[0];
  while (length > 1) {
    int length_r = length / 2;
    int length_l = length - length_r;
    int big_r = largest(&arr[length_l], length_r);
    if (big_r > big) big = big_r;
    length = length_l; 
  }
  return big;
}