【问题标题】:C/C++ - efficient method of rotating an array without using build-in functions (homework)C/C++ - 在不使用内置函数的情况下旋转数组的有效方法(作业)
【发布时间】:2016-02-24 23:02:52
【问题描述】:

任务是将数组的子数组向左或向右旋转给定次数。

让我用一个例子来解释一下:

  • 让数据成为一个数组。

数据 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  • 子数组由参数 begin 和 end 确定。

如果 begin = 3 且 end = 7,则子数组为 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

如果 begin = 7 且 end = 3,则子数组为 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;

  • 让我们向右旋转两次

如果 begin = 3 且 end = 7,则结果为 {0, 1, 2, 6, 7, 3, 4, 5, 8, 9};

如果 begin = 7 和 end = 3,那么结果是 {8, 9, 0, 1,, 4, 5, 6, 2, 3, 7};

我编写了执行此任务的代码,但速度很慢。 有人可以给我一个提示如何让它更快吗? 重要提示:我不允许使用除数据、子程序和内置函数之外的其他数组。

#include <iostream>
using namespace std;

int main(){

    int dataLength;

    cin >> dataLength;

    int data [ dataLength ];

    for (int i = 0; i < dataLength; i++){
        cin >> data [ i ];
    }


    int begin;
    int end;
    int rotation;
    int forLoopLength;
    int tempBefore;
    int tempAfter;

    cin >> begin;
    cin >> end;
    cin >> rotation;

    if (end > begin)
       forLoopLength = (end - begin) + 1;
    else
       forLoopLength = (end - begin) + 1 + dataLength;

    if (rotation < 0) 
       rotation = forLoopLength + (rotation % forLoopLength);
    else
        rotation = rotation % forLoopLength;

    for (int i = 0; i < rotation; i++) {

        tempBefore = data [ end ];

        for (int i = 0; i < forLoopLength; i++) {
            tempAfter = data [ (begin + i) % dataLength ];
            data [ (begin + i) % dataLength ] = tempBefore;
            tempBefore = tempAfter;
        }
    }

    for (int i = 0; i < dataLength; i ++ ) {
        cout << data [ i ] << " ";
    }

    return 0;
}

【问题讨论】:

  • using namespace std; + 名为beginend 的变量注定会发生灾难。
  • 如果您的代码已经正常工作,但您正在寻求改进它的提示,您也可以在Code Review 上提问。如果您决定这样做,请在此处删除问题,因为不赞成重复发布。
  • 请选择一种语言。
  • @ltw 你有没有想过看他的链接?

标签: c++ arrays algorithm performance


【解决方案1】:

这有一个技巧。如果在课堂上没有提到这个技巧,你会得到这个作为家庭作业是很奇怪的。总之……

旋转由 M 离开的 N 个元素的序列:

  • 反转整个序列
  • 反转最后的 M 个元素
  • 反转前 N-M 个元素

完成

例如2: 1234567 -> 7654321 -> 7654312 -> 3456712

【讨论】:

  • 这太棒了,但作为家庭作业的答案,他可能会遇到麻烦,因为它太高级了。
  • 对列表来说会很快,而不是对数组。这里你对整个数组进行两次遍历,而对旋转部分进行一次遍历就足够了。
  • @Alexey,您可能忘记了反转数组中的 N 个元素只需要 N/2 交换,所以这需要 N 交换。如果您有更快的数组方法,我很乐意看到它!
【解决方案2】:

这是我的代码,它恰好进行 n 次读取和 n 次写入,其中 n 是子数组大小。

#include<iostream>

int arr[]= { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// replacing 'addr( pos, from, size )' with just 'pos' mean rotation the whole array
int addr( int ptr, int from, int size)
{
  return (ptr + from ) % size;
}

void rotate( int* arr, int shift, int from, int count, int size)
{
  int i;
  int pos= 0;
  int cycle= 0;
  int c= 0;
  int c_old= 0;
  // exactly count steps
  for ( i=0; i< count; i++ ){
    // rotation of arrays part is essentially a permutation.
    // every permutation can be decomposed on cycles
    // here cycle processing begins
    c= arr[ addr( pos, from, size )  ];
    while (1){
      // one step inside the cycle
      pos= (pos + shift) % count;
      if ( pos == cycle )
        break;
      c_old= c;
      c= arr[ addr( pos, from, size )  ];
      arr[ addr( pos, from, size ) ]= c_old;
      i++;
    }
    // here cycle processing ends
    arr[ addr( pos, from, size ) ]= c;
    pos=   (pos   + 1) % count;
    cycle= (cycle + 1) % count;
  }
}

int main()
{
  rotate( arr, 4, 6, 6, 11 );
  int i;
  for ( i=0; i<11; i++){
    std::cout << arr[i] << " ";
  }
  std::cout << std::endl;
  return 0;
}

【讨论】:

  • 同意。 N 次移动比 N 次交换更快,即使是通过临时的,这比我想的要干净得多。我现在投票,稍后再测试。
  • N 移动不一定比N 交换快。数据访问的局部性很重要,Matt 回答中的 swaps 方法显着减少了通过数组的次数(M 的因子)——即使每次传递只重写 1/M 的元素,它仍然通过缓存控制器。
猜你喜欢
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
  • 2014-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多