如何通过从头到尾移动 N 个元素来旋转数组?

时间:2021-01-24 09:11:33

标签: c# arrays .net char rotation

我正在尝试使用 C# 将数组中的多个元素移动到末尾。
我有一个数组(在我的例子中是一个字符数组)和一个整数 z。现在我想将 z 个字符移到另一个数组的末尾,其他字符应该移到数组的开头。
因此,如果第一个数组是 {'H','E','L','L','O'}z = 3,则新数组应该是 {'L','O','H','E','L'}

我希望有人能帮助我。

最佳尝试:

static char[] rotate(char[] c, int z)
{
    char[] nc = new char[c.Length];
    for (int i = z; i < c.Length; i++)
    {
        nc[i - z] = c[i];
    }
    for (int i = 0; i < z; i++)
    {
        nc[i + z] = c[i];
    }
    return nc;
}

3 个答案:

答案 0 :(得分:1)

只需使用 Array.Copy

<块引用>

将一个 Array 中的一系列元素复制到另一个 Array 并执行 根据需要键入转换和装箱。

public static void DoStuff<T>(T[]source, int z)
{
   var start = source[0..z];
   Array.Copy(source, z, source, 0, source.Length - z);
   Array.Copy(start, 0, source, source.Length - z, z);
}

测试

var chars = "asdfghjk".ToCharArray();

DoStuff(chars, 3);

Console.WriteLine(string.Join(", ", chars));

迭代器的另一种方式

public static IEnumerable<T> DoStuff<T>(T[]source, int z) 
   => source.Select((t, i) => source[(i + z) % source.Length]);

或者使用 for 循环

public static T[] DoStuff<T>(T[]source, int z)
{
   var result = new T[source.Length];
   for (int i = 0; i < source.Length; i++)
      result[i] = source[(i + z) % source.Length];
   return result;
}

超级高效的零分配

public static unsafe void DoStuff(char[] source, int z)
{
   var start = stackalloc char[source.Length];
   fixed (char* pArray = source)
   {
      Buffer.MemoryCopy(pArray, start, z*sizeof(char), z * sizeof(char));
      Buffer.MemoryCopy(pArray+z, pArray, (source.Length-z)*sizeof(char) , (source.Length - z) * sizeof(char));
      Buffer.MemoryCopy(start, pArray+ (source.Length - z),  z * sizeof(char), z * sizeof(char));
   }
}

输出

f, g, h, j, k, a, s, d

注意:这缺少合适的输入验证和范围检查,以及一个描述性名称。我会把它留给你

答案 1 :(得分:1)

问题在于您代码中的错误索引。
这是固定版本:

static char[] rotate(char[] c, int z)
{
    char[] nc = new char[c.Length];
    for (int i = z; i < c.Length; i++)
    {
        nc[i - z] = c[i];
    }
    for (int i = 0; i < z; i++)
    {
        nc[i + z - 1] = c[i]; // <-- Change here
    }
    return nc;
}

然而,更好的解决方案可能是使用 doubly-linked lists 来更好地处理数组项的移动以开始/结束。双向链表的 .NET 实现是 LinkedList Classhere 你可以找到一些例子来说明如何做到这一点。

答案 2 :(得分:0)

您可以在一行中使用 LINQ 而无需像这样处理数组:

using System.Linq;

var letters = new[] { 'H', 'E', 'L', 'L', 'O' };

int posToRotate = 3;

var lettersRotated = letters.Skip(posToRotate).Concat(letters.Take(posToRotate)).ToArray();

Console.WriteLine(new string(lettersRotated));

假设 letters 已分配且不为空。

如果位置超过长度,则什么都不做,但可以先检查以避免处理(也可以检查负索引)。

我们skip前N个字符(取其余),然后我们add我们take的前N个字符。

我们调用 ToArray 是因为 LINQ 适用于 IEnumerable<>(和 IQueryable<>),所以执行是 deferred 直到这里。

要替换原来的数组,只需写:

letters = letters.Skip(posToRotate).Concat(letters.Take(posToRotate)).ToArray();

输出

LOHEL
相关问题