旋转数组
将包含 n 个元素的数组向右旋转 k 步。
例如,如果 n = 7 , k = 3,给定数组 [1,2,3,4,5,6,7]
,向右旋转后的结果为 [5,6,7,1,2,3,4]
。
注意:
尽可能找到更多的解决方案,这里最少有三种不同的方法解决这个问题。
感觉这个题目还是可以的。
首先要明白什么是一维数组向右旋转几步(当然肯定会有对应向左旋转几步喽,说不定也有向上或者向下旋转啦,呵呵自己google了),你以为是像上学时候那样军训时原地旋转吗?其实我在很久以前刚触到的时候就是这么想的,我厉害吧。
码子可以说明一切:
- [1,2,3,4,5,6,7]//初始数组
- //向右旋转1步
- [7,1,2,3,4,5,6]//向右旋转1步之后的数组
- //再向右旋转1步,相对一最开始的数组就是向右旋转2步
- [6,7,1,2,3,4,5]//向右旋转2步之后的数组
- //把最原始的数组向左旋转1步
- [2,3,4,5,6,7,1]//这就是向左旋转一步之后的数组
应该看明白了吧,就拿向右旋转1步来说:首先向拿旋转的数组要有一个初始的数组,是相对于一个数组来说向左或者向右的;向右旋转1步就是说把初始数组的最高位上的数字放置在数组的最低位置,让后其他位置上的数字都把自身的位置提高1位。就这么简单什么向左旋转都一个道理(程序员都爱说一些高大上的名词。听起来很牛逼,说穿了哎。。。)
其实这就很容易想到第一种方案:
- private static int[] Rotate2(int[] nums, int k)//k就是指旋转几步
- {
- int length = nums.Length;
- while (k > 0)//循环几次就看k是多少
- {
- int t = 0;
- t = nums[length - 1];//这是获取数组最高位置上的数字
- for (int j = length - 2; j >= 0; j--)//从倒数第二个数字开始,倒叙循环。循环主要目的就是把其他数字位置都抬高一位
- {
- nums[j + 1] = nums[j];
- }
- nums[0] = t;//抬高玩其他的,就可以直接把最高位上的数字赋值到0号位上了
- k--;//完成一个循环
- }
- return null;
- }
其实上面每一次循环就三步:第一获取最高位的数字,第二把其他位上的数字都抬高一位,第三把最初获得最高位的数字赋值到最低位置上。
第二种方法就比较取巧了:
- private static int[] Rotate1(int[] nums, int k)
- {
- int[] result = new int[nums.Length];//根据nums数组长度生成和其一样长度的数组
- for (int i = 0; i < nums.Length; i++)
- {
- result[(i + k) % nums.Length] = nums[i];//这里用到了取余,也是这个方法的核心
- }
- return result;
- }
为什么用取余上个例子:
- ///3元素在数组中的2位置上,(2+3) % 7 = 5
- ///5元素在数组中的4位置上,(4+3) % 7 = 0
- ///6元素在数组中的5位置上,(5+3) % 7 = 1
- ///7元素在数组中的6位置上,(6+3) % 7 = 2
长度为7的数组,向右旋转3步,数字的位置加3之后大于7的都要再从0号位置重新开始计算剩余的步子。这点特性就可以很好的用到求余。
应用一下:
- static int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7 };
- static void Main(string[] args)
- {
- //Rotate1(nums, 3);
- Rotate2(nums, 3);
- }
这种类型的题目还有很多种解法(自己可以google喽),以后愿意的话我再补充。
用四种方法实现了将数组元素循环右移k个位置,相关的解释作为注释放在代码里面了。
- package movearrayelement;
- import java.util.BitSet;
- public class MoveArrayElement {
- /**
- * 每次把数组中所有元素移动一个位置,移动k轮
- * @param array
- * @param k
- */
- public static void moveArrayElement(int[] array, int k) {
- int length = array.length;
- // 右移newk + n * length个位置,和右移newk个位置效果是一样的
- int newk = k % length;
- int temp = 0;
- for(int i = 0; i < newk; i++) {
- temp = array[length - 1];
- for(int j = length - 2; j >= 0; j--) {
- array[j+1]=array[j];
- }
- array[0]=temp;
- }
- }
- /**
- * 开辟一个新数组,把旧数组中的元素直接放在新数组中正确的位置
- * @param array
- * @param k
- * @return
- */
- public static int[] moveArrayElement1(int[] array, int k) {
- int length = array.length;
- // 右移newk + n * length个位置,和右移newk个位置效果是一样的
- int newk = k % length;
- int[] newArray = new int[length];
- // 重复length次把元素从旧位置移到新位置
- for(int i = 0; i < length; i++) {
- // 求出元素新的位置
- int newPosition = (i + newk) % length;
- newArray[newPosition] = array[i];
- }
- return newArray;
- }
- /**
- * 1.把一个元素放在一个正确的位置,再把被占位置的元素放到它应该在的正确的位置,一直
- * 重复下去,直到数组的所有元素都放在了正确的位置;
- * 2.但是必须考虑环形的情况,比如十个元素的数组,右移5个位置,这时,位置0的元素应该放在位置5,
- * 位置5的元素应该放在位置0,这样,完全通过1的迭代就不能得到 正确的结果
- * @param array
- * @param k
- */
- public static void moveArrayElement2(int[] array, int k) {
- int length = array.length;
- BitSet bitSet = new BitSet(length);
- boolean flag = false;
- // 保证最多只移动count=length次位置
- int count = 0;
- for(int j = 0; j < length; j++) {
- if (flag) {
- break;
- }
- if (!bitSet.get(j)) {
- // 右移newk + n * length个位置,和右移newk个位置效果是一样的
- int newk = k % length;
- // 旧位置
- int oldPosition = j;
- // 保存旧位置的值
- int oldValue = array[oldPosition];
- // 临时值
- int temp = 0;
- // 重复length次把元素从旧位置移到新位置
- for(int i = 0; i < length; i++) {
- // 求出元素新的位置
- int newPosition = (oldPosition + newk) % length;
- // 如果新位置已经放置了对得值,就不要往新位置再次放入值了
- if (bitSet.get(newPosition)) {
- break;
- }
- // 临时保存新位置(也就是新的旧位置)的值
- temp = array[newPosition];
- // 移动元素到新位置
- array[newPosition] = oldValue;
- // 又一个位置放置了正确的值
- count++;
- if (count == length) {
- flag = true;
- break;
- }
- // 新位置放置了正确的值
- bitSet.set(newPosition);
- // 永久保存旧位置的值
- oldValue = temp;
- // 新位置变为旧位置
- oldPosition = newPosition;
- }
- }
- }
- System.out.println(count);
- }
- /**
- * 经典方法,三次倒置数组中对应位置的元素;
- * 简单说一下原理:数组元素右移k个位置的结果是,原来在
- * 后面的k个元素跑到了数组前面,原来在前面的length-k
- * 个元素,跑到了数组的后面,并且前后两部分元素各自的顺序和
- * 移动前一致,而倒置整个数组元素就是让后面k个元素跑到前面去,
- * 让前面length-k个元素跑到后面去,但是倒置之后前后两部分
- * 元素的顺序跟移动之前不一样了,倒置了,所以要把两部分的元素
- * 倒置回来
- * @param array
- * @param k
- */
- public static void moveArrayElement3(int[] array, int k) {
- // 倒置所有元素
- reverse(array);
- // 倒置前k个元素
- reverse(array, 0, k - 1);
- // 倒置后length - k个元素
- reverse(array, k, array.length - 1);
- }
- /**
- * 倒置数组中begin和end之间的元素,包括begin和end
- * @param array
- * @param begin
- * @param end
- */
- private static void reverse(int[] array, int begin, int end) {
- int length = end - begin + 1;
- int half = length / 2;
- for(int i = 0; i < half; i++) {
- int temp = array[begin];
- array[begin] = array[end];
- array[end] = temp;
- begin++;
- end--;
- }
- }
- /**
- * 倒置数组中begin和end之间的元素,包括begin和end
- * @param array
- * @param begin
- * @param end
- */
- private static void reverse(int[] array) {
- reverse(array, 0, array.length - 1);
- }
- }