LeetCode第189题,旋转数组,Java

问题:

思路:

方法1:
最简单的,声明一个新列表,然后遍历记录

方法2:

引入一个标记数,用来更改标记的数值,然后一个个找已经存入新的地点的值

循环k步,即可得到结果

123456flag
56
4
3
2
1
flag

复杂度分析:

时间复杂度:O(n)  //n为移动多少位

空间复杂度:O(1)

public void rotate(int[] nums, int k) {
        k = k%nums.length;
        for(int i=1;i<=k;i++){
            int flag = nums[nums.length-1];
            for(int j = nums.length-1;j>=1;j--){
                nums[j]=nums[j-1];
            }
            nums[0] = flag;
        }
    }

当然,这样超时了……不过,是为了给下一个方法做铺垫,所以我保留了下来

 方法3:

方法2的延伸,最开始我试图寻找,能不能在2的基础上实现一步搞定,然后发现那样引入的标记位置,可能要  数组后边的都需要了

然后在想,可不可以跟随我已经存好的,去挨个找之前的;

来画个表,假设需要移动2步

123456flag
step146
step22
step36
step45
step53
step62

如上表,在走到第三步的时候发生了循环,

循环的时候怎么处理呢,可以在出现循环的数字,也就是填完2之后,往前进一位,进行下一轮;

 不难看出,6步,分2组搞定

所以可以尝试,步骤如下:

1、记录第一个位置的值

2、替换第一个位置上的数,然后顺着这条链,往前探索,直到再一次出现要填第一个位置时

3、一组结束后,判断是否已经替换了n个数了,如果没有,往前走一位,然后再次重复1 2步骤

注意,我这里时倒着往前走的,所以时减法,但是又不能让数变成负的,所以每个更新完找到的下一个数的公式是:

i = (i + nums.length - k) % nums.length

如果选择往前走,那就是另外一种分析方法了,那样的公式,也会比这个简单

但是不管是向前还是向后,这几个公式都很重要。

代码:

public void rotate(int[] nums, int k) {
        k = k % nums.length;
		if (k != 0) {
			int sum = 0;// 用来记录已经更新了多少个数
			int index = -1;// 用来记录每轮的第一个;
			while (sum < nums.length) {
				index++;
				int flag = nums[index];
				nums[index] = nums[(index + nums.length - k) % nums.length];
				sum++;
				for (int i = (index + nums.length - k) % nums.length; i != index
						&& sum < nums.length; i = (i + nums.length - k) % nums.length) {
					if ((i + nums.length - k) % nums.length == index) {
						nums[i] = flag;
						sum++;
					} else {
						nums[i] = nums[(i + nums.length - k) % nums.length];
						sum++;
					}
				}
			}
		}



    }

我的代码 还没有进一步优化,优点费时间,但是对于java来讲,空间还好

细分析会发现,其实,数组的长度,走多少步,他们的最大公因数,就是可以分的组数,也是后来惊喜的发现是一个经典的方法

所以,可以提前算出 最大公倍数,然后就避免了后期的一步一步判断是不是已经更新完了,代码我还没写~

这是我尝试了几个之后才感觉到的规律,尝试的心路历程如下,当然也可以不看啦~

数组123456
1组624
2组513

 惊喜的发现:6个数移动2步的时候,可以分成两组

再多尝试几种情况:

数组123456
1组63
2组52
3组41

6个数移动3步,可以分成3组,试想,难道跟移动的步数有关系?

数组123456
1组462
2组351

 6个数移动4步的时候,就不是4组,而是2组,这个时候就可以大致才出来,可能与公倍数有关系

不妨再7个数试试,会发现不管移动多少步,都只能分成1组了

数组1234567
1组5671234

方法4:

看了答案后,发现了很牛的反转

例如  1 2 3 4      5 6 7    //移动3步

反转  7 6 5      4 3 2 1    //先对整个数组反转,这样就可以把,循环的时候后边的数字移到前边

再反  5 6 7      1 2 3 4   //在分别对  前3个反转,还有后边剩余的部分

代码:

public void rotate(int[] nums, int k) {
        k = k%nums.length;
        if(k!=0){
            reverse(nums,0,nums.length-1);
            reverse(nums,0,k-1);
            reverse(nums,k,nums.length-1);
        }
    }
    public int[] reverse(int[] nums, int a , int b){
        //双指针实现反转
        while(a<b){
            int m = nums[a];
            nums[a] = nums[b];
            nums[b] = m;
            a++;
            b--;
        }
        return nums;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值