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]));
}