2855. 使数组成为递增数组的最少右移次数

【算法解析】将数组右移使其递增的最少次数(Python实现)

在日常算法练习中,我们常常会遇到关于数组旋转和排序的问题。本文将深入探讨一个典型问题:“通过最少右移次数,使一个数组变为递增数组”。我们将从题意分析、思路探索、代码实现到最优解优化一一讲解。


🧩 题目描述

给定一个长度为 n 的数组 nums,其中所有元素互不相同且为正整数。我们每次可以将整个数组右移一位,也就是说数组中每个元素 nums[i] 会变成 nums[(i + 1) % n]。我们需要找出使得数组变为严格递增数组所需的最少右移次数。如果无法通过任何右移操作使数组变为递增数组,返回 -1


✏️ 示例解释

示例 1:

输入:nums = [3, 4, 5, 1, 2]
输出:2

解释:
第一次右移后 => [2, 3, 4, 5, 1]
第二次右移后 => [1, 2, 3, 4, 5] ✅

示例 2:

输入:nums = [1, 3, 5]
输出:0 ✅

示例 3:

输入:nums = [2, 1, 4]
输出:-1 ❌
解释:无法通过右移得到一个递增数组。

🎯 解题思路

要判断一个数组是否可以通过若干次右移操作成为递增数组,我们可以将这个问题转化为找**“断点”**的问题。

👉 什么是断点?

一个“断点”是数组中前一个数比后一个数大的地方。也就是说,当我们在某个 i 处发现 nums[i] > nums[i+1](考虑循环意义,即 (i+1) % n),我们就认为这是一处断点。

✅ 合法的递增数组满足以下条件:
  • 最多只有一个断点。
  • 从断点位置进行右移,可以使数组重新排列为递增的形式。

💡 解法一:暴力模拟(不推荐)

尝试所有可能的右移次数(最多 n 次),每次判断数组是否为严格递增。时间复杂度是 O(n2)O(n^2),效率低下,适合面试时思考过程,不建议实际使用。


⚡ 解法二:线性扫描断点(推荐)

class Solution:
    def minimumRightShifts(self, nums: List[int]) -> int:
        cliff, ans = 0, 0
        n = len(nums)
        if n == 1: return 0
        for i in range(n):
            if nums[i] > nums[(i + 1) % n]:
                cliff += 1
                ans = i + 1
        if cliff > 1:
            return -1
        if cliff == 0:
            return 0
        # 检查旋转后的是否真的递增
        rotated = nums[ans:] + nums[:ans]
        for i in range(1, n):
            if rotated[i] <= rotated[i - 1]:
                return -1
        return n - ans

🧠 思路细节:

  • 记录“断点”数量 cliff
  • cliff == 0:数组本身递增,返回 0。
  • cliff == 1:记录断点后的位置 ans,尝试从该位置旋转,验证旋转后数组是否递增。
  • cliff > 1:说明存在多个下降趋势,不可能变为递增数组,返回 -1。

✅ 正确性验证

  • nums = [3, 4, 5, 1, 2],断点在索引 2,n - (2 + 1) = 2,答案正确。
  • nums = [1, 3, 5],无断点,返回 0,答案正确。
  • nums = [2, 1, 4],断点在索引 0,但旋转后为 [1, 4, 2],不递增,返回 -1。

⏱️ 时间与空间复杂度

  • 时间复杂度:O(n),只遍历一次数组和一次判断是否递增。
  • 空间复杂度:O(n)(因为需要构造 rotated 数组用于判断)。

✨ 总结

这个问题看似简单,却包含了多个关键点:

  1. 需要理解旋转数组的性质;
  2. 必须判断是否只有一个断点
  3. 注意旋转后的数组不一定符合严格递增的要求,需要额外验证。

这道题训练了我们数组遍历技巧边界判断能力,同时还体现了从暴力思维到最优解的算法优化路径,是一道很有价值的面试题。


🔚 附完整代码(可直接使用)

class Solution:
    def minimumRightShifts(self, nums: List[int]) -> int:
        cliff, ans = 0, 0
        n = len(nums)
        if n == 1: return 0
        for i in range(n):
            if nums[i] > nums[(i + 1) % n]:
                cliff += 1
                ans = i + 1
        if cliff > 1:
            return -1
        if cliff == 0:
            return 0
        # 验证旋转后的数组是否递增
        rotated = nums[ans:] + nums[:ans]
        for i in range(1, n):
            if rotated[i] <= rotated[i - 1]:
                return -1
        return n - ans

💬 有任何疑问或者其他优化思路,欢迎留言一起讨论!你还可以尝试自己用其他语言(如 C++ / Java)实现这个思路。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值