LeetCode每日一题--189. 旋转数组(数组)

题目:跳转至 189. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

进阶:

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

示例 1:
输入: nums = [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:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 2 * 104
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        
    }
};

思路:
最简单粗暴的,按题目意思每循环一次就把数组首位调换,其他位置右移位一次,时间复杂度肯定不够。

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

仔细看原数组和结果数组可以发现:

nums:[1, 2, 3, 4, 5, 6, 7]
右旋转3位后得到:[5, 6, 7, 1, 2, 3, 4]
借助的新数组:[5, 6, 7]

即是把后三位移到数组开头,那只要借助一个新的数组,从末尾遍历读取 k 个值,原数组整体右移 k 位,再把新数组中的值填入原数组即可。注意 k 与数组长度的大小关系。

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

黔驴技穷,题解就是比我想的花里胡哨的多。

方法一:使用额外数组
依旧使用这个例子:

nums:[1, 2, 3, 4, 5, 6, 7]
右旋转3位后得到:[5, 6, 7, 1, 2, 3, 4]
即要将旋转后对应的原数组下标:[4, 5, 6, 0, 1, 2, 3] 转换成新数组的正确下标:[0, 1, 2, 3, 4, 5, 6]

那就比较明显了,(原数组各位下标值 + k) % 数组长度即可。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> newArr(n);
        for (int i = 0; i < n; ++i)
            newArr[(i + k) % n] = nums[i];
        nums.assign(newArr.begin(), newArr.end());
    }
};

方法二:环状替换
这个本来想略略略,怕烦qwq。
和方法一中的思想类似,省略构建一个同样大小的额外数组的部分。
从位置 0 开始,直接借助变量 temp 交换 下标 0 与下标 (0 + k)% n 对应的数组值,令 x = (0 + k)% n,下一个交换下标 x 与下标 (x + k)% n 对应的数组值直至回到初始位置 0。

nums : [1, 2, 3, 4, 5, 6],k = 2 时,结果应该为:[3, 4, 5, 6, 1, 2]
如上述方法:
交换位置 0:[3, 2, 1, 4, 5, 6] --> [3, 2, 5, 4, 1, 6],回到初始位置。
交换位置 1:[3, 4, 5, 2, 1, 6] --> [3, 4, 5, 6, 1, 2],回到初始位置。
如下图,完成了整个交换。
在这里插入图片描述
最小公约数那个我就不强求自己理解了,使用单独的变量 count 跟踪当前已经访问的元素数量,当 count=n 时,结束遍历过程。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;
        int count = 0;  //计算当前已经访问的元素数量
        int start = 0;  //起始点
        while(count<n){  //当访问数量小于数组个数时
            int current = start;
            int prev = nums[start];
            //++count;  //这里原本是想选中一个数就计数(后面循环每次两两比较额外多加一个访问的数量,但是下面循环是do..while结构,最后执行到start==current退出循环,这一步不计数,下面就多算了一个++count,所以把上面的屏蔽掉就对了)
            do {
                int next = (current + k) % n;
                swap(nums[next], prev);
                current = next;
                ++count;
            } while (start != current);
            ++start;
        }
    }
};

方法三:数组翻转
这个就好理解多了,感觉这个也和我本身思路最像。

操作结果
原始数组1 2 3 4 5 6 7
翻转所有元素7 6 5 4 3 2 1
翻转 [0, k mod n-1] 区间的元素5 6 7 4 3 2 1
翻转 [k mod n, n-1] 区间的元素5 6 7 4 3 2 1
class Solution {
public:
    void reverse(vector<int>& nums, int start, int end) {
        while (start < end) {
            swap(nums[start], nums[end]);
            start += 1;
            end -= 1;
        }
    }

    void rotate(vector<int>& nums, int k) {
        k %= nums.size();
        reverse(nums, 0, nums.size() - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.size() - 1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值