当前位置: 首页 > article >正文

【leetcode】关于循环数组的深入分析

原题:https://leetcode.cn/problems/rotate-array/description/

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 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]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]

解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

开始的思路是通过mod运算完成循环的效果,从第一个位置元素起逐渐以步长k往下一个元素循环递推赋值,直到回到第一个位置元素。

from typing import List


class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        arr_len = len(nums)
        curr_idx, curr_num = 0, nums[0]
        while True:
            next_idx = (curr_idx + k) % arr_len
            if next_idx == 0:
                nums[next_idx] = curr_num
                break
            nums[next_idx], curr_num = curr_num, nums[next_idx]
            curr_idx = next_idx
        print(nums)


if __name__ == '__main__':
    s = Solution()
    s.rotate(nums=[1, 2, 3, 4, 5, 6, 7], k=3)
    s.rotate(nums=[-1, -100, 3, 99], k=2)

# [5, 6, 7, 1, 2, 3, 4]
# [3, -100, -1, 99]

可以看到第一个输出正确,第二个输出错误。

原因

上面方式只适用于数组长度n和步长k互质的情况。主要可以通过下面两点证明来理解:

1.循环数组中,从任意位置经过有限步步长移动后,一定会再次回到起始位置。

假设起始位置下标i,其实就是需要证明(i + mk) mod n = i,也就是证明mk mod n = 0m是至少需要移动的次数。

分两种情况:
1)nk互质,此时最小的移动次数显然等于ngcd(n, k)=1
2)nk不互质,设最大公约数gcd(n, k)=d n ′ = n d n'=\frac{n}{d} n=dn k ′ = k d k'=\frac{k}{d} k=dk,其中 n ′ 、 k ′ n'、k' nk互质。此时上面等式等价于 m d k ′ m o d    d n ′ = 0 mdk'\mod dn' = 0 mdkmoddn=0,化简为=> m k ′ m o d    n ′ = 0 mk' \mod n'=0 mkmodn=0,因此当m= n ′ n' n的时候成立。

综上,得证。

2.循环数组中环的数量等于数组长度n和步长k的最大公约数。

1中的证明其实说明从每个下标起始,m次移动后又会再次回到这个下标,这个过程其实构成了一个环,移动的次数就是环中的元素数量。并且因为是等距移动,所以环和环之间的元素也不会冲突。

同样分为nk互质和不互质两种情况:
1)互质:根据1,需要移动n次,数组长度n,所以环的数量1。
2)不互质:同样根据1,需要移动 n ′ n' n次,所以每个环中的元素数量也是 n ′ n' n,所以环的数量= n n ′ \frac{n}{n'} nn=d。

正确答案

求出环的数量,遍历环的起始下标。

from typing import List


class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        import math
        arr_len = len(nums)
        gcd_ans = math.gcd(arr_len, k)
        for idx in range(gcd_ans):  # 每个环的起始下标
            curr_idx, curr_num = idx, nums[idx]
            while True:
                next_idx = (curr_idx + k) % arr_len
                if next_idx == idx:
                    nums[next_idx] = curr_num
                    break
                nums[next_idx], curr_num = curr_num, nums[next_idx]
                curr_idx = next_idx
        print(nums)


if __name__ == '__main__':
    s = Solution()
    s.rotate(nums=[1, 2, 3, 4, 5, 6, 7], k=3)
    s.rotate(nums=[-1, -100, 3, 99], k=2)

# [5, 6, 7, 1, 2, 3, 4]
# [3, 99, -1, -100]

http://www.kler.cn/a/544572.html

相关文章:

  • Opensearch/ElasticSearch-ctx查询内容不全的问题
  • Python从0到100(八十八):LSTM网络详细介绍及实战指南
  • 基于千兆5G网关的5G急救车方案
  • git用法(简易版)
  • SSH 代理与私钥持久化:让你的开发环境不再因重启而中断
  • windows系统 从 Hugging Face网站上使用 huggingface-cli 命令下载AI大模型到本地
  • 模糊聚类分析方法:从模糊等价矩阵到动态分类
  • 【Java常用】注解与反射_2.反射
  • Windows 图形显示驱动开发-概述
  • .NET 9.0 的 Blazor Web App 项目,进度条 <progress> 组件使用注意事项
  • 相机闪光灯拍照流程分析
  • 生成式AI应用提示注入攻击防御解决方案(上)
  • 渗透测试--文件包含漏洞
  • 编程语言错误处理机制的演变与 Go 的实践
  • 华为支付-商户基础支付场景开发步骤
  • 2025 西湖论剑wp
  • Baumer集成一体式相机堡盟一体式相机相机如何通过NeoAPI SDK使用自动对焦功能和可分区光源控制功能(C#)
  • 【闲谈集】学网络应用开发好还是学网络安全好?
  • 【Docker】从瀑布开发到敏捷开发
  • 从零开始:Django初学者的实战之旅