题目链接:Rotate Array
Rotate an array of n elements to the right by k steps.
For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].
Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
Hint:
Could you do it in-place with O(1) extra space?
这道题的要求是将数组向右旋转k步。
可以开一个数组存储要旋转出来的元素,不过这样空间复杂度不是O(1)。
接下来:
-
Reverse
假设旋转数组1、2、3、4、5,k=2,那么旋转后为:4、5、1、2、3。可以将1、2、3看成向量a,4、5看成向量b,那么旋转ab就生成ba。这个过程,相当于现对a求逆,得到aTb,在对b求逆,得到aTbT,接着整体求逆,得到ba。
整理一下,全过程分为3部分:
- 左部分反转
- 右部分反转
- 整体反转
时间复杂度:O(n)
空间复杂度:O(1)
1
2
3
4
5
6
7
8
9
10
11
class Solution
{
public:
void rotate(int nums[], int n, int k)
{
k %= n;
reverse(nums, nums + (n - k));
reverse(nums + (n - k), nums + n);
reverse(nums, nums + n);
}
};
-
逐个旋转处理
这里说一下常规思路,将最后一个元素nums[n-1]移到临时变量temp中,然后移动nums[n-1-k]到nums[n-1],移动nums[n-1-2k]到nums[n-1-k],依此类推,直到返回到元素nums[n-1]结束。假设旋转数组1、2、3、4,k=2,那么:
- temp = nums[3], nums[3] = nums[1], nums[1] = temp
- temp = nums[2], nums[2] = nums[0], nums[0] = temp
这样只需加个计数器cnt用于统计移动了多少个元素,直到cnt等于n结束。
时间复杂度:O(n)
空间复杂度:O(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution
{
public:
void rotate(int nums[], int n, int k)
{
k %= n;
int cnt = 0;
for(int i = n - 1; cnt < n; -- i, ++ cnt)
{
int temp = nums[i], cur = i, front = (cur + n - k) % n;
while(front != i)
{
nums[cur] = nums[front];
cur = front;
front = (cur + n - k) % n;
++ cnt;
}
nums[cur] = temp;
}
}
};
其实,外层循环的次数就是n和k的最大公约数。用近世代数术语来说,就是旋转产生的置换群的陪集个数。————出自《编程珠玑》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution
{
public:
void rotate(int nums[], int n, int k)
{
k %= n;
if(k == 0)
return ;
int gcd_n_k = gcd(n, k);
for(int i = n - 1; i > n - 1 - gcd_n_k; -- i)
{
int temp = nums[i], cur = i, front = (cur + n - k) % n;
while(front != i)
{
nums[cur] = nums[front];
cur = front;
front = (cur + n - k) % n;
}
nums[cur] = temp;
}
}
private:
int gcd(int i, int j)
{
while(i != j)
i > j ? i -= j : j -= i;
return i;
}
};
-
Swap子数组
这部分程序来自Gries的Sciences of Programming一书的18.1节。————出自《编程珠玑》
时间复杂度:O(n)
空间复杂度:O(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution
{
public:
void rotate(int nums[], int n, int k)
{
k %= n;
if(k == 0)
return;
k = n - k;
int i = k, j = n - k;
while(i != j)
{
if(i > j)
{
swaps(nums + (k - i), nums + k, j);
i -= j;
}
else
{
swaps(nums + (k - i), nums + (k + j - i), i);
j -= i;
}
}
swaps(nums + (k - i), nums + k, j);
}
private:
void swaps(int *a, int *b, int m)
{
while(m --)
swap(*a ++, *b ++);
}
};
-
不断Swap最后k个元素
不断将最后k个元素与前面k个元素交换,这样可以保证前面k个元素是旋转后的了。
接下来数组从k位置开始,同时更新n为n-k,k为k%n。
假设旋转数组1、2、3、4、5,k=3,那么:
- 第一次swap后:3、4、5、2、1,前面3个元素以及排好序。接下来数组从元素2开始,n更新为2,k更新为1;
- 第二次swap后:1、2,前面1个元素以及排好序。接下来数组从元素2开始,n更新为1,k更新为0;
由于k得0循环结束。
时间复杂度:O(n)
空间复杂度:O(1)
1
2
3
4
5
6
7
8
9
10
class Solution
{
public:
void rotate(int nums[], int n, int k)
{
for ( ; k %= n; n -= k, nums += k)
for (int i = 0; i < k; ++ i)
swap(nums[i], nums[n - k + i]);
}
};