leetcode189.旋转数组 的三种方法


)

前言:

新开一个专栏

要学好编程肯定得多敲代码,于是刷题提高是重要的过程

该栏目内容专门记录刷leetcode题目以及刷题笔记,记录做题方法,巩固提高。

在做题之前我们需要了解一个东西
一般,常见的在线OJ的两种类型:接口型IO型

接口型:测试用例和结果一般是通过参数接口函数的参数和返回值交互和判对错,一般不需要写函数,直接调用参数即可。

IO型:测试用例通过IO输入输出,需要自己写头文件、main函数,自己写输入获取测试用例。

这两种OJ类型本质类同,都是通过网络传到后台服务器,去运行所写的程序,通过测试用例检测正确性。

那么这是一道来自leetcode的题目,题目描述如下:

image-20211023115336495

以下给出题目的链接,方便大家去练习。

同样的网站上也给出了提示

image-20211023115639217

同时这是一道接口型在线OJ,题目给出了接口函数和参数,如下:

void rotate(int* nums, int numsSize, int k){

}

接下来开始手撕题目,这题本身并不难

但是这种题目三种想法可以有效地开阔思路,特别是第三种方法,多刷刷这种题目将对本类题型有新的看法。

那么针对这道题目的话,我们有三种思路。

想法一:最朴素思路

整体概括为实现一个可以反转一次一个数字的循环,然后实现k次该循环,就实现了k次翻转。这里不开辟新数组

时间复杂度o(K*N)
空间复杂度o(1)
void rotate(int* nums, int numsSize, int k) {
	int tmp = 0;
	while (k >= numsSize)
	{
		k %= numsSize;
	}
	for (int i = 0; i < k; i++)
	{
		tmp = nums[numsSize - 1];
		for (int j = 0; j < numsSize - 1; j++)
		{
			nums[numsSize - 1-j] = nums[numsSize - 2-j];
		}
		nums[0] = tmp;
	}
}

我们主要的想法是创建一个tmp,利用tmp存放最后一个数,然后将前面numsSize-1个数往后移,这样可以实现1次旋转,如果我们要操作k次就是重复k次这样的操作就可以了。

但是这里为了减少冗余操作,我们写了一个循环如果K是>整个数组长度的话,那么我们利用%取模方法来将其降低为最少等同结果得次数并输出,这样就可以减少无必要的操作,这个循环后面依然会用到。

然而一次旋转得时间复杂度就是O(N),如果转k次就是o(K*N)

image-20211023183826371

所以经过测试,遇到大量数据就无法通过leetcodeimage-20211023184150333

想法二:利用新数组

这里我们的想法是为了不改变原先的数组排列顺序,我们要创建一个新的数组,其本质就是把第numsSize-k个数字变成新数组的第一个数组元素,然后依次放入按顺序新数组中。

时间复杂度o(N)
空间复杂度o(N)

image-20211023191159052

实现如下:

 int newArr[numsSize];

  for (int i = 0; i < numsSize; i++)

  { 

    newArr[(i + k) % numsSize] = nums[i];

  }

  for (int i = 0; i < numsSize; i++)

  {

    nums[i] = newArr[i];

  }

当然也可以把numsSize-k之后的数放在最前,然后再后面接numsSize-k之前的数,都可以

image-20211023191355059

想法三:3次逆置法

这个方法地主要思路是有技巧性和有规律的,也是效果最好的,我们需要对原数组实现三次逆置

时间复杂度o(N)
空间复杂度o(1)

三次倒置分别是

1.对原数组的前n-k个数倒置

2.对原数组的后k个数倒置

3.对原数组整体逆置

就可以完成旋转了

这里我们最好是写一个reverse函数,是数组倒置,数组导致函数 本身是很好写的,然后改变参数调用三次就可以了。

代码实现如下:

void reverse(int* num, int left, int right)
{
	while (left < right)
	{
		int tmp = num[left];
		num[left] = num[right];
		num[right] = tmp;
		++left; 
		--right;
	}

}
void rotate3(int* nums, int numsSize, int k)
{
	while (k >= numsSize)
	{
		k %= numsSize;
	}
	reverse(nums, 0, numsSize - k - 1);//前k个数
	reverse(nums, numsSize-k, numsSize-1);//后k个数
	reverse(nums, 0, numsSize- 1);//整个数组
}

但是这里我们需要注意一个点

倘若numsSize这里要比k来的,我们就会导致这个传到reverse函数的left或right<0,这样明显无发正确运行我们的程序,所以接下来的问题是要使得k比numsSize小,还是一样用%来使得k<numsSize就可以解决。

image-20211023191355059

总结:

以上就是本道题的三种想法

如果你觉得写的还行,有点收获,那么请给我三连(点赞、收藏、关注)吧,谢谢。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

言之命至9012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值