LeetCode189.旋转数组

前言:刷LeetCode时室友让我做道题目,就是这道旋转数组。我看这道题目的时候自己想的,做完跟室友一讨论发现是一个比较好的方法,比较快的方法一般也就是三次反转。我的方法比比三次反转还快一点。

下面先看题目:

1、我的方法

题目中要求使用原地算法,并且如果时间复杂度是O(n^2)的话,会报“超出时间限制”。

我的方法是从nums[0]开始,每次移动k的位置:即将nums[0]的值移动到nums[0+k],nums[k]移动到nums[k+k],依次类推,每次都移动到最终的位置,这样的方法不需要额外的数组空间,只需要几个变量临时存储以便数组元素交换之用。具体解释如下。

假如说数组长度为5,k=2,示意图如下:

0位 -> 2位 -> 4位 -> 1位 -> 3位 -> 0位, 这样刚好完成一个闭合;但是数组长度 len 与 k 的组合未必一定能一次就完成一个闭合操作,就比如len = 6,k = 2的情况,示意图如下:

一次循环,只移动了一半的元素,其他的元素没有移动,这个时候就需要第二轮循环,第二轮循环从下标为1的元素开始,如下

这样就完成了所有元素的移动,但是问题来了,怎么确定移动几轮呢,原本以为是k轮,但是换一种情况只知道自己认识的肤浅了,比如len=6,k=4的情况,移动示意图如下:

第一轮

注意每个元素都是从右边移动四个位置,当右边到头了就要从左边算起了,比如4,往右移一下就到头了,所以还要折到左边开头再移动三格就是到了2的位置。

第二轮

所以发现2轮也完了,这个时候如果多移的话就是画蛇添足了。我本来以为移动多少轮会跟len和k的关系有关系,后来绕了一大圈发现并没有什么关系,就在行将放弃的时候,发现一个简单的控制就可以完美解决。思路就是加一个计数变量count,每次一个位置移动成功就加一,当count等于len的时候不就完事了么?所以有时候发现思维定式坑死人,真的是最简单的方法往往是最难想到的哈哈! 下面直接上代码:

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
       int temp1, temp2, index, count = 0;//count为计数变量,记录移动成功的次数,temp1和temp2是辅助存储变量,为交换元素值之用
        int len = nums.size();
        k %= len;//对k值的前期处理,处理原因是如果k值比len大,那么只有大于len的那部分才是有效移动
        if(k == 0)
            return;//如果k等于0或者k原本是等于len的,那么就相当于没有移动嘛,直接返回
        for (int i = 0; i <= k; i++) //移动的轮数最多k次,当然计数变量count=len的时候会跳出循环
        {
            if(count >= len)
                break;//对计数变量的控制,当所有位置全部移动完了就可以结束了
            index = i;//每轮移动的初始下标
            temp1 = nums[i];
            while((index + k)%len != i) //一个while循环移动一次
            {
                temp2 = nums[(index + k)%len];
                nums[(index + k)%len] = temp1;
                index = (index + k)%len;
                temp1 = temp2;
                count++;
            }
            nums[i] = temp1;
            count++;
        }
	}
};

复杂度分析:代码虽然看着有两个循环,但是因为总的循环次数只有len,因此是O(N)的线性复杂度。最后的提交结果如下:

2、三次反转数组

这个方法我听室友一讲,立马想起来,这道题目、这个方法我考研的时候应该是做过的,这个三次数组反转的方法还是比较经典的,假如len = 7, k = 3,则过程如下:

要先实现一个数组反转的函数,或者使用C++自带的reverse函数。代码如下:

class Solution {
public:
    //数组反转函数
    void reverseArray(vector<int>& array, int begin, int end)
    {
        int temp, tmp_end = end;
        for (int i = begin; i <= (begin + end)/2; i++)
        {
            temp = array[i];
            array[i] = array[tmp_end];
            array[tmp_end] = temp;
            tmp_end--;
        }
    }
    
    void rotate(vector<int>& nums, int k) {
       int len = nums.size();
        k %= len; 
        if(k == 0)
            return; 
        //使用自定义的反转函数
        reverseArray(nums, 0, len - k - 1);
        reverseArray(nums, len - k, len - 1);
        reverseArray(nums, 0, len - 1);

        //使用C++自带的反转函数
	    /*reverse(nums.begin(), nums.end() - k);
	    reverse(nums.end() - k, nums.end());
	    reverse(nums.begin(), nums.end());*/
	}
};

复杂度分析:三次反转的时间复杂度也是O(N)。

使用自定义反转函数的提交结果:

使用C++的reverse函数的执行结果:

一个比一个慢哈哈~ 还是上面我实现的方法比较快。

  • 14
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值