题目:旋转数组
给定一个数组,将数组中的元素向右移动 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);//反转第三次,将数组剩余部分反转
}
};