LeetCode Rotate Array

189. Rotate Array

Given an array, rotate the array to the right by k steps, where k is non-negative.

Follow up:

  • Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
  • Could you do it in-place with O(1) extra space?

Example 1:

Input: nums = [1,2,3,4,5,6,7], k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]

Example 2:

Input: nums = [-1,-100,3,99], k = 2
Output: [3,99,-1,-100]
Explanation: 
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]

Constraints:

  • 1 <= nums.length <= 2 * 10^4
  • It’s guaranteed that nums[i] fits in a 32 bit-signed integer.
  • k >= 0

本题要把一个数组向右旋转k步,尽量想出3种方法。
首先,无论什么解法,都先判断一下n是否小于2,如果是的话,不管k是多少,都不必旋转了。其次,执行k%=n,因为移动n的倍数位相当于没移动。
暴力解法是每次保留最后一位数字,然后把前n-1个数往后移动一位,重复操作k次。代码如下:

class Solution {
public:
    void rotate(vector<int>& nums, int k)
    {
        int n = nums.size();
        if (n < 2 || k % n == 0)
            return;
        k %= n;
        while (k–) {
            int last = nums[n – 1];
            int j = n – 2;
            while (j >= 0) {
                nums[j + 1] = nums[j];
                –j;
            }
            nums[0] = last;
        }
    }
};

本代码提交TLE,过不了最后一个大数据集,因为最坏是O(n^2)复杂度。
稍微高明一点的方法是,先把末尾k位数保存到一个临时数组中,然后一次性把前n-k个数移动到末尾,最后再把临时数组中的数据回帖到原数组开头。完整代码如下:

class Solution {
public:
    void rotate(vector<int>& nums, int k)
    {
        int n = nums.size();
        if (n < 2 || k % n == 0)
            return;
        k %= n;
        vector<int> right(nums.begin() + n – k, nums.end());
        int i = n – k – 1, j = n – 1;
        while (i >= 0)
            nums[j–] = nums[i–];
        i = 0;
        while (i < k) {
            nums[i] = right[i];
            ++i;
        }
    }
};

本代码提交AC,用时29MS。
还可以对上述解法稍加变换,即把末尾k位数保存到临时数组right中,然后把原数组的前n-k个数拼接到right的后面,vector的拼接可以用insert。思路和上述算法是一样的。代码如下:

class Solution {
public:
    void rotate(vector<int>& nums, int k)
    {
        int n = nums.size();
        if (n < 2 || k % n == 0)
            return;
        k %= n;
        vector<int> right(nums.begin() + n – k, nums.end());
        right.reserve(n);
        right.insert(right.end(), nums.begin(), nums.begin() + n – k);
        nums = right;
    }
};

本代码提交AC,用时19MS。
最后还有一种很巧妙的方法,比如题目中的例子,要对1,2,3,4,5,6,7向右旋转3位,我们可以首先对整个数组逆序一下(reverse),编程7,6,5,4,3,2,1,然后再对前k=3,后n-k=4个数分别逆序,得到5,6,7,1,2,3,4,这不就是最终答案吗,很有意思。数组逆序可以直接用STL中的reverse,其实也可以自己实现,就是收尾元素不断交换swap。这种解法是常数空间复杂度、O(n)时间复杂度的,代码如下:

class Solution {
private:
    void my_reverse(vector<int>::iterator bg, vector<int>::iterator ed)
    {
        –ed;
        while (bg < ed) {
            swap(*bg, *ed);
            ++bg;
            –ed;
        }
    }
public:
    void rotate(vector<int>& nums, int k)
    {
        int n = nums.size();
        if (n < 2 || k % n == 0)
            return;
        k %= n;
        my_reverse(nums.begin(), nums.end());
        my_reverse(nums.begin(), nums.begin() + k);
        my_reverse(nums.begin() + k, nums.end());
    }
};

本代码提交AC,用时19MS。

Leave a Reply

Your email address will not be published. Required fields are marked *