使字符数组向左/向右旋转其单元格 n 次

问题描述

我是新来的,但我听说了很多关于这个网站的信息,现在我已经被接受了 7 个月的软件开发“训练营”,我正在为即将到来的测试磨练我的 C 知识。

我已经通过了一个测试题,但我没有完成这个问题,这让我很困扰。

问题是用 C 编写一个程序,该程序将字符(char)数组的单元向左移动 1(对我来说朝哪个方向并不重要,但问题指定为左)。而且我还自己决定在执行期间不要使用临时数组/堆栈或任何其他结构来保存整个数组数据。

所以包含 '0' '1' '2' 'A' 'B' 'C' 的 'string' 或字符数组将变成 '1' '2' 'A' 'B' 'C' '0' 函数使用一次后。

写这个没问题,我相信我最终得到了类似的东西:

void ArrayCharMoveLeft(char arr[],int arrsize,int times) {
    int i;
    for (i = 0; i <= arrsize ; i++) {
        ArraySwap2CellsChar(arr,i,i+1);
    }
}

正如您所见,该函数有点模块化,因为它允许输入单元格需要向左移动或移动的次数。我没有实施它,但这就是想法。

据我所知,有 3 种方法可以做到这一点:

  1. 循环 ArrayCharMoveLeft 次。这本能地感觉效率低下。
  2. 在 ArrayCharMoveLeft 中使用递归。这应该类似于第一个解决方案,但我不是 100% 确定如何实现它。
  3. 这就是我想弄清楚的方法:循环中没有循环、没有递归、没有临时数组,程序将知道如何将单元格向左/向右移动 x 次而不会出现任何问题。

问题是,在交换数组中的 N 次单元格后,剩余的数组大小 - 有时没有组织。例如:

使用 ArrayCharMoveLeft 和 3 作为上述给定数组的次数将产生 ABC021 而不是 ABC012 的期望值。

我为此运行了以下函数

int i;
char* lastcell;
if (!(times % arrsize))
{
    printf("nothing to move!\n");
    return;
}
times = times % arrsize;
// Input checking. in case user inputs multiples of the array size,auto reduce to array size reminder
for (i = 0; i < arrsize-times; i++) {
    printf("I = %d ",i);
    PrintArray(arr,arrsize);
    ArraySwap2CellsChar(arr,i+times);
}

如您所见,for 运行从 0 到数组大小 - 次。如果使用此函数,请使用包含 14 个字符的数组。然后使用 times = 5 将使 for 从 0 运行到 9,因此单元格 10 - 14 不按顺序排列(但其余的都是)。

最糟糕的是,剩余的单元格始终保持序列,但位置不同。意思是不是 0123,它们可以是 3012 或 2301 ......等等。

我在不同的时间值上运行了不同的数组,但没有找到特定的模式,例如“如果剩余单元格 = 3,则对剩余单元格使用 ArrayCharMoveLeft,时间 = 1)。

它似乎总是 2 个选项中的 1 个:剩余的单元格按顺序排列,或者用不同的值移动。它似乎与此类似: 时移+方向对齐 1 0 2 0 3 0 4 1R 5 3R 6 5R 7 3R 8 1R 数字随着不同的时间和数组而变化。有人对此有什么想法吗? 即使您在循环中使用递归或循环,我也想听听可能的解决方案。唯一确定的规则是不要使用临时数组。

提前致谢!

解决方法

  1. 使用标准库函数 memcpymemmove 等,因为它们针对您的平台进行了非常优化。
  2. 使用正确的尺寸类型 - size_t 而非 int
char *ArrayCharMoveLeft(char *arr,const size_t arrsize,size_t ntimes) 
{
    ntimes %= arrsize;
    if(ntimes)
    {
        char temp[ntimes];
        memcpy(temp,arr,ntimes);
        memmove(arr,arr + ntimes,arrsize - ntimes);
        memcpy(arr + arrsize - ntimes,temp,ntimes);
    }
    return arr;
}

但是您希望它没有临时数组(内存效率更高,性能非常差):

char *ArrayCharMoveLeft(char *arr,size_t arrsize,size_t ntimes) 
{
    ntimes %= arrsize;
    while(ntimes--)
    {
        char temp = arr[0];
        memmove(arr,arr + 1,arrsize - 1);
        arr[arrsize -1] = temp;
    }
    return arr;
}

https://godbolt.org/z/od68dKTWq https://godbolt.org/z/noah9zdYY

,

如果出于学习的目的而不管效率或简单性,您只想使用 ArraySwap2CellsChar 交换两个数组元素,您可以通过一些调整来保持循环。正如您所指出的,给定的 for (i = 0; i < arrsize-times; i++) 循环使最后一个 times 元素不合适。为了正确放置所有元素,循环条件必须是 i < arrsize-1(少一个就足够了,因为如果除了最后一个元素之外的每个元素都是正确的,那么最后一个也必须是正确的)。当然,当 i 几乎运行到 arrsize 时,i+times 不能作为另一个交换索引;相反,必须计算要放置在索引 j 处的元素的正确索引 i。这个计算结果有点棘手,因为元素已经从它的原始位置交换了。这是循环的修改变体:

for (i = 0; i < arrsize-1; i++)
{
    printf("i = %d ",i);
    int j = i+times;
    while (arrsize <= j) j %= arrsize,j += (i-j+times-1)/times*times;
    printf("j = %d ",j);
    PrintArray(arr,arrsize);
    ArraySwap2CellsChar(arr,i,j);
}
,

免责声明:我不确定在这里共享完整的工作代码是否很常见,因为这实际上是我在这里提出的第一个问题,因此假设该想法是回答特定问题,我将避免这样做,并且不提供抓取的示例解决方案(这可能会破坏学习和探索 C 的目的)。这一论点得到以下事实的支持,即此特定任务源自编程课程使用的编程测试,其目的是过滤掉不适合进行为期 7 个月的软件开发密集培训的申请人。如果你还想看我的代码,私信我。

因此,在@Armali 的大力帮助下,我很高兴地宣布问题已得到解答!我们一起想出了一个函数,它接受 C 中的字符数组(字符串),并且不使用任何以前编写的库(例如 strings.h),甚至不使用临时数组,它将数组中的所有单元旋转 N 次向左。

示例:在 N = 5 的以下数组上使用 ArrayCharMoveLeft(): 原始数组:0123456789ABCDEF 更新数组:56789ABCDEF01234 如您所见,第一个单元格 (0) 现在是第六个单元格 (5),第二个单元格是第 7 个单元格,依此类推。所以每个单元格向左移动了 5 次。前 5 个单元格“溢出”到数组的末尾,现在显示为最后 5 个单元格,同时保持它们的顺序。 该函数适用于各种数组长度和 N 值。

这不是什么成就,而是尝试用尽可能少的变量来执行任务(只有 4 个整数,除了 char 数组,还计算用于交换单元格的子函数)。

它是使用嵌套循环实现的,因此它在运行时方面并不高效,只是在内存方面,同时仍然是自编码函数,没有使用外部库(stdio.h 除外)。

参考 Armali 发布的解决方案,它应该可以为您提供这个问题的答案。