[LeetCode 189] Rotate Array——我五个小时的纠结

90 篇文章 0 订阅

Rotate an array of n elements to the right by k steps.

For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].

Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.

Hint:
Could you do it in-place with O(1) extra space?

Related problem: Reverse Words in a String II

2015/3/13

这道题从昨天下午四点半开始一直折磨我到晚上九点半,中间没去吃饭。这道题用一个简单的循环(每次移动一个元素)就能解决,只是时间复杂度是O(n^2),空间复杂度是O(1)罢了。但当时一见到这题,脑子不知道哪根线搭错了,想找出一种时间复杂度为O(n)的方法,借鉴快排的思想,一次循环就能定位出每个元素的最终位置。

第一次尝试:

主要想法是每个元素都向后移动k位,假如当前元素是i,那么它要移动到(i+k)的位置,第(i+k)个元素就要移动到(i+k+k)的位置,一直到元素要移动的位置超过了数组的长度。这时,换(i+1)个元素重新循环。这样的循环要进行k次。以题目中的例子作为说明,1移动到4,4移动到7,7应该移动到10,但10应该是nums[9],9超过了数组的长度7,所以用10-7=3,那么7就要移动到3的位置。此次循环结束,换1+1=2继续循环。

这种方法遇到的问题是有可能第j个元素还没有进行循环,但它的值被第j个元素前面的第i个元素进行循环时替换掉了,原有的nums[j]的值丢失。因此这种方法是错误的。

第二次尝试:

如果nums[i]移动到nums[j],那么保存nums[j]的值,再计算出nums[j]应该移动到的位置k,用保存的nums[j]的值替换nums[k]。循环(n+1)次。这种方法执行过程与第一种方法是一样的,当第一种方法的某一次循环结束时,即计算出的位置超过了数组长度时,并不是从头开始换下一个元素重新循环,而是计算出当前被替换元素应该移动到的位置继续替换。还是以题目中的实例作为说明,1到4,4到7,7到3(此时不再重头进行下一个元素的循环)3到6,6到2,2到5,5到1,程序结束。至于为什么要循环(n+1)次,因为当nums[j]被替换时,nums[j]应该接收前一个元素nums[i]传递过来的值,并把它的值保留用于下一次替换。那么当程序开始进行循环时,所有中间量的初始值都应设置为nums[0],那么第一次循环进行的操作就是nums[0]接收nums[0]自己,第二次循环才是nums[0]移动到最后的位置。这就浪费了一次循环,所以n个值要进行n+1次循环。

这种方法在数组长度n与循环次数k互质的时候,是可以正确运行的,例如题目中的例子。但当n与k存在公约数时,程序将会在一些元素中来回替换,其他元素得不到替换的机会。如[1,2,3,4],n=4,k=2,循环的过程就变为1到3,3到1,3到3,3到3,3到3。也就是说替换在一些元素中循环进行。

第三次尝试:

第二种方法失败在于替换只在一些元素中循环进行,其他元素得不到替换的机会。那么循环会在什么样的元素中进行呢?假如n=8,k=2,按照上述方法的思路,其实是把8个元素以2个为一组,分成了4组,每组的第一个元素将被替换,而第二个元素就得不到替换机会。扩展到其他情况,n=8,k=6,6不是8的约数,所以分组不那么明显,但把数组扩展到8和6的最小公倍数24,对于24来说,6将24分成了4组,每组的第一个元素:1,7,13,19是循环替换的。再将它们换算成数组中的元素位置,即第1个,第7个,第5个和第3个。再举一些其他例子我们就会发现,当n和k的最大公约数不为1的时候,n和k的最大公约数(n,k)就将n分成了n/(n,k)组,每组的第一个元素才有替换机会。

昨天的这道题我发现了,我应该是不适合搞算法的……数学真是天生的短板,但真正搞出来优化的算法还是相当令人振奋的,估计以后要作为兴趣搞一搞吧~

按照上述思路,代码实现应该分为两层循环,外层循环遍历每组中的元素,内层循环遍历每组中特定的元素。昨天实在被这道题搞的不行了,今天早上一来就写博客,等过一两天再实现吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值