## 【我解C语言面试题系列】004 数组的循环右移问题
**数组的循环右移**
【题目】有一个整数数组,现要求实现这个整数数组的循环右移。如:1,2,3,4,5 则循环右移两位后结果是:4,5,1,2,3。
**方法一:**(最最容易想到的办法)
~~~
void RightCircleShift_00(int buffer[],int shift)
{
int i,j,tt;
for(i=0;i<shift;i++)
{
tt = buffer[ARRSIZE-1];
for(j=ARRSIZE-1;j>0;j--)
buffer[j] = buffer[j-1];
buffer[0] = tt;
}
}
~~~
这个办法是用两个循环来控制,内循环每次向右移动一位,外循环则用来限制移动的位数。算法需要执行 ARRSIZE * ShiftValue次,时间复杂度是O( N2 )。
**方法二:**(由方法一得到的递归程序)
~~~
void RightCircleShift_01(int buffer[],int shift)
{
int *p,tt;
tt = *(buffer+ARRSIZE-1);
for(p = buffer+ARRSIZE-1;p > buffer;p--)
*p = *(p-1);
*buffer = tt;
shift--;
if(shift > 0)
RightCircleShift_00(buffer,shift);
}
~~~
这个程序跟方法一类似,区别就是它是用递归来实现的。同样需要执行ARRSIZE * ShiftValue次,时间复杂度也是O( N2 )。
**方法三**:
~~~
void RightCircleShift_02(int buffer[],int shift)
{
int *title,*r,*p;
if(shift == 0)
return;
shift = shift % ARRSIZE;
title = (int *)malloc(sizeof(int)*shift);
if( title == NULL )
return;
r = buffer + (ARRSIZE - shift);
memcpy(title, r, shift * sizeof(int));
p = buffer + shift;
memmove(p, buffer, (ARRSIZE - shift) * sizeof(int));
memcpy(buffer, title, shift * sizeof(int));
free(title);
}
~~~
这个算法需要移动位数大小的临时辅助空间。如需移动两位,则申请两个的空间,然后把从右边起的两个元素拷贝的临时辅助空间,然后前面的元素向后移动两位,最后再把临时空间里面的两个元素拷贝到前面的两位即可完成循环右移。需要执行 ARRSIZE次,时间复杂度是O( N )。
**方法四:
~~~
void RightCircleShift_03(int buffer[],int shift)
{
if(shift <= 0)
return;
if( (shift & 1) == 0)
{
BufferShiftEvenNumber(buffer,shift-1);
BufferShiftEvenNumber(buffer,1);
}
else
BufferShiftEvenNumber(buffer,shift);
}
void BufferRightShiftEvenNumber(int buffer[],int shift)
{
int i,j,tt,res;
res = buffer[0];
for (i=0,j=0; i<ARRSIZE; i++)
{
tt = res;
res = buffer[(j+shift)%ARRSIZE];
buffer[(j+shift)%ARRSIZE] = tt;
j = (j+shift) % ARRSIZE;
}
}
~~~
这个算法并不需要开额外的辅助空间,而且时间复杂度同样也是O( N )。BufferRightShiftEvenNumber函数是这个算法的核心,该函数只能实现每次向右移动奇数位元素。如果要移动的位数是偶数的时候就需要把该数分解为两个奇数,n = (n – 1) + 1 。
**方法五:**
~~~
void RightCircleShift_04(int buffer[],int shift)
{
shift %= ARRSIZE;
ReverseArray(buffer,1,ARRSIZE);
ReverseArray(buffer,1,shift);
ReverseArray(buffer,shift+1,ARRSIZE);
}
void ReverseArray(int buffer[],int start,int end)
{
int i,tt;
if(end > ARRSIZE)
return;
start -= 1;
end -= 1;
while(start < end)
{
tt = buffer[start];
buffer[start++] = buffer[end];
buffer[end--] = tt;
}
}
~~~
这个办法也是很不错,需要两次扫描数组即可,时间复杂度O(N)。这个办法跟方法四移动偶数个元素的情况一样,都是需要两次扫描数组。当然如果是移动奇数个元素的话,则不如方法四有效,方法四只需要扫描一次数组就可以了。
算法是网友[luodongshui](http://blog.csdn.net/luodongshui/)提出的:
1、先将整个数组反转。
2、然后再反转前shift个元素。
3、接着再反转后N-shift个元素。