C# 如果我们需要使用索引作为条件,如何将循环修改为递归?

C# 如果我们需要使用索引作为条件,如何将循环修改为递归?,c#,recursion,C#,Recursion,假设我们想要实现一个sum算法,我在这里用C#作为例子: // Iterative int sum(int[] array) { int result = 0; foreach(int item in array) { result += item; } return item; } 这相当于 // Recursive int sum(int[] array) { if(array.Length == 0) { retur

假设我们想要实现一个
sum
算法,我在这里用C#作为例子:

// Iterative
int sum(int[] array) {
    int result = 0;
    foreach(int item in array) {
        result += item;
    }
    return item;
}
这相当于

// Recursive
int sum(int[] array) {
    if(array.Length == 0) {
        return 0;
    }
    // suppose there is a SubArray function here
    return array[0] + sum(array.SubArray(1));
}
这很容易

但是,如果我们想在算法中添加一个条件,而不想在结果中添加索引2处的整数,那么我们只需要在第一个(迭代)实现中添加一个条件语句


Q:我们的递归版本是否有任何调整以使其工作?

递归版本由于重复的
子数组
调用而效率低下,使得时间复杂度为O(n2)。您可以重新编写此函数以接受附加的索引参数,这也恰好是您可以实现跳过特定索引(或一组索引,如果您选择)的方式

在C#中:

如果您不喜欢添加的改变函数头的
i
参数,只需编写一个单独的私有递归“helper”函数,可以使用首选头从包装器调用该函数

我还假设您不希望将2索引到算法中(如果希望,请删除
skip
参数,并将
I==skip
替换为
I==2


最后,请记住,对于这种算法(对数组求和),递归是一个糟糕的选择,即使是索引版本也是如此。我们必须调用一个新函数来处理一个数字,这意味着我们有很多调用开销(分配堆栈帧),如果列表太长,很容易破坏堆栈。但我假设这只是一个学习练习。

可以使用数组切片在C#8中完成一个一致的解决方案

public static int SumArray(int[] arr, int exclude){
  if(arr.Length == 0){
    return 0;
  }
  return (exclude==0?0:arr[0]) + SumArray(arr[1..], exclude-1);
}
三值运算符检查跳过索引是否为0,如果不是,它将为下一个递归调用递减跳过索引。使用切片减少阵列,切片应比子阵列性能更好。(有人问我后者的情况)

编辑:正如另一个答案所建议的,由于缺少尾部调用递归,这会导致堆栈帧膨胀。下面的解决方案将通过使用尾部调用优化来缓解此问题,将sum变量添加到函数中。这意味着递归调用可以使用相同的堆栈帧,而不是创建一个新的堆栈帧,以在完成求和之前等待返回值

public static int SumArray(int[] arr, int exclude, int sum=0){
  if(arr.Length == 0){
    return sum;
  }
  return SumArray(arr[1..], exclude-1, sum + (exclude==0?0: arr[0]));
}
public static int SumArray(int[] arr, int exclude){
  if(arr.Length == 0){
    return 0;
  }
  return (exclude==0?0:arr[0]) + SumArray(arr[1..], exclude-1);
}
public static int SumArray(int[] arr, int exclude, int sum=0){
  if(arr.Length == 0){
    return sum;
  }
  return SumArray(arr[1..], exclude-1, sum + (exclude==0?0: arr[0]));
}