LeetCode 189. 轮转数组

一、题目

  给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [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:

输入: nums = [-1,-100,3,99], k = 2
输出: [3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1)原地 算法解决这个问题吗?

  点击此处跳转题目

二、Java 题解

  自己拿到题目后的第一反应是将每个元素向前移动 k 个位置,直至遇到先前移动过的元素,则停止该组移动,进行下一组移动。
  例如,对于 length = 9 的数组,k = 3,其具体流程如下所示:
↓ ← ← ← ← ← ↑ ↓ ↑ 1 ‾ 2 3 4 ‾ 5 6 7 ‾ 8 9 ↓ ↑ ↓ ↑ ↓ → → ↑ ↓ → → ↑ \begin{array}{l} \downarrow\hspace{0.8em} \leftarrow \hspace{0.6em} \leftarrow \hspace{0.6em} \leftarrow \hspace{0.5em} \leftarrow \hspace{0.5em} \leftarrow \hspace{0.8em} \uparrow \hspace{1.5em} \hspace{1.5em} \\ \downarrow\hspace{1.3em} \hspace{1.3em} \hspace{1.2em} \hspace{2.4em} \hspace{1.3em} \hspace{1.3em} \uparrow \hspace{1.5em} \hspace{1.5em} \\ \underline{\bold{1}} \hspace{1em} 2 \hspace{1em} 3 \hspace{1em} \underline{\bold{4}} \hspace{0.9em} 5 \hspace{1em} 6 \hspace{1em} \underline{\bold{7}} \hspace{0.9em} 8 \hspace{1em} 9 \\ \downarrow\hspace{1.3em} \hspace{1.3em} \hspace{1.4em} \uparrow\downarrow \hspace{1.1em} \hspace{1.3em} \hspace{1.4em} \uparrow \hspace{1.5em} \hspace{1.5em} \\ \downarrow\hspace{0.8em} \rightarrow \hspace{0.7em} \rightarrow \hspace{0.5em} \uparrow\downarrow \hspace{0.6em} \rightarrow \hspace{0.5em} \rightarrow \hspace{0.7em} \uparrow \hspace{1.5em} \hspace{1.5em} \\ \end{array} 123456789↑↓↑↓

  记 lengthk 的最大公约数为 round,因此在该例子中将数组分为 round = 3 组,上图表示第一组,即 [1, 4, 7],为一个循环,其中每个数不断前进 k 个位置,最终会无限循环下去。每一组的起点记为 start;使用 i 遍历,每次 + knext 为下一个即将到达的位置,其值为 i + kval 记录下一个位置的值,即 val = nums[next]last 记录上一个位置的值,用于对下一个位置 next 进行赋值,last = val(上一次遗留下来的 val)。
  当 next == start 时,表示循环遍历完成,进行特殊处理:

  1. 对起点位置进行赋值,即 nums[start] = val
  2. 进行下一组的移动,将 start++,且更新 i,即 i = start
  3. 更新 last,即 last = nums[i]

  最终所有组移动完毕,结束算法。

class Solution {
    public void rotate(int[] nums, int k) {
        if (k == 0) return;
        int n = nums.length, round = GCD(n, k);
        if (round == n) return;
        int i = 0, last = nums[i], val = nums[k % n], start = i, next;
        do {
            next = (i + k) % n;
            if (next == start) { // 循环结束,特殊处理
                nums[next] = val;
                i = ++start;
                last = nums[i];
                round--;
                continue;
            }
            // 将元素后移 k 个位置
            val = nums[next];
            nums[next] = last;
            last = val;
            i = next;
        } while (round > 0);
    }

    // 辗转相除法求解最大公约数
    public int GCD(int a, int b) {
        if (a < b) return GCD(b, a);
        if (a % b == 0) return b;
        return GCD(b, a % b);
    }
}
  • 时间:1 ms,击败 62.24% 使用 Java 的用户
  • 内存:52.65 MB,击败 83.18% 使用 Java 的用户

  当然,这道题的最优解还是翻转三次数组啦~

class Solution {
    public void rotate(int[] nums, int k) {
        k %= nums.length;
        Reverse(nums, 0, nums.length);
        Reverse(nums, 0, k);
        Reverse(nums, k, nums.length);
    }

    // 翻转 [start, end) 区间
    public void Reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start++] = nums[--end];
            nums[end] = temp;
        }
    }
}
  • 时间:0 ms,击败 100.00% 使用 Java 的用户
  • 内存:52.61 MB,击败 86.35% 使用 Java 的用户
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蔗理苦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值