题目:对于有n个元素的数组 int a[n]={....};写一个高效算法将数组内容循环左移m位。比如: int a[6] ={1,2,3,4,5,6} ,循环左移3位得到结果{456123}
基本思想:
初始序列 1234567,左移3,结果为4567123
对比两个序列,可由以下三步得到结果。
a. 123 和 456 交换, 得到 456 123 7
b. 此时456已在其最终位置。对序列1237,1和7交换,得到 7231。
因为如果此时还是用123和后面部分交换,len(123)=3 > len(7)=1,无法继续。
这里交换的长度应该是len(123)和len(7)中较小的一个。交换后较短序列移到其最终位置。与最终位置不符的序列的长度为 max(len(123), len(7))。
c. 最后对序列231,循环左移2.
最后序列的长度为 3,在步骤b中已分析过。
那左移的2怎么来的呢? len(123) - len(7)。
因为本来123三个数字的相对位置是不会变的(1,2,3 三个数字之间有其他数字不在此讨论范围,因为最后剩下的len(123)个数字肯定是连续的,循环左移len(123)位)
交换1和7后,序列变成231,破坏了这种相对位置关系,需要还原。
以上分析写的自己都有点晕。后续再想想,怎么能表达清楚。
任何一个序列都可以通过上述3步的得到结果(只需要步骤a,只有b和c,abc都有)
程序中即对上述三种情况进行处理。
/*a 为数组其实地址; n 为数组长度, left 为循环左移个数*/
void rotateleft(int *a, int n, int left) {
int i, j, k, change;
if(left >= n)
left = left % n;
if(left == 0)
return ;
for(i = 0; i + left < n; i += change) {
if(i + 2 * left < n) //step a
change = left;
else //step b
change = n -i - left;
for(j = i; j < i + change; ++j) {
a[j] = a[j] ^ a[j + left];
a[j + left] = a[j] ^ a[j + left];
a[j] = a[j] ^ a[j + left];
}
}
k = left - change;
rotateleft(&a[i], left, k);
}
非递归
void rotateleft(int *a, int n, int left) {
int i, j, k, change;
if(left >= n)
left = left % n;
if(left == 0)
return ;
for(i = 0; i + left < n; i += change) {
if(i + 2 * left < n)
change = left;
else
change = n -i - left;
for(j = i; j < i + change; ++j) {
a[j] = a[j] ^ a[j + left];
a[j + left] = a[j] ^ a[j + left];
a[j] = a[j] ^ a[j + left];
}
if(n - i == left + change) {
k = (left - change) % left;
if(k == 0)
return ;
left = k;
}
}
}
后续给出链表的循环左移代码。