力扣 初级算法 全套力扣精解

初级算法-数组-旋转数组

2021-07-28  本文已影响0人  coenen
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

摘一个示例做个说明.
示例 1:
输入: nums = [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]
提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
条件分析:
  1. 数组向右移动k位,k 是非负数 -> k是非负数,
  2. 0 <= k <= 105 -> k可能比较大
解决思路1:
  1. 根据分析1、2,k可能为0或者非常大.
以此说明,k可能是能循环数组好几圈的,所以需要对k取余.找出当前数据旋转后的位置,然后用新数组去承接转换后的数据.
解决思路2:

旋转数组,说明数据不变,只是改变位置.可以想象成一个圆环,进行转圈操作

如果数组是环形的话,我们直接旋转就行了.怎么实现数组环形类型的操作呢?因为数据是右移k位,所以实际移动的位置是k余.所以采用翻转,先翻转数组,在翻转旋转点前的和旋转点后的即可
解决思路3:

旋转数据,说明数据不变,只是改变位置.可以想象成数组后面的数据跑到前面了

找到k余,然后将后面的数据依次添加到起始位置,然后删除最后一个即可.
解决思路4:

根据思路2延伸,不用翻转数组.说明我们需要将起始数据挪到旋转后的位置,然后将该位置的数据挪到对应旋转后的位置.直到每个数据都被循环一次.即通过找k和数组长度的最大公约数,这样能确定循环几次才能完成.然后循环进行替换.直到遍历完成

首先找k和nums长度的最大公约数,获取需要循环的圈数.以保证每个数据都被循环一次.然后找到每次旋转后的位置,进行数据交换.再交换该位置的数据到对应相应的位置,直到结束

代码实现-Swift版本:

思路1代码:

func rotate(_ nums: inout [Int], _ k: Int) {
    if k % nums.count == 0 || nums.count == 1{
        return
    }
    var array = nums
    // 采用新数组去承接,然后求余位置,赋值到新数组
    for i in 0 ..< nums.count {
        array[(i + k) % nums.count] = nums[i]
    }
    nums = array
}
旋转数组 1 提交结果.jpg

思路2代码:

func rotate(_ nums: inout [Int], _ k: Int) {
    if k % nums.count == 0 || nums.count == 1{
        return
    }
    // 翻转整个数组
    nums.reverse()
    // 翻转旋转点前的,这样数据就双翻回正
    nums[0 ..< k % nums.count].reverse()
    // 翻转旋转点后到,数据双翻回正
    nums[k % nums.count ..< nums.count].reverse()
}
旋转数组 2 提交结果.jpg

思路3代码:

func rotate(_ nums: inout [Int], _ k: Int) {
    if k % nums.count == 0 || nums.count == 1{
        return
    }
    // 获取旋转点,然后数组最后的数据依次添加到前面即可
    for _ in 0 ..< k % nums.count {
        nums.insert(nums.last!, at: 0)
        nums.removeLast()
    }
    
    print(nums)
}
旋转数组 3 提交结果.jpg

思路4代码:

func rotate(_ nums: inout [Int], _ k: Int) {
    if k % nums.count == 0 || nums.count == 1{
        return
    }
    // 获取需要循环的次数
    let count = gcd(k, nums.count)
    
    for i in 0 ..< count {
        // 获取当前索引
        var current = i
        repeat{
            // 获取旋转后的位置
            let next = (current + k) % nums.count
            // 交换位置
            nums.swapAt(next, i)
            current = next
            
        }while i != current
    }
    
    print(nums)
}

// 最大公约数
func gcd(_ a :Int ,_ b :Int) -> Int {
    
    if a == b {//如果两个数相等.则直接返回
        return a
    }
    let big = max(a, b)
    let small = min(a, b)
    
    var divi = 0
    for i in 1..<small+1 {//选出两个数中较小的那个数将其分解因数
        if small % i  == 0{
            divi = small/i  //分解因子,因为是从1到small遍历.所以i 为较小的那个 ,divi为较大的那个
            if big%divi == 0{//判断divi能否被较大的那个数整除,如果能则divi是最大公约数
                return divi
            }
        }
    }
    return 1
}
旋转数组 4 提交结果.jpg

测试用例:

// 数组元素为最小一个,且k不为0
var nums0: [Int] = [1]
let k = 4
// 数组元素为最小一个,且k为0
var nums1: [Int] = [1]
let k = 0
// 数组元素为两个,且k不为0
var nums2: [Int] = [1,3]
let k = 24
// 数组元素为两个,且k为0
var nums3: [Int] = [1,3]
let k = 0
// 数组元素为多个,且k不为0
var nums4: [Int] = [1,2,3,4,5,6,7,8,9,10,11,12,13]
let k = 1024
// 数组元素为多个,且k为0
var nums5: [Int] = [1,2,3,4,5,6,7,8,9,10,11,12,13]
let k = 0

考察要点:

上一篇下一篇

猜你喜欢

热点阅读