数据结构 --- 将数组的元素循环右移k位

递归是一种比较好的解决方法,因为可以这样想,求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;
}

猜你喜欢

转载自blog.csdn.net/weixin_41133154/article/details/79940682
今日推荐