[leetcode]189. 旋转数组

题目

传送门

简要描述

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

实例

实例1

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

实例2

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

解题

思路1

按照实例中给出的说明,每次向右移动一位,然后总共移动k次.最终提交结果超时。

代码

// 第一种:每次向右旋转1次,总共旋转k次
rotate_right(nums, k);  

void rotate_right(vector<int> & nums, int k) {
    int temp;
    int size = nums.size();
    for (int i = 0; i < k; i++) {
        temp = nums[size - 1];
        for (int j = size - 1; j > 0; j--) {
            nums[j] = nums[j - 1];
        }
        nums[0] = temp;
    }
}

思路2

在思路1的基础上,考虑到当向右移动位数大于一半时,改为向左移动总长度减去向右移动次数。最终结果超时。

代码

// 第二种:根据总长度和旋转值,如果旋转值大于一半的长度,将向右旋转转为向左旋转
k = k % nums.size();
if (k <= (nums.size() >> 1)) {
    rotate_right(nums, k);
} else {
    rotate_left(nums, nums.size() - k);
}

void rotate_right(vector<int> & nums, int k) {
    int temp;
    int size = nums.size();
    for (int i = 0; i < k; i++) {
        temp = nums[size - 1];
        for (int j = size - 1; j > 0; j--) {
            nums[j] = nums[j - 1];
        }
        nums[0] = temp;
    }
}

void rotate_left(vector<int> & nums, int k) {
    int temp;
    int size = nums.size();
    for (int i = 0; i < k; i++) {
        temp = nums[0];
        for (int j = 0; j < size - 1; j++) {
            nums[j] = nums[j + 1];
        }
        nums[size - 1] = temp;
    }
}

思路3

题目要求是向右移动 k 个位置,那么就将每个元素向右移动k个位置,并且题目给出了要求使用了O(1)的空间,就可以将第1个元素移动到第k个位置后,记录下第k个位置的元素的值,然后将第k个位置的值,继续向右移动k个位置。
这样存在的问题是,如果是4个元素,向右移动两个位置,将会出现0和2的位置的元素在移动,1和3的位置的元素不变。这个时候,在0,2位置移动完之后,就需要手动移动到1位置再进行一次移动。
需要手动调整位置的次数与最大公因数有关,每一轮移动的元素数量为数组大小除以最大公因数的值。

代码

// 第三种:跳跃式旋转,每个元素直接向后移动k位,此时需要考虑长度与移动位置的最大公因数
// 如果为1的话,表示不会出现重复,一轮便可以
// 如果不为1的话,表示会出现重复,需要最大公因数轮次才可以
// 举例 5个元素,右移3位 
// 1,2,3,4,5 -> 1,2,3,1,5 (4) -> 1,4,3,1,5 (2) -> 1,4,3,1,2 (5) -> 1,4,5,1,2 (3) -> 3,4,5,1,2 (1)
// 这样一轮便可以搞定,如果是4个元素,右移2位
// 1,2,3,4 -> 1,2,1,4 (3) -> 3,2,1,4 (1) 一轮之后,需要调整起始点为第二个元素
// 3,2,1,4 -> 3,2,1,2 (4) -> 3,4,1,2 (2) 两轮之后,就可以结束
rotate_jump(nums, k % nums.size());

void rotate_jump(vector<int> & nums, int k) {
    // temp1 记录要放进去的元素
    // temp2 记录需要暂时取出来的元素
    int temp1, temp2;
    int size = nums.size();
    // 计算最大公因数,也就是循环轮次
    int gcd_val = gcd(size, k);
    // 计算每轮循环需要进行多少次跳跃
    int times = size / gcd_val;

    int pos, next_pos;
    for (int i = 0; i < gcd_val; i++) {
        pos = i;
        temp1 = nums[pos];
        for (int j = 0; j < times; j++) {
            next_pos = (pos + k) % size;
            temp2 = nums[next_pos];
            nums[next_pos] = temp1;
            pos = next_pos;
            temp1= temp2;
        }
    }
}
posted @ 2021-01-08 11:11  by-sknight  阅读(59)  评论(0编辑  收藏  举报