C#旋转数组递归(左,右)多次
Posted
技术标签:
【中文标题】C#旋转数组递归(左,右)多次【英文标题】:C# rotate array recursive (left, right) multiple times 【发布时间】:2021-12-03 11:22:48 【问题描述】:在某种程度上,递归实现对我来说是一个令人困惑的主题。我想了解如何将正常方法精确地“翻译”为递归方法,经过一些研究后我仍然有点困惑,尤其是在第一步(递归停止的地方)。
这个任务是左右旋转一个数组:
[TestCase(new[] 1, 2 , new[] Direction.Left, Direction.Left, Direction.Right , ExpectedResult = new[] 2, 1 )]
我不想在此使用linq
,我们可以使用Array.Copy
和indices
代替我的标准方法。
这是我要翻译的代码:
public static int[] Shift(int[] source, Direction[] directions)
if (source is null)
throw new ArgumentNullException(nameof(source));
if (directions is null)
throw new ArgumentNullException(nameof(directions));
if (directions.Length == 0)
return source;
else
for (int i = 0; i < directions.Length; i++)
switch (directions[i])
case Direction.Left:
var temp1 = source[0];
for (var j = 0; j < source.Length - 1; j++)
source[j] = source[j + 1];
source[source.Length - 1] = temp1;
break;
case Direction.Right:
var temp2 = source[source.Length - 1];
for (int j = source.Length - 1; j > 0; j--)
source[j] = source[j - 1];
source[0] = temp2;
break;
default:
throw new InvalidOperationException($"Incorrect directions[i] enum value.");
return source;
这是我关于单独编程递归的小代码:
public static int[] Shift(int[] source, Direction[] directions)
if (source is null || directions is null)
throw new ArgumentNullException(nameof(source));
ShiftRecursively(source, source.Length, directions, directions.Length);
public static int ShiftRecursively(int[] sourceShift, int n, Direction[] currentDirection, int iterations)
if (iterations == 0)
if (currentDirection == Direction.Left)
// Handle "left" shift.
else if (currentDirection == Direction.Right)
// Handle "right" shift.
else
throw new InvalidOperationException($"Incorrect currentDirection enum value.");
【问题讨论】:
“递归实现对我来说是一个令人困惑的主题” - 要了解递归,您需要了解递归...... 这正是我在这里发帖的原因,以了解实践中的递归并有足够好的示例供以后参考。 嗯,...我认为这根本不是一个使用递归的好例子。事实上,10 次中有 9 次,我将递归重构为非递归,而不是反过来。但无论如何......你在这里有一个数组来应用你的班次和要应用的操作列表。现在我要改变的是两件事:它应该返回排列后的数组,iterations
不应该倒数......我会试着写一个答案..
旁注:我可能会通过只取左右移位的网络来优化
@Charlieface 我完全同意实际的例子是有问题的,但 OP 的重点是“如何递归”,......所以我想我们需要通过这个镜头来看待它。
【参考方案1】:
免责声明:我已经在 cmets 中建立,我认为这不是一个“好”的例子,说明使用递归实际上是一件明智的事情。所以,我不会关注那个或其他细节。只是纯粹的“如何进行递归”。
让我们开始你的方法:
public static int ShiftRecursively(int[] sourceShift, int n, Direction[] currentDirection, int iterations)
// "n" is superfluent, here.
// I guess you are trying to model a stop-condition here?
if (iterations == 0)
// this is ok vv
if (currentDirection == Direction.Left)
// Handle "left" shift.
else if (currentDirection == Direction.Right)
// Handle "right" shift.
else
throw new InvalidOperationException($"Incorrect currentDirection enum value.");
// Now what's missing is the next layer of recursion ...
现在,我将其更改为:
public static int[] ShiftRecursively(int[] sourceShift, Direction[] directions, int iteration = 0)
// Stopping condition
if( iteration == directions.Length ) return sourceShift;
// iteration shall be increased in each recursion step,
// so we need to check if there are steps left to take.
// If not: Stop recursion = just return input.
// Data mutation
Direction currentDirection = directions[iteration];
// => factored out the application of 1 shift operation.
// Makes your code "cleaner".
int[] resultShift = Shift(sourceShift, currentDirection);
// Next layer of recursion
return ShiftRecursively(resultShift, directions, iteration + 1);
开始通话:int[] result = ShiftRecursively(source, directions);
注意,这只是一种可能的递归场景。 基本上,我们可以区分何时触发下一步与应用当前突变的时间有关。 这意味着:
您可以应用当前突变并将该结果用作下一个递归步骤的输入。 或者您可以将输入传递到下一个递归步骤并改变结果。 或者你甚至可以两者都做。选择哪一个取决于应用程序。
【讨论】:
【参考方案2】:让我们从一般情况开始:shift
左(正)或右(负):
public static T[] MakeShift<T>(T[] source, int shift)
if (source is null)
throw new ArgumentNullException(nameof(source));
if (source.Length == 0)
return Array.Empty<T>();
// A pinch of modular arithmetics:
// - negative shifts are converted into Length + Shift
// - on large shifts (|Shift| > Length) we take remainder
shift = (shift % source.Length + source.Length) % source.Length;
T[] result = new T[source.Length];
Array.Copy(source, shift, result, 0, source.Length - shift);
Array.Copy(source, 0, result, source.Length - shift, shift);
return result;
然后我们准备实施您的签名:
public static int[] Shift(int[] source, Direction[] directions)
if (source is null)
throw new ArgumentNullException(nameof(source));
if (directions is null)
throw new ArgumentNullException(nameof(directions));
int moves = 0;
// No Linq .Sum(), just a loop as we want
foreach (var dir in directions)
moves += (dir == Direction.Left ? 1 : -1);
return MakeShift(source, moves);
如果你坚持递归解决方案,这对问题没有效率:
private static int[] ShiftRecursively(int[] source, Direction[] directions, int index)
if (index < 0 || index >= directions.Length)
return source;
return ShiftRecursively(MakeShift(
source,
directions[index] == Direction.Left ? 1 : -1,
directions,
index + 1));
public static int[] ShiftRecursively(int[] source, Direction[] directions)
if (source is null)
throw new ArgumentNullException(nameof(source));
if (directions is null)
throw new ArgumentNullException(nameof(directions));
return ShiftRecursively(source, directions, 0);
【讨论】:
以上是关于C#旋转数组递归(左,右)多次的主要内容,如果未能解决你的问题,请参考以下文章