leetcode189 旋转数组

一个有点困难的题目,因为在正确的道路上走了岔路,所以没写出来。

先是最简单的解法,重新创建一个同样大的数组用来存放经过移位的数据,然后再复制过去,简单易懂。贴代码

class Solution {
public:
    void rotate(vector<int>& nums, int k) 
    {
        int n = nums.size();
        vector<int> newNums(n);
        for(int i = 0 ; i < n ;i ++)
        {
            newNums[(i+k)%n] = nums[i];
        }
        nums.assign(newNums.begin(),newNums.end());
    }
};

自己当时想到的办法是环形替换,什么意思,就是指数组整体往右移位,对于下标为i的数组元素来说就是移位到i+k % n的位置上,基于这个原理,可以定义一个额外的temp变量,用于存储每一次替换中被覆盖的变量,经过n次替换之后,就完成了整体的替换。我当时想的太过简单,单纯认为经过一次遍历回到原点后就能够解决问题,后来发现,如果k与n之间存在公约数,单次循环并不能解决问题,于是年轻的我就陷入了疑惑。假设总共循环的次数为a,一次循环代表从某一点出发,不断替换之后回到原点,这样一次循环转了a次圈之后,经过的总路程为a*n,而当完成这a次循环中,每替换一次的跳跃路程为k,假设跳了b次,也就是一次循环遍历了b个元素,则得到一个等式a*n = b*k,由于需要循环的次数越小越好,则an是n与k的最小公倍数,所以b = lcm(n,k)/k,则总共需要循环的次数就是总的元素个数除以单次循环中遍历的元素个数,即nk/lcm(n,k) = gcd(n,k),经过这个推导之后,问题实际上就很好解决了。下面贴代码

class Solution {
public:
    void rotate(vector<int>& nums, int k) 
    {
        int n = nums.size();
        int t = k%n;
        int count = gcd(n,t);
        int start = 0;
        for(start = 0 ; start < count ; start ++)
        {
            int temp = nums[start];
            int s = start;
            do
            {
                s = (s+t)%n;
                int temp1 = nums[s];
                nums[s] = temp;
                temp = temp1;
            }while(s!=start);
        }
    }
};

总共循环count次,每次循环以循环到初始位置为结束。简洁明了,该题是第一次需要经过数学推导完成的题目,很有历史意义。

接下来是翻转的做法,有脑筋急转弯的感觉,非常巧妙。

class Solution {
public:
    void reverse(vector<int>& nums,int begin,int end)
    {
        while(begin<end)
        {
            swap(nums[begin],nums[end]);
            begin++;
            end--;
        }
    }
    void rotate(vector<int>& nums, int k) 
    {
        int n = nums.size();
        k = k%n;
        reverse(nums,0,n-1);
        reverse(nums,0,k-1);
        reverse(nums,k,n-1);
    }
};

思路来源于对右移后数组的观察,整体上为数组翻转,前k个元素翻转,剩下的元素翻转,就能解决问题,简单。

posted @ 2021-03-04 17:04  zhaohhhh  阅读(55)  评论(0)    收藏  举报