zoukankan      html  css  js  c++  java
  • [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;
            }
        }
    }
    
  • 相关阅读:
    setTimeout,clearTimeout,setInterval,clearInteral详解
    关于JS读取DOM对象(标签)的自定义属性
    jquery.form.js官方插件介绍Form插件,支持Ajax,支持Ajax文件上传
    按回车键切换input鼠标光标
    电压跟随器的作用,以及其中两个电阻的作用?
    阅读RB521S30肖特基二极管的datasheet
    配置linux内核,解决lcd logo和十分钟息屏!
    配置linux,关闭LCD的console控制台输出。
    记录一次编译linux内核或者说编译dtbs的问题!!
    copy_from_user的详细用法!
  • 原文地址:https://www.cnblogs.com/by-sknight/p/14250472.html
Copyright © 2011-2022 走看看