【leetcode】189 旋转数组

解法一: 一步步挪

步骤

1. 当k是0或数组长度整数倍时,旋转之后的数组就是原数组,所以只需要考虑k%nums.size()不为0的情况,将余数取出来赋给k,这个k才是真正需要旋转的数值。

2. 第一步中取得的余数不为0时才需要做旋转,否则不用。

3. 根据k的值,一步步挪:

    1)k只要不为0,每次挪动1位后k自减1,直到k减到0为止。

    2)挪动1位的做法是:将每一个数都往后挪一位,将最后的数挪到首位。

 

代码

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        if (k >= nums.size()) k %= nums.size();

        if (k != 0) {
            while (k) {
                int temp = nums[nums.size() - 1];
                for (int i = nums.size()-2; i>= 0; i--) {
                    nums[i + 1] = nums[i];
                }
                nums[0] = temp;
                k--;
            }
        }
    }
};

分析

时间复杂度:O(kn),当k很大时会超时。

 

解法二:分段反转再将全数组反转

思路

这个旋转数组的问题,可以按照题意一步步挪,也可以很巧妙的将数组分成两段分别反转,再将整个数组反转就可以得到结果。

举例:

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

分别反转[1,2,3,4]和[5,6,7]变成[4,3,2,1]和[7,6,5],拼起来就是[4,3,2,1,7,6,5],最后将整个数组进行反转,得到[5,6,7,1,2,3,4],这正是要求的结果。

步骤

1. 当k为数组长度的整数倍时,旋转后的数组就是原数组,所以仅考虑k不是数组长度整数倍的情况。

    1) 当k小于nums.size()时,k不变

    2) 当k大于nums.size()时,k取k除以nums.size()的余数。

2. 由于k是取余数得到的,k可能为0,根据上面的分析,k只考虑不为0的情况,仅当k不为0时:

    1) 反转 第0(即第一个) 到 第nums.size()-k-1 的元素。

    2) 反转 第nums.size()-k 到 第 nums.size()-1(即最后一个) 的元素。

    3) 反转 第0 到 第nums.size()-1 的元素,即全部反转。

3. 实现反转函数:

    1) 函数设计:void reverse1(vector<int>& a, int s, int e)

    2) 参数说明: vector<int>& a表示传入的数组a,int s表示要反转的start位置的元素在数组中的下标,int e表示要反转的end位置的元素在数组中的下标

    3) 实现步骤:

          s、e是双指针,初始情况分别指向首位元素,用while循环,每次让s、e指向的元素互换位置,然后让s、e同时向中间逼近,终止条件是两指针相遇。

代码

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        if(k>=nums.size())
            k%=nums.size();
        if(k!=0)
        {
            reverse1(nums,0,nums.size()-k-1);
            reverse1(nums,nums.size()-k,nums.size()-1);
            reverse1(nums,0,nums.size()-1);
        }
    }
    void reverse1(vector<int>& a,int s,int e)
    {
        while(s<e)
        {
            int temp=a[e];
            a[e]=a[s];
            a[s]=temp;
            e--;
            s++;
        }
    }
};

分析

时间复杂度:O(n)。

空间复杂度:O(1)。

 

解法三:环状替换

思路

对于旋转数组的问题,只要k不是nums.size()的整数倍,旋转之后的数组和原数组就会不同,并且每个元素都不在原先的位置上,即需要将数组中所有的元素都进行换位,所以旋转数组实际上一共需要换元素的次数是nums.size()次。

这个解法不是很好理解,找到一个非常形象的图解

(作者:xfzhao)
(链接:https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-yuan-di-huan-wei-xiang-xi-tu-jie/)
(来源:力扣(LeetCode))

两种情况:

一、A,B,C,D,E五位同学换座位,要求每个人都往后挪三个:

二、A,B,C,D四位同学换座位,要求每个人都往后挪两个:

对于长度为n的数组,整体移动k个位置,会出现上述两种情况:

1) n、k的最大公约数为1,1次遍历可以完成所有交换

2) n、k的最大公约数不为1,需要最大公约数次遍历

      比如A、B、C、D、E、F,n为6,此时k假设为2,那么n、k的最大公约数为2,则需要两次遍历完成所有交换。

      第一次遍历换的元素是:

      

      第二次遍历交换的元素是:

      

      每一次遍历都只会在自己的小循环中不停的换。共有n、k最大公约数次遍历,每次遍历的起始位置依次取前最大公约数个元素。

步骤

1. k大于等于nums.size()时,k = k % nums.size()

2. k不为0时,执行以下步骤

3. 初始化计数参数count,count表示交换元素的次数,一共要执行nums.size()次

4. 两层循环

    1) 外循环终止条件是count = nums.size(),start每次自加一,因为每次遍历的起始位置每次都要向后一位

    2) 当前位置curr从start位置开始,用pre存放curr位置的元素。

    3) 开一个新的循环来做这一组元素的交换,循环终止条件是当前位置curr和本组交换的元素初始位置start重合,循环具体要做的事:

          找到curr要去的位置next --> next位置的元素存放在temp中 --> 当前元素pre放到要去的next位置 --> temp放到pre --> 当前位置更新成next的位置(也就是为next位置的元素找下家) --> count自加一,表示经过这些步骤已经换了一次了,最后是要换nums.size()次。

代码

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len = nums.size();

        if (k >= len) k %= len;

        if (k != 0) {
            int count = 0;

            for (int start = 0; count < len; start++) {
                int curr = start;
                int pre = nums[curr];

                do {
                    int next = (curr + k) % len;
                    int temp = nums[next];
                    nums[next] = pre;
                    pre = temp;
                    curr = next;
                    count++;
                } while (curr != start);
            }    
        }
    }
};

分析

时间复杂度: O(n),一共只换了n次。

空间复杂度:O(1),使用了常数个额外空间。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值