189.[LeetCode] Rotate Array

反转字符串,这是一个非常经典的题,算法很多种
我的算法是借鉴《编程之法》一书中的 三步反转法(题中的移动操作其实就是变向的反转)

三步反转法非常好理解,简单来说就是

数据(数组,字符串等),由 A,B两个部分组成:
<—A— | <—B—

变化后的目标为:
<—B— | <—A—

那我们可以用三步反转来组成:

第一步, 反转A:—A—> | <—B—

第二步,反转B:—A—> | —B—>

第三步,反转整个数据,也即是AB: <—B— | <—A—

数学公式来表示就是:(X^TY^T)^T=YX

public class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        // 首先,右移位数需要化为n以内数,移动等于n则相当于没有移动
        k %= n;
        // 交换点为 n-k
        int swap = n-k;
        reverse(nums,0,swap-1);
        reverse(nums,swap,n-1);
        reverse(nums,0,n-1);
    }

    private void reverse(int[] nums,int begin,int end){
        int i = begin;
        int j = end;
        int temp;
        while(j>i){
            temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
            i++;
            j--;
        }
    }
}

下面来介绍一些其他的算法:

暴力转移法:最直接的方法,直接一位一位的移动,但是这样的时间代价很高 O(n*k),leetcode也不会通过,超时


额外空间法:

新创建一个数组,从 n-k 的位置开始读入,到尾部就从头读入。最终 再使得 nums = newNums 即可

时间和空间的复杂度都是 O(n)

public class Solution {
    public void rotate(int[] nums, int k) {
        int[] a = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            a[(i + k) % nums.length] = nums[i]; // 这一步是最关键的
        }
        for (int i = 0; i < nums.length; i++) {
            nums[i] = a[i];
        }
    }
}

循环置换法,这个方法空间复杂度为 O(1),事件复杂度为 O(n)

真正的思路其实异常的简答清晰,细节部分可以慢慢理解

首先来讨论,为什么暴力移动法是非常耗时的?

1,2,3,4,5,6

如果我要移动2位 那么如果用暴力移动法 第一次移动的结果;

6,1,2,3,4,5

现在,每一个数字都在错误的位置上,但是有没有可能避免这种错误置位,直接将数字移动到正确的位置呢?

直接移动到 数字移位的正确的位置,就是循环移位的核心了。


如上面的举例,

1,2,3,4,5,6

1移动两位应该在3的位置

X , 2,1, 4,5,6 (X表示目前未知)

而3应该在 5的位置

X , 2,1, 4,3,6

5应该在1的位置

5 , 2,1, 4,3,6

这样就完成了一次循环,这次循环过后,所有交换过的位置,都是最终移位后的位置,对比可见

5 , 2,1, 4,3,6 (一次循环后的)
5, 6,1, 2,3,4 (最终的)

可以看到我们交换的 1,3,5都是正确的。

这样的循环,执行k次,就可以完成了

代码如下,细节可以自己理解了:

public class Solution {
    public void rotate(int[] nums, int k) {
        k = k % nums.length;
        int count = 0;
        for (int start = 0; count < nums.length; start++) {
            int current = start;
            int prev = nums[start];
            do {
                int next = (current + k) % nums.length;
                int temp = nums[next];
                nums[next] = prev;
                prev = temp;
                current = next;
                count++;
            } while (start != current);
        }
    }
}
AIO Boot 中文版是一个方便的实用程序,使您可以通过几个简单的步骤使用 Grub2,Clover 或 Syslinux 创建可启动的 USB 或磁盘驱动器。此外,它允许您使用 PXE Server 和 iPXE Tiny 通过 LAN 引导,以及集成多个操作系统或支持程序。 开源免费 U 盘启动制作工具 AIO Boot 中文版开源免费 U 盘启动制作工具 AIO Boot 中文版 AIO Boot 特色: 对于旧版 MBR:Grub2,Grub4dos,Syslinux 和 Clover [旧版]。 对于 UEFI-GPT:Grub2,Clover [UEFI]和 rEFInd。 UEFI-GPT 和 MBR-Legacy。 支持以 GPT 样式引导至传统模式的硬盘。 具有 FAT32 / NTFS / exFAT 格式的 USB 和 HDD。 通过 Tiny PXE 服务器通过 LAN 引导。 一个或多个分区,AIO Boot 也支持隐藏分区。 支持 Grub2 的热键。在主菜单上,如果要引导至某个菜单,只需按其热键即可。例如,要重新启动计算机,只需按“ r”或“ o”即可关闭。 支持多种语言,包括越南语,英语,俄语和法语… 支持多组 Windows 设置文件- XP,2000、2003,Vista,Windows 7,Server 2008,Windows 8,Server 2012、32 位和 64 位版本。 BIOS 和(U)EFI 模式均受支持。 支持将 Windows 引导加载程序还原为传统模式和 UEFI 模式。 支持在 USB 3.0 端口上安装 Windows 7。这将帮助您解决错误:缺少所需的 CD/DVD 驱动器设备驱动程序。如果您有驱动程序软盘,CD,DVD 或 USB 闪存驱动器,请立即将其插入。 支持集成以下软件包:Windows,Linux,防病毒,DOS 程序,Android,备份和恢复,磁盘工具 支持编辑和删除 MENU(Windows 软件包除外)。 自动识别要集成的 DOS 程序。 通过 Shimx64.efi(需要测试仪)以 UEFI 模式绕过安全启动。如果您的计算机有“安全启动”部分,请将其打开,然后启动/EFI/Boot/shimx64.efi。 注意:AIO Boot 官方下载的密码为:aioboot,把 AIO Boot 文件夹内的所有文件都复制到 U 盘或者其它移动设备的根目录再运行,否则软件会提示您需要拷贝所有文件到根目录才可以使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值