题目描述

✅ 896. 单调数列

题意分析

判断原数组是否整体非递减,或者整体非递增;两种方向只需满足一种,相等的元素不会破坏单调性。

不必比较任意两个位置。如果每对相邻元素都满足前者不大于后者,由大小关系的传递性,任意更靠前的元素也不会大于更靠后的元素;非递增同理。因此只检查相邻关系就足够了。

解法:分别排除两种单调方向

核心思路

[!blue]

用 increasing 表示已经扫描的前缀仍然非递减,用 decreasing 表示它仍然非递增。最开始还没有相邻关系,两种方向都没有被否定,所以都初始化为 true。

扫描到 nums[i] 时,如果它小于前一个元素,就出现下降,非递减方向被排除;如果它大于前一个元素,就出现上升,非递增方向被排除;相等则两种方向都可以保留。标记一旦变为 false 就不能恢复,因为后面的元素无法消除已经出现的反例。

每轮只增加一对新的相邻关系,因此更新后,两个标记仍准确描述整个已扫描前缀。扫描结束时,它们就分别代表整个数组是否非递减、是否非递增,返回逻辑或即可;两者都为假才说明数组同时出现过上升和下降。

解题步骤

  1. 将两个方向标记设为 true。
  2. 扫描每对相邻元素,按大小关系更新标记。
  3. 返回两个标记的逻辑或。

单元素数组没有需要比较的相邻项,会保留两个真值;全相等数组也不会排除任何方向,所以这两类输入都自然返回 true。开头有多项相等时,同样不必提前决定方向。

代码实现

class Solution {
    public boolean isMonotonic(int[] nums) {
        boolean increasing = true;
        boolean decreasing = true;

        for (int i = 1; i < nums.length; i++) {
            if (nums[i] < nums[i - 1]) {
                increasing = false;
            }

            if (nums[i] > nums[i - 1]) {
                decreasing = false;
            }
        }

        return increasing || decreasing;
    }
}
func isMonotonic(nums []int) bool {
    increasing, decreasing := true, true
    for i := 1; i < len(nums); i++ {
        if nums[i] < nums[i-1] {
            increasing = false
        }
        if nums[i] > nums[i-1] {
            decreasing = false
        }
    }
    return increasing || decreasing
}

复杂度分析

  • 时间复杂度:$O(n)$,每对相邻元素只比较一次。
  • 空间复杂度:额外空间 $O(1)$。

关键点总结

[!green]

  • 相邻关系通过传递性保证全局单调,无需两两比较所有元素。
  • 两个标记记录尚未被反例排除的方向,更新只能从真变假。
  • 同时保留两种可能性,统一处理开头相等和全相等的情况。

易错点总结

[!yellow]

  • 非严格单调允许相等,不能把相等当成失败。
  • 最后返回逻辑或,不能要求两个方向同时成立;后者只会接受全相等数组。
  • 一次上升或下降只能排除相反方向,不能把另一方向重新设为真。
  • 排序后再检查会丢掉原顺序,无法回答本题。

相似题目

题目 难度 关联与区别
941. 有效的山脉数组 简单 都根据相邻升降关系扫描;山脉要求先严格升后严格降,本题要求整段只有一个非严格方向。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/96571260
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!