189. 旋转数组

189. 旋转数组

给定一个数组,将数组中的元素向右移动 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 次,每次将数组旋转 1 个元素。

class Solution {
    public void rotate(int[] nums, int k) {
        int temp = nums[nums.length - 1];
        int number = 0;
        
        while(number < k){
            for (int i = nums.length - 2; i >= 0;i --){
                Solution.swap(nums,i,i+1);
            }
            number ++;
        }
        
    }
    
    public static void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
复杂度分析
  • 时间复杂度:O(n*k)O(n∗k) 。每个元素都被移动 1 步 O ( n ) O(n) O(n) k k k O ( k ) O(k) O(k)
  • 空间复杂度: O ( 1 ) O(1) O(1)没有额外空间被使用

方法二:环状替代

引用处

我们假设现在有 A 、B 、C、 D、 E 五名同学,今天考试完,老师要求换座位,每个同学向后移动 3 3 3 个座位
于是就从 A 同学开始换座位了… (下图左边)

  1. A同学 非常自觉,看了看自己座位号(0),根据老师要求,他走到了3 号位置,即 D 同学的位置,同时他把D 同学赶到了角落,自己坐在了 3 号位置,第一个完成任务真爽!
  2. D同学 一看,不行啊,我咋能呆在角落,于是D同学也按要求理直气壮来到了1号位置,同样把B同学赶到了角落,猛男落泪…
  3. B同学 当然也不干,气汹汹走到了4号位置,“E同学,麻烦起来一下,角落给你收拾好了:)”,于是 E同学来到角落…
  4. E同学 一想:不行呀,我这么帅,必须有座位!站起来跑到了2号位置,二话不说,赶走了C,坐上上去,一下子舒服了…
  5. C同学 此时来了角落,想:不是每个人都有座位吗??还需要抢?于是C从容的来到了0号位置,至此,所有同学都坐好了。

以上是 下图中左边 的情况,这个算法还会遇到另外一种情况,就是今天E同学请假,老师说那就每个人往后移动 2个座位。于是大家开始行动,和上面发生的一样,不过,当第二轮C同学坐好了以后,角落没人了,大家都有位子,就没人闹意见了…
老师一看,我精心布置的局,咋就停了呢,于是吼了一嗓子:“咳咳!还有谁没换?第二组打头的那个!B同学!是不是你!你们第二组学学第一组,第一组早早的换完了,你们组还一个没动!”
B同学一听到喊自己的名字,秒怂…本来想偷懒,无奈换起来座位…就这样,随着第二组的同学换完座位,最终大家完成了换座位
(上面老师喊的那一嗓子,就是我们内循环退出,即C回到了起始位置0位置,这时候我们就将起始位置 Start + 1)

在这里插入图片描述

(补充)关于上述两种情况何时出现:

其实是这样的,对于一个长度为 n n n 的数组,整体移动 k k k 个位置

  • n n n k k k 的最大公约数 等于 1 的时候:1 次遍历就可以完成交换;比如 n n n = 5, k k k = 3
    n n n k k k 的最大公约数 不等于 1 的时候:1 次遍历是无法完成的所有元素归位,需要 m m m (最大公约数) 次

所以在最大公约数不为 1 的时候
比如 [ A , B , C , D , E , F ] [A,B,C,D,E,F] [A,B,C,D,E,F] 此时 n n n = 6 6 6 , k k k = 4 4 4,其最大公约数为 2 2 2 ,因此需要 2 2 2 轮循环
我们就可以把这个数组分成两部分来看:
第 1 轮循环(分组1): A C E A C E ACE
第 2 轮循环(分组2): B D F B D F BDF

即:每一轮循环只会在自己的那一组上不停的遍历。

  • 所以数组的前 m 个元素,其实就是每一个分组的第一个元素,我们控制流程在每次发现一轮循环走到原点时+1

那么如何判断所有的分组都执行归位了呢? 可以有两种方法来控制

  • 第一种:我们就用最大公约数 m 来控制外循环,代表总共有 m 轮循环
  • 第二种:由于n个元素归位需要n次交换,所以我们定义一个count代表交换次数,当 count = n 时完成
class Solution {

    public void rotate(int[] nums, int k) {
        int len = nums.length;
        k = k % len;
        int count = 0;//计数,统计交换的次数

        //第一层循环在 len % k == 0,时执行,
        for (int i = 0; count < len; i++) {
            int current = i;    //当前位置
            int prev = nums[i]; //交换的数字
            do {
                int next = (current + k) % len; 

				//下一个位置两个数字交换位置
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;

                current = next;//更新当前位置
                count ++;
            }while (i != current);
        }
    }
}

方法三:使用反转

这个方法基于这个事实:当我们旋转数组 k k k 次, k k k % n n n 个尾部元素会被移动到头部,剩下的元素会被向后移动。

在这个方法中,我们首先将所有元素反转。然后反转前 k 个元素,再反转后面 n-k 个元素,就能得到想要的结果。

假设 n=7且 k=3。

原始数组 : 1 2 3 4 5 6 7
反转所有数字后 : 7 6 5 4 3 2 1
反转前 k 个数字后 : 5 6 7 4 3 2 1
反转后 n-k 个数字后 : 5 6 7 1 2 3 4 --> 结果

class Solution {

    public void rotate(int[] nums, int k) {
        int len = nums.length;
        k = k % len;
        
        reverse(nums,0,len-1);
        reverse(nums,0,k-1);
        reverse(nums,k,len-1);
    }
    
    public void reverse(int[] nums,int start,int end){
        while(start < end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            
            start++;
            end--;
        }
    }
}

复杂度分析

  • 时间复杂度:O(n)。 n 个元素被反转了总共 3 次。
  • 空间复杂度:O(1)。 没有使用额外的空间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值