leetcode 189. 旋转数组

问题描述

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:

输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释: 
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:

尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。

代码1

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

多使用了\(O(k)\)的空间,明显不符合题目要求。结果:

执行用时 :8 ms, 在所有 C++ 提交中击败了80.09%的用户
内存消耗 :10 MB, 在所有 C++ 提交中击败了5.00%的用户

代码2

根据(i + k) % n = 旋转后的位置,可以将原有数组中的数据复制到新数组中

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

多使用了\(O(n)\)的空间,结果

执行用时 :8 ms, 在所有 C++ 提交中击败了80.09%的用户
内存消耗 :10.1 MB, 在所有 C++ 提交中击败了5.00%的用户

代码3

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k %= n;
        if(k == 0) return;
        int nextval,curind,curval,nextind,cnt = 0;
        for(int start = 0; cnt < n; start++)
        {
            curind = start;
            curval = nums[curind];
            nextind=(curind+k)%n;
            do{
                nextval = nums[nextind];
                nums[nextind] = curval;
                curind = nextind;
                curval = nextval;
                nextind = (curind+k)%n;
                cnt++;
            }while(curind != start);
        }
    }
};

结果

执行用时 :8 ms, 在所有 C++ 提交中击败了80.09%的用户
内存消耗 :10.2 MB, 在所有 C++ 提交中击败了5.26%的用户

分析:以 nums= [0 1 2 3 4 5 6],k = 3为例,下标交换顺序为 0 -> 3 -> 6 -> 2 -> 5 -> 1 -> 4 -> 0,具体过程如下:

[start 1 2 0 4 5 6] -> [start 1 2 0 4 5 3]   -> [start 1 6 0 4 5 3]  
           |     |              |       |                |     |                  
       curind=3 nextind       nextind  curind=6      curind=2 nextind       
-> [start 1 6 0 4 2 3]   -> [start 5 6 0 4 2 3] -> [start 5 6 0 1 2 3]
          |       |                |     |           |   
       nextind curind=5       curind=1 nextid     curind=0

但存在一个问题,如果k是n的约数的话,会有元素遍历不到,例如nums=[0 1 2 3 4 5],k=2,则遍历的下标为0 -> 2 -> 4 - > 0,显然1,3,5没有遍历到,需要将start=1后再循环一遍。

posted @ 2020-02-20 10:17  曲径通霄  阅读(79)  评论(0编辑  收藏  举报