RotateArray [source code]


public class RotateArray {
static
/******************************************************************************/
public class Solution {
    public void rotate(int[] nums, int k) {
        rotate(nums, 0, nums.length - 1, k);
    }

    public void rotate(int[] nums, int lo, int hi, int k) {
        int n = hi - lo + 1;
        if (k >= n) k = k % n;
        if (k <= 0) return;
        if (k == n - k) reverse(nums, lo, hi, k);
        else if (n - k > k) {
            reverse(nums, lo, hi, k);
            rotate(nums, lo + k, hi, k);
        } else {
            reverse(nums, lo, hi, n - k);
            rotate(nums, lo, hi - (n - k), k - (n - k));
        }
    }

    public void reverse(int[] nums, int lo, int hi, int k) {
        int tmp;
        int n = hi - lo + 1;
        if (k > n / 2) return;
        for (int i = 0; i < k; i++) {
            tmp = nums[hi - k + 1 + i];
            nums[hi - k + 1 + i] = nums[lo + i];
            nums[lo + i] = tmp;
        }
    }
}
/******************************************************************************/

    public static void main(String[] args) {
        RotateArray.Solution tester = new RotateArray.Solution();
        int[][] inputs = {
            {1,2,3,4,5,6,7}, {3}, {5,6,7,1,2,3,4},
            {1,2,3,4,5,6,7,8,9,10}, {3}, {8,9,10,1,2,3,4,5,6,7},
            {1}, {0}, {1},
            {1,2}, {3}, {2, 1},
        };
        for (int i = 0; i < inputs.length / 3; i++) {
            int[] nums = inputs[3 * i];
            int k = inputs[1 + 3 * i][0];
            String expected = Printer.array(inputs[2 + 3 * i]);
            System.out.println(Printer.seperator());
            Printer.openColor("magenta");
            System.out.print(Printer.array(nums));
            Printer.closeColor();
            tester.rotate(nums, k);
            String output = Printer.array(nums);
            System.out.print(" -> " + output);
            Printer.openColor(output.equals(expected) ? "green" : "red");
            System.out.println(", expected: " + expected);
            Printer.closeColor();
        }
    }
}

首先仔细读题: 这里的n和k都是length, 而不是index;

刚开始看错题目了, 直接写了这个东西:

public class Solution {  
    public void rotate(int[] nums, int k) {  
        int n = nums.length;  
        int tmp;  
        for (int i = 0; i < k; i++) {  
            tmp = nums[n - 1 - i];  
            nums[n - 1 - i] = nums[i];  
            nums[i] = tmp;  
        }  
    }  
}

recursion的算法在debug的时候一个常用技巧就是在每一个call进入的时候把参数print出来;

看起来很简单的一个题目, 结果出乎意料的难写, 花了很长时间才写完. 最后的想法是利用recursion来做, 而且写了两个helper; 中间各种变量的更新的细节处理也并非那么简单, 必须要例子与visual结合来帮助分析.

最后的速度是1ms (13%), 其实不差了, 不过老题目, 最优解太多了;

另外这一题其实我一开始看错题目了, 以为就是从右边量k个, 然后k和n-k对调. 后来处理k >= n的情况的时候才发现这个题目的本意就是从0开始, 把i跟n-i对调, 进行k次. 所以如果k比n大, 直接mod一下就行了; (每n个step, 这个array其实是最后被还原了);

这个速度好像是InPlace的最优解了. 0ms那个实际上就是copy来做的;


另外这一题的bruteforce的做法其实并不难, 直接就是按照题目对于rotate的定义来做就行了. 不过这个做法实际上最后是超时了的, 这个肯定也是OJ故意的;

这个是暴力解的代码:

public class Solution {  
    public void rotate(int[] nums, int k) {  
        int temp, previous;  
        for (int i = 0; i < k; i++) {  
            previous = nums[nums.length - 1];  
            for (int j = 0; j < nums.length; j++) {  
                temp = nums[j];  
                nums[j] = previous;  
                previous = temp;  
            }  
        }  
    }  
}

看了这个暴力解才感觉, 我之前对这个rotate的理解其实还是错的, 并不是头和尾, 然后头+1和尾巴-1这样的swap,而是一个类似于cyclical shift这样的rotate;
这个也符合了为什么我的这种先分块再交换的理解是对的原因;
这个理解的改变实际上对于代码的实现并没有影响;

至于非InPlace的做法就不多说了, 上面也讲了, 很好写, 完全没有难度, 直接几个copy就行了;


这个是editorial4:

Original List                   : 1 2 3 4 5 6 7  
After reversing all numbers     : 7 6 5 4 3 2 1  
After reversing first k numbers : 5 6 7 4 3 2 1  
After revering last n-k numbers : 5 6 7 1 2 3 4 --> Result
public class Solution {  
    public void rotate(int[] nums, int k) {  
        k %= nums.length;  
        reverse(nums, 0, nums.length - 1);  
        reverse(nums, 0, k - 1);  
        reverse(nums, k, nums.length - 1);  
    }  
    public void reverse(int[] nums, int start, int end) {  
        while (start < end) {  
            int temp = nums[start];  
            nums[start] = nums[end];  
            nums[end] = temp;  
            start++;  
            end--;  
        }  
    }  
}

这个方法应该是这个问题最简练的一个方法了. 出发点其实跟我的算法是类似的, 不是用shift来做, 而是只考虑最终的结果, 其实就是长度为n - k跟长度为k的两个subarray对掉了位置(两个subarray内部的顺序不变);

这里的做法其中有一个就是, 当你把整个长度为n的array都reverse一遍的时候, 正好本来在前n-k跟后k个位置的元素也全都正好掉换了位置. 然后两个subarray内部的顺序还原一下就行了;

不得不说这个算法还是比较讨巧的, 短时间内比较难想出来, 我感觉还是我自己的算法比较具有普适性: 我的算法其实就是一个非常典型的recursion算法, divide and conquer.

底下有人说这个算法其实是在programming pearls里面的; 不知道真的假的, 如果是这样, 那我也释然了;


我这个算法, discussion里面果然也有人想出来了, 好写的是一个iterative的版本:

class Solution   
{  
public:  
    void rotate(int nums[], int n, int k)   
    {  
        if ((n == 0) || (k <= 0) || (k%n == 0))  
        {  
            return;  
        }  

        k = k%n;  
        // Rotation to the right by k steps is equivalent to swapping   
        // the two subarrays nums[0,...,n - k - 1] and nums[n - k,...,n - 1].  
        int start = 0;  
        int tmp = 0;  
        while (k > 0)  
        {  
            if (n - k >= k)  
            {  
                // The left subarray with size n - k is longer than   
                // the right subarray with size k. Exchange   
                // nums[n - 2*k,...,n - k - 1] with nums[n - k,...,n - 1].  
                for (int i = 0; i < k; i++)  
                {  
                    tmp = nums[start + n - 2*k + i];  
                    nums[start + n - 2*k + i] = nums[start + n - k + i];  
                    nums[start + n - k + i] = tmp;  
                }  

                // nums[n - 2*k,...,n - k - 1] are in their correct positions now.  
                // Need to rotate the elements of nums[0,...,n - k - 1] to the right   
                // by k%n steps.  
                n = n - k;  
                k = k%n;  
            }  
            else  
            {  
                // The left subarray with size n - k is shorter than   
                // the right subarray with size k. Exchange   
                // nums[0,...,n - k - 1] with nums[n - k,...,2*(n - k) - 1].  
                for (int i = 0; i < n - k; i++)  
                {  
                    tmp = nums[start + i];  
                    nums[start + i] = nums[start + n - k + i];  
                    nums[start + n - k + i] = tmp;  
                }  

                // nums[n - k,...,2*(n - k) - 1] are in their correct positions now.  
                // Need to rotate the elements of nums[n - k,...,n - 1] to the right   
                // by k - (n - k) steps.  
                tmp = n - k;  
                n = k;  
                k -= tmp;  
                start += tmp;  
            }  
        }  
    }  
};

当然, 我用的是recursion, 反映subproblem的变化利用的是参数, 他既然写的是iterative, 利用的就是local var的更新来完成, 这个也是提过很多次了;

关于recursion跟iteration之间的转换, 感觉以后可能会多次碰到. 在这个问题上, Binary Search就是一个非常典型的例子, 分析Binary Search就能得到很多启发. 一般从recursion转换过来的iteration, 都是有两个一头一尾的var来保存iterate的范围, 然后每一个iteration, 这两个范围var都被更新, 对应了每一个recursive call的时候的argument的更新;

好像这个答案才是跟我的做法比较对应的一个做法:

class Solution   
{  
public:  
    void rotate(int nums[], int n, int k)   
    {  
        for (; k = k%n; n -= k, nums += k)  
        {  
            // Swap the last k elements with the first k elements.   
            // The last k elements will be in the correct positions  
            // but we need to rotate the remaining (n - k) elements   
            // to the right by k steps.  
            for (int i = 0; i < k; i++)  
            {  
                swap(nums[i], nums[n - k + i]);  
            }  
        }  
    }  
};

不过他这个写的比较奇怪. 但是思路还是对的好像;


Problem Description

Rotate an array of n elements to the right by k steps.

For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].

Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.

[show hint]

Related problem: Reverse Words in a String II

Credits:
Special thanks to @Freezen for adding this problem and creating all test cases.

Difficulty:Easy
Total Accepted:130.5K
Total Submissions:534K
Contributor: LeetCode
Companies
microsoft bloomberg
Related Topics
array
Similar Questions
Rotate List Reverse Words in a String II

Hint:
Could you do it in-place with O(1) extra space?
Related problem: Reverse Words in a String II

results matching ""

    No results matching ""