您的位置:首页 > 其它

向量旋转算法

2015-10-15 20:54 483 查看
最近看《编程珠玑》,里面提到了一个常见的向量旋转问题,是指将一个数按照某点前后置换,比如【1,2,3,4,5,6,7,8,9,10】按照4旋转后,就变成了【5,6,7,8,9,10,1,2,3,4】。

之前在leetcode上也看到了这个问题,当时觉得很简单,就是把前i个数存起来,然后将后面的数据向前移动,然后在把存起来的前i个数加在后面。

《编程珠玑》上提到了两种算法,非常高效,时间复杂度为O(n),空间复杂度为O(1)。

代码1:

int gcd(int a, int b) {
int c;
while ((c = a%b) != 0) {
a = b;
b = c;
}
return b;
}
void rotate1(int* nums, int size,int rotateLen) {
int times = gcd(rotateLen, size);
for (int i = 0; i < times; i++) {
int val = nums[i];
int cur = i;
int next = i + rotateLen;
while (next % size != i) {
nums[cur] = nums[next % size];
cur = next % size;
next += rotateLen;
}
nums[cur] = val;
}
}


描述的算法的思路是:将num[0]保存起来,然后num[0] = num[i % size],num[i % size] = num[2*i % size],,,直到进行到这样一点待保存的数正好为num[0],然后将之前保存的临时值赋给它。如果此时,数组中所有的值都已经被遍历了一遍,那么退出循环,否则从num[1]重新开始循环。

这里有三个问题:

1、为什么这个算法是有效的?我不知道;

2、为什么一定会出现num[k * i % size] = 0的情况。因为当k足够大,比如k == size时,一定会等于0。实际上k的最大值是有讲究的。当满足条件时,一定是k*i = i和size的最小公倍数。因为最小公倍数 乘 最大公约数为 = i*size;所以等式两边变成了 k*i*gcd(i,size) = i*size;所以k*gcd = size;

这也就回答了下面问题:

3、要循环几次才会退出循环过程?答案是gcd次。

算法2:

void reverse(int *nums,int start, int end) {
while (start < end) {
int val = nums[start];
nums[start] = nums[end];
nums[end] = val;
start++; end--;
}
}
void rotate2(int* nums, int size,int rotateLen) {
if (rotateLen >= size || rotateLen < 0)
return;
reverse(nums,0, rotateLen - 1);
reverse(nums,rotateLen, size - 1);
reverse(nums,0, size - 1);
}


这个算法非常美妙,它将前i个值进行翻转,将后面的n-i个值翻转,然后总体再翻转一次,完成向量旋转了。

该算法时间复杂度为O(2*n),不占用额外空间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: