LeetCode-旋转数组

旋转数组

将包含 n 个元素的数组向右旋转 步。

例如,如果  n = 7 ,  k = 3,给定数组  [1,2,3,4,5,6,7]  ,向右旋转后的结果为 [5,6,7,1,2,3,4]

注意:

尽可能找到更多的解决方案,这里最少有三种不同的方法解决这个问题。

感觉这个题目还是可以的。

首先要明白什么是一维数组右旋转几(当然肯定会有对应向左旋转几步喽,说不定也有向上或者向下旋转啦,害羞呵呵自己google了),你以为是像上学时候那样军训时原地旋转吗?其实我在很久以前刚触到的时候就是这么想的,我厉害吧。

码子可以说明一切:

[csharp]  view plain  copy
  1. [1,2,3,4,5,6,7]//初始数组  
  2. //向右旋转1步  
  3. [7,1,2,3,4,5,6]//向右旋转1步之后的数组  
  4. //再向右旋转1步,相对一最开始的数组就是向右旋转2步  
  5. [6,7,1,2,3,4,5]//向右旋转2步之后的数组  
  6. //把最原始的数组向左旋转1步  
  7. [2,3,4,5,6,7,1]//这就是向左旋转一步之后的数组  

应该看明白了吧,就拿向右旋转1步来说:首先向拿旋转的数组要有一个初始的数组,是相对于一个数组来说向左或者向右的;向右旋转1步就是说把初始数组的最高位上的数字放置在数组的最低位置,让后其他位置上的数字都把自身的位置提高1位。就这么简单什么向左旋转都一个道理(程序员都爱说一些高大上的名词。听起来很牛逼,说穿了哎。。。)

其实这就很容易想到第一种方案:

[csharp]  view plain  copy
  1. private static int[] Rotate2(int[] nums, int k)//k就是指旋转几步  
  2.         {  
  3.             int length = nums.Length;  
  4.             while (k > 0)//循环几次就看k是多少  
  5.             {  
  6.                 int t = 0;  
  7.                 t = nums[length - 1];//这是获取数组最高位置上的数字  
  8.                 for (int j = length - 2; j >= 0; j--)//从倒数第二个数字开始,倒叙循环。循环主要目的就是把其他数字位置都抬高一位  
  9.                 {  
  10.                     nums[j + 1] = nums[j];  
  11.                 }  
  12.                 nums[0] = t;//抬高玩其他的,就可以直接把最高位上的数字赋值到0号位上了  
  13.                 k--;//完成一个循环  
  14.             }  
  15.             return null;  
  16.         }  

其实上面每一次循环就三步:第一获取最高位的数字,第二把其他位上的数字都抬高一位,第三把最初获得最高位的数字赋值到最低位置上。

第二种方法就比较取巧了:

[csharp]  view plain  copy
  1. private static int[] Rotate1(int[] nums, int k)  
  2.        {  
  3.            int[] result = new int[nums.Length];//根据nums数组长度生成和其一样长度的数组  
  4.            for (int i = 0; i < nums.Length; i++)  
  5.            {  
  6.                result[(i + k) % nums.Length] = nums[i];//这里用到了取余,也是这个方法的核心  
  7.            }  
  8.            return result;  
  9.        }  

为什么用取余上个例子:

[csharp]  view plain  copy
  1.    ///3元素在数组中的2位置上,(2+3) % 7 = 5  
  2.    ///5元素在数组中的4位置上,(4+3) % 7 = 0  
  3.    ///6元素在数组中的5位置上,(5+3) % 7 = 1  
  4.    ///7元素在数组中的6位置上,(6+3) % 7 = 2  

长度为7的数组,向右旋转3步,数字的位置加3之后大于7的都要再从0号位置重新开始计算剩余的步子。这点特性就可以很好的用到求余。

应用一下:

[csharp]  view plain  copy
  1.        static int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7 };  
  2.        static void Main(string[] args)  
  3.        {  
  4.            //Rotate1(nums, 3);  
  5.            Rotate2(nums, 3);  
  6.        }  

这种类型的题目还有很多种解法(自己可以google喽),以后愿意的话我再补充。


用四种方法实现了将数组元素循环右移k个位置,相关的解释作为注释放在代码里面了。

[java]  view plain  copy
  1. package movearrayelement;  
  2.   
  3. import java.util.BitSet;  
  4.   
  5. public class MoveArrayElement {  
  6.     /** 
  7.      * 每次把数组中所有元素移动一个位置,移动k轮 
  8.      * @param array 
  9.      * @param k 
  10.      */  
  11.     public static void moveArrayElement(int[] array, int k) {  
  12.         int length = array.length;  
  13.         // 右移newk + n * length个位置,和右移newk个位置效果是一样的  
  14.         int newk = k % length;  
  15.         int temp = 0;  
  16.         for(int i = 0; i < newk; i++) {  
  17.             temp = array[length - 1];  
  18.             for(int j = length - 2; j >= 0; j--) {  
  19.                 array[j+1]=array[j];  
  20.             }  
  21.             array[0]=temp;  
  22.         }  
  23.     }  
  24.       
  25.     /** 
  26.      * 开辟一个新数组,把旧数组中的元素直接放在新数组中正确的位置 
  27.      * @param array 
  28.      * @param k 
  29.      * @return 
  30.      */  
  31.     public static int[] moveArrayElement1(int[] array, int k) {  
  32.         int length = array.length;  
  33.         // 右移newk + n * length个位置,和右移newk个位置效果是一样的  
  34.         int newk = k % length;  
  35.         int[] newArray = new int[length];  
  36.         // 重复length次把元素从旧位置移到新位置  
  37.         for(int i = 0; i < length; i++) {  
  38.             // 求出元素新的位置  
  39.             int newPosition = (i + newk) % length;  
  40.             newArray[newPosition] = array[i];  
  41.         }  
  42.         return newArray;  
  43.     }  
  44.       
  45.     /** 
  46.      * 1.把一个元素放在一个正确的位置,再把被占位置的元素放到它应该在的正确的位置,一直 
  47.      * 重复下去,直到数组的所有元素都放在了正确的位置; 
  48.      * 2.但是必须考虑环形的情况,比如十个元素的数组,右移5个位置,这时,位置0的元素应该放在位置5, 
  49.      * 位置5的元素应该放在位置0,这样,完全通过1的迭代就不能得到 正确的结果 
  50.      * @param array 
  51.      * @param k 
  52.      */  
  53.     public static void moveArrayElement2(int[] array, int k) {  
  54.         int length = array.length;  
  55.           
  56.         BitSet bitSet = new BitSet(length);  
  57.         boolean flag = false;  
  58.         // 保证最多只移动count=length次位置  
  59.         int count = 0;  
  60.         for(int j = 0; j < length; j++) {  
  61.             if (flag) {  
  62.                 break;  
  63.             }  
  64.             if (!bitSet.get(j)) {  
  65.                 // 右移newk + n * length个位置,和右移newk个位置效果是一样的  
  66.                 int newk = k % length;  
  67.                 // 旧位置  
  68.                 int oldPosition = j;  
  69.                 // 保存旧位置的值  
  70.                 int oldValue = array[oldPosition];  
  71.                 // 临时值  
  72.                 int temp = 0;  
  73.                 // 重复length次把元素从旧位置移到新位置  
  74.                 for(int i = 0; i < length; i++) {  
  75.                     // 求出元素新的位置  
  76.                     int newPosition = (oldPosition + newk) % length;  
  77.                     // 如果新位置已经放置了对得值,就不要往新位置再次放入值了  
  78.                     if (bitSet.get(newPosition)) {  
  79.                         break;  
  80.                     }  
  81.                     // 临时保存新位置(也就是新的旧位置)的值  
  82.                     temp = array[newPosition];  
  83.                     // 移动元素到新位置  
  84.                     array[newPosition] = oldValue;  
  85.                     // 又一个位置放置了正确的值  
  86.                     count++;  
  87.                     if (count == length) {  
  88.                         flag = true;  
  89.                         break;  
  90.                     }  
  91.                     // 新位置放置了正确的值  
  92.                     bitSet.set(newPosition);  
  93.                     // 永久保存旧位置的值  
  94.                     oldValue = temp;  
  95.                     // 新位置变为旧位置  
  96.                     oldPosition = newPosition;  
  97.                 }  
  98.             }  
  99.         }  
  100.         System.out.println(count);  
  101.     }  
  102.       
  103.     /** 
  104.      * 经典方法,三次倒置数组中对应位置的元素; 
  105.      * 简单说一下原理:数组元素右移k个位置的结果是,原来在 
  106.      * 后面的k个元素跑到了数组前面,原来在前面的length-k 
  107.      * 个元素,跑到了数组的后面,并且前后两部分元素各自的顺序和 
  108.      * 移动前一致,而倒置整个数组元素就是让后面k个元素跑到前面去, 
  109.      * 让前面length-k个元素跑到后面去,但是倒置之后前后两部分 
  110.      * 元素的顺序跟移动之前不一样了,倒置了,所以要把两部分的元素 
  111.      * 倒置回来 
  112.      * @param array 
  113.      * @param k 
  114.      */  
  115.     public static void moveArrayElement3(int[] array, int k) {  
  116.         // 倒置所有元素  
  117.         reverse(array);  
  118.         // 倒置前k个元素  
  119.         reverse(array, 0, k - 1);  
  120.         // 倒置后length - k个元素  
  121.         reverse(array, k, array.length - 1);  
  122.     }  
  123.       
  124.     /** 
  125.      * 倒置数组中begin和end之间的元素,包括begin和end 
  126.      * @param array 
  127.      * @param begin 
  128.      * @param end 
  129.      */  
  130.     private static void reverse(int[] array, int begin, int end) {  
  131.         int length = end - begin + 1;  
  132.         int half = length / 2;  
  133.         for(int i = 0; i < half; i++) {  
  134.             int temp = array[begin];  
  135.             array[begin] = array[end];  
  136.             array[end] = temp;  
  137.             begin++;  
  138.             end--;  
  139.         }  
  140.           
  141.     }  
  142.       
  143.     /** 
  144.      * 倒置数组中begin和end之间的元素,包括begin和end 
  145.      * @param array 
  146.      * @param begin 
  147.      * @param end 
  148.      */  
  149.     private static void reverse(int[] array) {  
  150.         reverse(array, 0, array.length - 1);  
  151.     }  

猜你喜欢

转载自blog.csdn.net/wangbo1998/article/details/79804909