189. Rotate Array [Leetcode]

来源:互联网 发布:王家卫我爱你知乎 编辑:程序博客网 时间:2024/06/06 12:26

题目:

--------------------------------------------------------------------------------------------------------------------------------------

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.

[show hint]

Hint:
Could you do it in-place with O(1) extra space?

--------------------------------------------------------------------------------------------------------------------------------------

如果空间复杂度没有限制的话,会简单很多,用vector即可完成。

若是空间复杂度限制在O(1)的话,想了很久都没想出一个便捷的方法。


方法一:Reverse法

看讨论区发现一个及其简单的方法:

利用reverse来解决。

如:1234567, k = 2

先将整个数列reverse成 7654321

再reverse前k个及最后n-k个数。变成6712345


哎,一口老血喷出.......


方法二:迭代替换法


用一个变量t来记下被替换掉的数字。一共替换n步。 

如下, 数组 [1,2,3,4,5,6,7] k = 3

1,2,3,1,5,6,7  t = 4

1,2,3,1,5,6,4  t = 7

1,2,7,1,5,6,4  t = 3

1,2,7,1,5,3,4  t = 6

1,6,7,1,5,3,4  t = 2

1,6,7,1,2,3,4  t = 5

5,6,7,1,2,3,4  t = 1

一共7步。

注意:

当size与k互质的时候,迭代只有一个循环:比如对于1,2,3,4,5,6   k = 3,循环为:1,4,7,3,6,2,5。

当size 与 k 非互质时,有可能会发生多个循环:比如对于1,2,3,4,5,6   k = 2, size = 6 时,会在1,3,5,之间循环,以及2,4,6之间循环。可以通过判断数组下表是否跟起始下标相同以跳出循环。

(证明在下一个方法里讲)


代码如下:

class Solution {public:    void rotate(vector<int>& nums, int k) {        if (k == 0 || nums.size() < 2 || k % nums.size() == 0) return;         int n = nums.size(), count = 0;        int pre = nums[0], i = 0, start = 0;        while (count++ < n) {            if (i == start) {                i++;                start = i;                pre = nums[i];                                    }             i = (i + k) % n;            int tmp = nums[i];            nums[i] = pre;            pre = tmp;        }        return;    }};

方法三:(不知如何命名)

 当k与n非互质的时候,存在大于一个的迭代循环。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

证明:

假设经过i次迭代后,回到了原点,则:

i * k  = j * n(i,j为>0的整数)

当k与n互质的时候,i = n,j = k,我们知道迭代步数为n时,我们就完成了迭代过程。因此只有一个循环。

设 g = gcd(k, n); k' = k / g ; n' = n / g

i * k' = j * n'

i = n'

每次循环,迭代的步数为 n / gcd(n, k); 

循环个数为 gcd (n, k);


实例:

 [1,2,3,4,5,6] k = 2 的。循环个数为gcd = 2.,每个循环迭代步数为 i = 6 / 2 = 3 。

[1,2,3,4,5,6,7] k = 3, 循环个数 gcd(3,7) = 1, 每个循环迭代步数: i = 7 / 1 = 7 。

与我们的推理相合。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

我们可以通过计算有几个的循环,以及每个循环需要迭代的步数来解这道题。


代码如下:

这里看了leetcode讨论区的代码,由于知道每次迭代的步数,可以直接用swap来代替迭代的过程。

class Solution {public:    void rotate(vector<int>& nums, int k) {        if (k == 0 || nums.size() < 2 || k % nums.size() == 0) return;         k = k % nums.size();        int g = gcd (nums.size(), k), count = nums.size() / g;        for (int i = 0; i < g; i++) {            for (int j = 1; j < count; j++) {                swap(nums[i], nums[(i + j * k) % nums.size()]);            }        }        return;    }    int gcd(int a, int b) {        return b == 0 ? a : gcd (b, a % b);    }};


参考:

https://discuss.leetcode.com/topic/11349/my-three-way-to-solve-this-problem-the-first-way-is-interesting-java/8


方法四: 交换法

通过不停地将后K个数交换到正确的位置。用start来记录为无序数组(也就是位置还不正确的数)的开始。n代表当前无序数组的个数。k表示当前无需数组需要旋转的步数。

如:

[1,2,3,4,5,6,7]   k = 3

第一次交换: 5,6,7,4,1,2,3  n = 7, k = 3, start = 0

第二次交换: 5,6,7,1,2,3,4  n = 4, k = 3,  start = k

[1,2,3,4,5,6,7]  k = 4

4,5,6,7,2,3,1   n = 7, k = 4,start = 0

4,5,6,7,1,2,3   n = 3, k = 1, star = 4


参考了https://discuss.leetcode.com/topic/9406/3-lines-of-c-in-one-pass-using-swap/14


代码如下:

class Solution {public:    void rotate(vector<int>& nums, int k) {        if (nums.size() < 2) return;        int n = nums.size();        for (int start = 0; k %= n; n -= k, start += k) {            for (int i =0; i < k; i++) {                swap(nums[start + i], nums[nums.size() - k + i]);            }        }        return;    }};








原创粉丝点击