递归是一种比较好的解决方法,因为可以这样想,求n个元素的循环右移k位,可以分解为求一个元素的循环右移k位以及剩下的
n-1个元素的循环右移k位。所以我们可以这样想,先将元素A1移动k位到相应的位置,但是需要用一个存储空间来存储那个位置的元素的值,紧接着将这个存储起来的元素移动到相应的位置,然后又存储那个位置的值,以此类推,用图说明如下:
数字的顺序说明了移动的顺序,就是先把1移动到相应的位置,然后把3移动到相应的位置,然后是5移动到相应的位置,最后的话就是是4移动到相应的位置。
但是有一种特殊情况要考虑,就是如果An移动到Am的位置,Am恰好移动到An的位置,那么算法就会陷入到这两个元素移动的死循环,应当避免这种情况,假设上述情况出现,则有m-n也就是k(m-n是右移的距离,也就是k),恰好等于(arrsize-1)-m+n+1(为Am要移动到An的距离),也就是如下:
k = m - n
m-n = arrsize-1-m+n+1
得出k=arrsize/2,(步骤如下: m - n = arrsize - m + n ----> 2m - 2n = arrsize ---> 2k = arrsize ---> k = arrsize/2)
也就是当右移的位置恰好是数组长度的一半的话,那么就会出现上述情况,比如:
1 2 3 4
要右移2位就是如上所述情况,这时只要从a[0]开始,一直到a[size/2-1](也就是2的位置)依次与右移的位置的元素交换即可,即:
1和3交换,2和4交换,得:
3 4 1 2
就完成移动了,另外还要提醒的是,如果arrsize不是偶数,则k就带有小数部分,而我们移动的步数都是整数的,所以说如果数组的长度并不是基础,那么上述情况(指的是Am移动到An,An移动Am,下同)就不会发生,换句话说,只有在arrsize为偶数并且它的一半恰好是要移动的步数(k)的时候,才会出现上述情况。
并且可以证明,如果k!=arrsize/2,就不会出现上述情况,并且任何一个元素都不会出现,这时我们就可以递归解决,算法如下:
void MoveKsteps(int arr[], const int size, int curelem, int curloc, int n, int k) {//arr是要移动的数组,size是数组的大小,curloc是数组中当前待移动的元素 //curloc是当前待移动元素的位置 //n记录移动的元素个数,k是要右移的个数 using namespace stdioes; if (n == size)//元素已经交换完 { return; } if (!(size % 2) && k == size / 2) { for (int i = 0; i <= size / 2 - 1; ++i) { Swap(arr[i], arr[(i + k) % size]);//交换元素 } return;//省略也无妨 } else { Swap(curelem, arr[(curloc = (curloc + k) % size)]); return MoveKsteps(arr, size, curelem, curloc, ++n, k);//该处的return也是 } } void Swap(int &a, int &b) {//交换元素a和b的值 a = a + b; b = a - b; a = a - b; }
算法复杂度分析:分析算法,可知算法主要有两个循环,一个是边界条件处理的循环,涉及到元素的交换,循环的次数为n/2,第二个循环是递归的循环,由于这是一个单向递归(即递归没有分支,也没有回溯),递归的出口为n=size,递归的主要任务是交换元素的值,算法的复杂度为O(1),所以整个递归算法的时间复杂度为O(n)。
下面是整个程序,包含测试的main函数。
#include<iomanip>//控制输出格式
#include<iostream> #define OVERFLOW -1//动态分配内存失败 namespace stdioes //常用的标识符,其中ioes是几个标识符取第一个字母 { using std::cout; using std::endl; using std::cin; using std::setw; } void MoveKsteps(int arr[], const int size, int curelem, int curloc, int n, int k); void Swap(int &a, int &b); int main() { //数组的大小,数组的元素以及右移的大小。 using namespace stdioes; int *arr, size, k; cout << "input the size of array:\n"; cin >> size; cout << "input the k:\n"; cin >> k; cout << "input the elemments of array:\n"; arr = new int[size]; if (!arr) { exit(OVERFLOW); } for (int i = 0; i < size; ++i) { cin >> arr[i]; } cout << "Before moved:\n"; for (int i = 0; i < size; ++i) { cout.setf(std::ios_base::left); cout << setw(3) << arr[i]; } cout << endl; MoveKsteps(arr, size, arr[0], 0, 0, k);
cout << "After moved:\n"; for (int i = 0; i < size; ++i) { cout.setf(std::ios_base::left); cout << setw(3) << arr[i]; } cout << endl; return 0; } void MoveKsteps(int arr[], const int size, int curelem, int curloc, int n, int k) {//arr是要移动的数组,size是数组的大小,curloc是数组中当前待移动的元素 //curloc是当前待移动元素的位置 //n记录移动的元素个数,k是要右移的个数 using namespace stdioes; if (n == size)//元素已经交换完 { return; } if (!(size % 2) && k == size / 2) { for (int i = 0; i <= size / 2 - 1; ++i) { Swap(arr[i], arr[(i + k) % size]);//交换元素 } return; } else { Swap(curelem, arr[(curloc = (curloc + k) % size)]); return MoveKsteps(arr, size, curelem, curloc, ++n, k); } } void Swap(int &a, int &b) {//交换元素a和b的值 a = a + b; b = a - b; a = a - b; }