LeetCode 旋转数组

传送门

题目描述

给定一个数组,将数组中的元素向右移动 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) 的原地算法。
假·解答一

先来看一种不符合题目要求的解法

  • 时间复杂度:O(n)
  • 控件复杂度:O(n)

因为第i个元素向右移动k个位置的位置是(i + k) % nums.length,所以我们可以创建一个大小为nums.length的辅助数组tmp,然后遍历数组nums,把nums[i]复制到tmp[(i + k) % nums.length)],最后用tmp覆盖nums就行了。

class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length == 0 || (k %= nums.length) == 0) return;
        int[] tmp = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            tmp[(i + k) % nums.length] = nums[i];
        }
        System.arraycopy(tmp, 0, nums, 0, nums.length);
    }
}
假·解答二

再来一种不符合题目要求的解法

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

创建一个大小为k的辅助数组tmp,先把nums的后nums.length - k个元素复制到tmp中,然后把nums的前k个元素挪到后面,最后把tmp中的元素复制到nums的起始位置。

class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length == 0 || (k %= nums.length) == 0) return;
        int[] tmp = new int[k];
        System.arraycopy(nums, nums.length - k, tmp, 0, k);
        System.arraycopy(nums, 0, nums, k, nums.length - k);
        System.arraycopy(tmp, 0, nums, 0, k);
    }
}

真·解答一

  • 时间复杂度:O(n2)
  • 空间复杂度:O(1)

每次向右移动1个位置,重复k次,就相当于向右移动k个位置。
向右移动1个位置的时候先把最后的元素记录到t中,然后遍历数组nums把让nums[j] = nums[j - 1],最后把t放到nums[0]就完成了。

class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length == 0 || (k %= nums.length) == 0) return;
        for (int i = 0; i < k; i++) {
            int t = nums[nums.length - 1];
            for (int j = nums.length - 1; j > 0; j--) {
                nums[j] = nums[j - 1];
            }
            nums[0] = t;
        }
    }
}
真·解答二
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

通过不停地交换数组元素来实现。
例如nums = [1, 2, 3, 4],k=2

  • 第一次交换:start = 0, i = 2, nums = [3, 2, 1, 4]
  • 第二次交换:start = 0 1, i = 0 3, nums = [3, 4, 1, 2]

需要注意的是当i == start的时候需要把start移动到下一个位置,并且把i重置为start,否则的话会在nums.length % k == 0的那些元素那里循环,处理不到nums.length % k != 0 的那些元素。

class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length == 0 || (k %= nums.length) == 0) return;
        int cnt = 0, start = 0, i = 0;
        while (cnt < nums.length) {
            i = (i + k) % nums.length;
            if (i == start)
                i = ++start;
            else 
                swap(nums, i, start);
            cnt++;
        }
    }

    private void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}
真·解答三
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

采用翻转的方法,先翻转前nums.length - k个元素,在翻转后k个元素,最后翻转整个数组。
例如nums = [1, 2, 3, 4, 5], k = 2

  • 第一次翻转:nums = [3, 2, 1, 4, 5]
  • 第二次翻转:nums = [3, 2, 1, 4, 5]
  • 第三次翻转:nums = [5, 4, 1, 2, 3]
class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length == 0 || (k %= nums.length) == 0) return;
        reverse(nums, 0, nums.length - k - 1);
        reverse(nums, nums.length - k, nums.length - 1);
        reverse(nums, 0, nums.length - 1);
    }

    private void reverse(int[] a, int start, int end) {
        while (start < end) {
            int t = a[start];
            a[start++] = a[end];
            a[end--] = t; 
        }
    }
}
参考文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值