leetcode 189. 旋转数组

匿名 (未验证) 提交于 2019-12-02 23:36:01

leetcode 189. 旋转数组

难度:简单

题库地址:https://leetcode-cn.com/problems/rotate-array/

1. 题目描述

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

2. 解题思路

英文版的有答案:https://leetcode.com/problems/rotate-array/solution/

这里记录一下第4种方法的理解:Approach #4 Using Reverse [Accepted]

假设对长度为 n 的数组,进行 k 次旋转。

首先要找到最终有效的次数,因为旋转的循环的,当对数组进行 n 次旋转时,相当于没有对数组进行操作,同时该方法必须先找到最终有效次数,否则没法对数组进行镜像操作。

为什么在不同位置采用三次镜像操作就能实现数组的 k 次旋转,连同答案和自己的理解整理为如下:

  1. 先计算 kk 的有效次数,因为旋转是循环进行的,像自行车链条一样,当元素旋转一整圈即:k=nk = n 时,所有元素回到了原来的位置,所以 kk 次旋转的有效次数为 k%nk \% n

  2. 我们将数组以 nn 为边界拆分成两部分看待,前 nkn-k 个元素部分:S1={t1,t2,...,tnk}S_1 = \{t_1,t_2,...,t_{n-k}\} 和 后 nn 个元素部分 S2={tnk+1,tnk+2,...,tn}S_2 = \{t_{n-k+1},t_{n-k+2},...,t_n\}为什么以 kk 为分界点呢?因为对数组进行 kk 次旋转后,S1S_1S2S_2 将互换位置
    如数组:{1,2,3,4,5,6}\{1,2,3,4,5,6\},长度 n=6n = 6,进行 k=2k=2 次旋转,对应 S1={1,2,3,4}S_1 = \{1, 2, 3, 4\}S2={5,6}S_2 = \{5,6\},旋转结束后得到数组: S2={5,6}S_2 = \{5, 6\}S1={1,2,3,4}S_1 = \{1, 2, 3, 4\}

  3. 为了实现将 S1S_1S2S_2 位置互换,这里采用将整个数组镜像的策略。
    原元素:{1,2,3,4,5,6}\{1,2,3,4,5,6\}
    镜像后:{6,5,4,3,2,1}\{6, 5, 4, 3, 2, 1\}

  4. 对数组进行镜像操作后,虽然 S1S_1S2S_2 位置互换,但是其内部元素也被镜像了,为了将 S1S_1S2S_2 内部元素的顺序恢复,对 S1S_1S2S_2 再分别进行一次镜像。

    举例如下原数组:{1,2,3,4,5,6}\{1,2,3,4,5,6\}
    镜像前: S1={1,2,3,4}S_1 = \{1, 2, 3, 4\}S2={5,6}S_2 = \{5,6\}
    镜像后:S2={6,5}S_2 = \{6,5\}S1={4,3,2,1}S_1 = \{4, 3, 2, 1\}
    S1S_1S2S_2 分别进行一次镜像后得到的最终结果:S2={5,6}S_2 = \{5, 6\}S1={1,2,3,4}S_1 = \{1, 2, 3, 4\}
    最终答案:{5,6,1,2,3,4}\{5, 6, 1, 2, 3, 4\}

    算法思想再精简一点如下:

    1. 对数组以 kk 划分成两部分,分为前 nkn-k 个元素部分,和后 kk 个部分,这样划分的目的是旋转 kk 次后这两部分正好位置互换。
    2. 为了实现这种位置互换需要将数组镜像
    3. 镜像虽然将两部分数据整体的位置转换了过来,但是两部分数组内部数据顺序错误,需要对两部分数组分别镜像

Java实现:

class Solution {     public void rotate(int[] nums, int k) {         if (nums == null || nums.length < 2) {             return;         }         // 计算有效旋转次数         k %= nums.length;         // 对整个数组镜像,使 S2 和 S1 位置互换         reverse(nums, 0, nums.length - 1);         // 对 S2 镜像处理,使其内部元素顺序恢复         reverse(nums, 0, k - 1);         // 对 S1 镜像处理,使其内部元素顺序恢复         reverse(nums, k, nums.length - 1);     }      private void reverse(int[] nums, int start, int end) {         while (start < end) {             int temp = nums[end];             nums[end] = nums[start];             nums[start] = temp;             start++;             end--;         }     } } 
文章来源: https://blog.csdn.net/wangxiaoan1234/article/details/90740819
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!