当前位置:   article > 正文

用Java将数组中的元素向右移动_给定一个数组,将数组中的元素向右

给定一个数组,将数组中的元素向右

leetcode 189  题目来自:这里

给定一个数组,将数组中的元素向右移动 个位置,其中 是非负数。

例子1:

  1. 输入: [1,2,3,4,5,6,7] 和 k = 3
  2. 输出: [5,6,7,1,2,3,4]
  3. 解释:
  4. 向右旋转 1 步: [7,1,2,3,4,5,6]
  5. 向右旋转 2 步: [6,7,1,2,3,4,5]
  6. 向右旋转 3 步: [5,6,7,1,2,3,4]

例子2:

  1. 输入: [-1,-100,3,99] 和 k = 2
  2. 输出: [3,99,-1,-100]
  3. 解释:
  4. 向右旋转 1 步: [99,-1,-100,3]
  5. 向右旋转 2 步: [3,99,-1,-100]

解答:

首先分析题目:例子中的元素和leetcode中的程序都指定元素为整型,况且其他数据类型也是一样的,所以用整型数组作答。k为非负数,就有可能是0,也可能比数组的长度大,也就是说k=203131234,而数组只有3个元素,这里就需要取模运算;数组的长度没有限制,就说明可能是0个元素,也可能是10亿个元素(占满4G内存)。首先看能比较直观想到的算法:

解法1:使用另外一个数组,直接把元素移动到指定位置

  1. //时间复杂度O(n) n为数组长度
  2. //空间复杂度O(n) n为数组长度
  3. public void rotate(int[] nums, int k) {
  4. if(k==0){return;}
  5. int n = k%nums.length;
  6. int[] tmp = new int[n];
  7. for(int i=0;i<n;i++){
  8. tmp[i]=nums[nums.length-n+i];
  9. }
  10. for(int i=nums.length-1;i>=n;i--){
  11. nums[i]=nums[i-n];
  12. }
  13. for(int i = 0;i<n;i++){
  14. nums[i]=tmp[i];
  15. }
  16. }

分析:首先排除特殊情况k=0,并将k和数组长度取模运算,用new重新创建一个数组,这里可以用.clone()方法简化,下面的循环移动数组元素的逻辑,只把移动元素时会被覆盖的元素备份出来,然后在移动完元素后,把备份的元素移动回去。拷贝数组元素的操作可以从for循环简化为使用System.arraycopy 这样更快。

解法1简化:

  1. //时间复杂度O(n) n为数组长度
  2. //空间复杂度O(n) n为数组长度
  3. public void rotate(int[] nums, int k) {
  4. k=k%nums.length;
  5. int[] tmp=nums.clone();
  6. System.arraycopy(tmp,tmp.length-k,nums,0,k);
  7. System.arraycopy(tmp,0,nums,k,tmp.length-k);
  8. }

解法2:每次把所有元素向右移动1个位置,移动k轮,使用额外的k个变量的空间,这种叫冒泡旋转

  1. //时间复杂度O(k*n) k为右移的实际次数(k%n) n为数组长度
  2. //空间复杂度O(1)
  3. public void rotate(int[] nums, int k) {
  4. int len = nums.length;
  5. if (k % len == 0) return;
  6. k = k % len;
  7. for (int i = 0; i < k; i++) {
  8. int temp = nums[len-1];
  9. System.arraycopy(nums, 0, nums, 1, len-1);
  10. nums[0] = temp;
  11. }
  12. }

 

解法3:不使用额外空间,三次反转代替移动

  1. //时间复杂度O(n)
  2. //空间复杂度O(1)
  3. public void rotate(int[] nums, int k) {
  4. k %= nums.length;
  5. reverse(nums, 0, nums.length - 1);
  6. reverse(nums, 0, k - 1);
  7. reverse(nums, k, nums.length - 1);
  8. }
  9. //反转数组
  10. public void reverse(int[] nums, int left, int right){
  11. while (left < right) {
  12. nums[left] = nums[right] ^ nums[left];
  13. nums[right] = nums[right] ^ nums[left];
  14. nums[left] = nums[right] ^ nums[left];
  15. left++;
  16. right--;
  17. }
  18. }

分析:

reverse方法是把数组中从left到right位置的元素颠倒过来,那为什么颠倒过来就相当于右移呢,直观解释,请看下面一幅图:

reverse方法中之所以用异或,这只是三种交换两个数的方式中,最快的一种。

reverse算法是运行时间最快(不使用额外空间),运行效率更稳定,代码更简洁的一种方式。

其实还有一种杂凑算法(juggling algorithm),其中用到了最小公约数,因为过于复杂,不在这里列出,有兴趣的同学可以看下面的参考2。

知识点:

1.System.arraycopy()方法复制数组元素的速度比用for循环快,见《Java中数组复制的几种方法》

2.数组名字.clone()可以克隆数组,不再需要用new

3.交换两个整数,可以不用临时变量,用异或效率最高

参考:

1.《Program for array rotation》 https://www.geeksforgeeks.org/array-rotation/

2.编程珠玑笔记:数组循环左移https://www.cnblogs.com/longcpp/p/4029664.html

3.《Java 交换两个数的三种方法》https://blog.csdn.net/yancewong/article/details/80550720

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/567394
推荐阅读
相关标签
  

闽ICP备14008679号