题目:有n个整数,使前面各数顺序向后移m个位置,最后m个数变成最前面m个数,写一函数实现以上功能,在主函数中输入n个整数和输出调整后的n个数。
要求:最多只让使用一个临时空间。
函数接口定义如下:
Int moveRight_n(int* p,int n,int m);
这道题最容易想到的方法就是循环移位,实现如下:
int moveRight_n(int* p, int n, int m)
{
int nRet = 0;
int* pStart = NULL;
int* pEnd = NULL;
int nTmp;
if (p == NULL || n <= 0 || m < 0)
{
nRet = -1;
printf("param input error!\n");
return nRet;
}
if (m > n)
m %= n;
int i, j;
for (i = 0; i<m; ++i)
{
nTmp = p[0];
for (j = n - 1; j>0; --j)
{
if (j + 1 == n)
p[(j + 1) % n] = p[j];
p[j + 1] = p[j];
}
p[j + 1] = nTmp;
}
return nRet;
}PS:使前面各数顺序向后移m个位置,注意题意了,这里不是旋转数组,前面各数顺序向后移m个位置是不能够使用旋转数组的高效算法的!
所以下面的内容不适合本题意思。还是太粗心了!
这种方法效率低,但是是最基本的解法。我们还可以观察数组移动的规律,里面的每一个数字无论向左还是向右移动n位,都会回到原来的位置,而我们传入的m值可能大于n值,所以这段代码也就是处理这种情况
if(m > n)
m %= n;
我们右移m位相当于左移n-m位,利用这个我们还可以近一半提高上面基本算法的效率。我们先求出数组长度的一半mid,如果m>mid。题目要求左移m位,我们可以右移n-m位,这样可以减少循环的次数,从而提高了一些效率。
最高效的时间复杂度当然是O(n),类似于july博客《程序员编程艺术》的旋转字符串一节。把数组分为【0...m】和【m+1,n】2部分,分别对这2段数组反转,然后对整个数组反转,最后就是所要求得的移位后的数组。
详细算法讲解参见july的博客《程序员编程艺术》第一章、左旋转字符串,里面有详细的讲解,在这里也向july大神致敬!
下面是实间复杂度为O(n)的算法实现:
void Swap(int* p1, int *p2)
{
int nTmp;
nTmp = *p1;
*p1 = *p2;
*p2 = nTmp;
}
int moveRight_n(int* p, int n, int m)
{
int nRet = 0;
int* pStart = NULL;
int* pEnd = NULL;
if (p == NULL || n <= 0 || m < 0)
{
nRet = -1;
printf("param input error!\n");
return nRet;
}
if (m > n)
m %= n;
pStart = p;
pEnd = p + n - m - 1;
while (pStart < pEnd)
{
Swap(pStart, pEnd);
++pStart;
--pEnd;
}
pStart = p + n - m;
pEnd = p + n - 1;
while (pStart < pEnd)
{
Swap(pStart, pEnd);
++pStart;
--pEnd;
}
pStart = p;
pEnd = p + n - 1;
while (pStart < pEnd)
{
Swap(pStart, pEnd);
++pStart;
--pEnd;
}
return nRet;
}
原文:http://blog.csdn.net/bcypxl/article/details/34161841