空间复杂度O(1)——旋转数组(Leetcode)

🚗题目描述

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

Q:你能用空间复杂度O(1)原地解决这个问题吗?

示例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]

🧀 解法1:暴力求解,旋转k次

 利用temp和end,end为数组最后一位,将nums[j]存放在temp中,将end→nums[j],经过一次旋转后得到 7 1 2 3 4 5 6,旋转三次后即得到 5 6 7 1 2 3 4

时间复杂度为:O(N)=k*N

空间复杂度为:O(N)=1

  图释:

 代码实现如下:

void Rotate(int *nums,int numsize,int k){
	k%=numsize;
	for(int i=0;i<k;i++)
	{
		int end = nums[numsize-1];
		int temp; 
		for(int j=0;j<numsize;j++)
		{
			temp=nums[j];
			nums[j]=end;
			end=temp;
		}
	 } 
}
int main(){
	int nums[7],k;
	for(int i=0;i<7;i++)
	{
		cin>>nums[i];
	}
	cout<<"k=";
	cin>>k;
	Rotate(nums,7,k);
	for(int i=0;i<7;i++)
	{
		cout<<nums[i]<<" ";
	}
	return 0;
}

🧀 解法2:巧妙逆置算法

思路:将长度为numsize数组分为两部分,一部分为[0,numsize-k-1],另一部分为[k,numsize-1],分别进行转置,得到数组再全部进行转置即可巧妙得到题目要求的

时间复杂度:O(N)

空间复杂度:O(1)

图释:

 

 代码实现如下:(需要先写转置代码)

void Reverse(int *nums,int left,int right)
{
	while(left<right){
		int temp=nums[left];
		nums[left]=nums[right];
		nums[right]=temp;
		left++;
		right--;
	}
}
void Rotate(int *nums,int numsize,int k){
    k%=numsize;
	Reverse(nums,0,numsize-k-1);
	Reverse(nums,numsize-k,numsize-1);
	Reverse(nums,0,numsize-1);
}
int main(){
	int nums[7],k;
	for(int i=0;i<7;i++)
	{
		cin>>nums[i];
	}
	cout<<"k=";
	cin>>k;
	Rotate(nums,7,k);
	for(int i=0;i<7;i++)
	{
		cout<<nums[i]<<" ";
	}
	return 0;
}

🧀 解法3:拷贝法(空间复杂度不为O(1))

        创建一个新的数组,将原数组的后k个数即[numsize-k,numsize-1]拷贝到新数组的前面[0,k-1],原数组前面的数拷贝到新数组后面[k,numsize-1]。

时间复杂度为:O(N)

空间复杂度为:O(N)

图释: 

 

 代码实现如下:

void Rotate(int *nums,int numsize,int k)
{
    k%=numsize;
	int newnums[numsize];
	for(int i=0;i<=numsize-k;i++)
	{
		newnums[i]=nums[numsize-k+i];
	}
	for(int i=numsize-k+1,j=0;i<numsize;i++,j++)
	{
		newnums[i]=nums[j];
	}
	for(int i=0;i<7;i++)
	{
		cout<<newnums[i]<<" ";
	}
}
int main(){
	int nums[7],k;
	for(int i=0;i<numsize;i++)
	{
		cin>>nums[i];
	}
	cout<<"k=";
	cin>>k;
	Rotate(nums,7,k);
	return 0;
}

🧀 解法4:环状替换法

从起点开始,每次以K为单位进行跳跃式的替换,每个元素都会进行一次替换,进行了N次替换,即完成了数组的旋转;
注意点:
1.当下标+K大于数组长度的时候,就需要减去长度回到”起点“处;
2.回到起点后,判断回来的位置是否和出发的位置重复,如果不重复,那么一直进行下去,直至完成N此跳跃,形成一个环圈;
如果重合了,每次回到起点后就将起点完后挪动一个位置,直至完成N次跳跃;

时间复杂度为:O(N)

空间复杂度为:O(1)

 

 代码实现如下:

void Rotate(int* nums, int numsize, int k){
	k %= numsize;
	int count = 0;
	int previous = nums[0];//先将出发点元素储存起来,和遍历法类似
	int lable = 0;//标记出发点,用于判断返回点是否和出发点重合
	int sub = k;
	while (count < numsize)//count=元素个数时跳出循环,即完成了size次交换
	{
		if (sub < numsize)//跳跃之后没有超过数组长度
		{
			int temp = nums[sub];
			nums[sub] = previous;//进行替换
			count++;
			previous = temp;
			sub += k;//跳跃K个距离
		}
		else//即将完成一个系列的转换
		{
			int temp = nums[sub - numsize];//超过了数组长度,回到起点
			nums[sub - numsSize] = previous;
			count++;
			previous = temp;
			if (sub - numsize == lable)//回到起点的位置和出发位置相同
			{
				sub = sub - numsize + 1;
				lable = sub;//继续标记出发位置
				previous = nums[sub];//重新储存起点
				sub += k;
			}
			else
			{
				sub = sub - numsize + k;
			}
			
		}
	}
}

---------以上为四种方法,仅供参考~ 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值