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.Copyindices 代替我的标准方法。

这是我要翻译的代码:

       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#旋转数组递归(左,右)多次的主要内容,如果未能解决你的问题,请参考以下文章

33.Search in Rotated Sorted Array---二分变形

leetcode中等33搜索旋转排序数组

LeetCode 81——搜索旋转排序数组 II

LeetCode-数组旋转数组

189. 旋转数组

189. 旋转数组