leetcode189.旋转数组 多种解法详解

题目:旋转数组

给定一个数组,将数组中的元素向右移动 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) 的 原地 算法。

分析

题目的意思是将数组元素向右移动k个位置,首先想到的是暴力解法,先将移动一次的数组求出来,然后循环k次即可,当然这种方法超出了时间限制。既然这种方法不行,那么我们将相对复杂的问题化小,将数组插分成两个部分,这样明显简单多了。

解决方案

方法一:暴力解法:每次旋转一个元素,旋转k次 //注意在leetcode上超出了时间限制,均不可用,在这里我只是想提供一种思路分析,给思路稍微欠缺的人一种想法。

形式一:从后往前遍历,相对与形式二简单,清晰。当然你也可以将两个函数化为一个,只是我这种写法看起来更清晰一些。

class Solution {
public:
    void goForward(vector<int>& nums){//移动一次
        int temp=nums[nums.size()-1];
        for(int i=nums.size()-1;i>0;i--)
        {
            nums[i]=nums[i-1];
        }
        nums[0]=temp;
    }
    void rotate(vector<int>& nums, int k) {
        for(int i=0;i<k;i++)//循环k次,即移动k次
        {
            goForward(nums);
        }
    }
};

形式二:从前往后遍历,具体就不解释了,推荐形式一。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int temp, previous;
        for (int i = 0; i < k; i++) {
            previous = nums[nums.size() - 1];
            for (int j = 0; j < nums.size(); j++) {
                temp = nums[j];
                nums[j] = previous;
                previous = temp;
            }
        }
    }
};

方法二:使用额外的容器vector,将数组分割保存再拼接。

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len=nums.size();
        if(len<k){ //这个步骤必须写,很关键,记住len长度为一个周期,要不然下方len-k为负数
            k=k%len;
        }
        vector<int>temp(len-k,0);
        vector<int>temp2(k,0);
        temp.assign(nums.begin(),nums.begin()+len-k);//左闭右开,即[nums.begin(),nums.begin()+len-k),保存前段
        temp2.assign(nums.begin()+len-k,nums.end());//左闭右开,保存后段
        nums.clear();//清空操作
        nums.insert(nums.end(),temp2.begin(),temp2.end());//向nums.end()位置插入temp2 vector
        nums.insert(nums.end(),temp.begin(),temp.end());//再插入temp vector
    }
};

方法三:反转,将数组反转三次从而得到结果。

举个例子:初始数组为[12345],k=3,反转第一次为[54321],反转第二次为[34521],反转第三次为[34512]。

class Solution {
public:
    void reverse(vector<int>& nums,int begin,int end)//此函数功能是反转nums[begin],nums[end]之间的数组,包括两端
    {
        int temp;
        for(int i=begin;i<end;i++,end--)
        {
            temp=nums[i];
            nums[i]=nums[end];
            nums[end]=temp;
        }
    }
    void rotate(vector<int>& nums, int k) {
        int len=nums.size();
        if(k>len) //len为一个周期,k需要转换到小于或等于len,防止下面的函数超出nums界限
            k=k%len;
        reverse(nums,0,len-1);//反转第一次,将整个数组反转
        reverse(nums,0,k-1);//反转第二次,将数组的前k个数反转
        reverse(nums,k,len-1);//反转第三次,将数组剩余部分反转
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值