1. 概述

在本篇文章中,我们将讨论一个经典数组问题:找出数组中每个元素的下一个更小元素(Next Smaller Element)

我们会先明确问题定义,通过一个例子帮助理解。随后,我们介绍两种解决思路:
✅ 一种是直观但效率较低的暴力解法(O(n²))
✅ 一种是使用栈结构优化的线性解法(O(n))

2. 问题定义

给定一个整数数组 A,长度为 N。我们需要为每个元素 A[i] 找出其右侧第一个比它小的元素 A[j],满足:

  • j > i
  • A[j] < A[i]
  • j 尽可能小

如果找不到这样的元素,则返回 -1

示例

数组为 [7, 3, 4, 1, 5, 3],其每个元素的下一个更小元素如下:

NextSmaller

  • 7 → 3
  • 3 → 1
  • 4 → 1
  • 1 → -1(没有更小)
  • 5 → 3
  • 3 → -1(没有更小)

3. 暴力解法(Naive Approach)

3.1 核心思想

对于每个元素,从它右边开始逐个比较,直到找到第一个比它小的元素。

3.2 实现代码(伪代码)

algorithm NaiveApproach(Arr):
    // INPUT
    //   Arr = an array of integers
    // OUTPUT
    //   Returns the next smaller element for each element in Arr

    next_smaller <- initialize an array of size N with -1
    for i <- 1 to N:
        for j <- i + 1 to N:
            if Arr[j] < Arr[i]:
                next_smaller[i] <- Arr[j]
                break

    return next_smaller

3.3 时间复杂度分析

  • 时间复杂度:O(N²)
  • 空间复杂度:O(N)

⚠️ 虽然实现简单,但在数据量较大时性能较差,容易超时。

4. 栈解法(Stack Approach)

4.1 核心思想

利用栈结构来维护“可能成为下一个更小元素”的候选值。

  • 从右往左遍历数组
  • 维护一个栈,栈顶元素始终是当前元素右侧第一个比它小的元素
  • 如果栈顶比当前元素大或相等,则弹出,直到找到更小的或栈为空
  • 最终栈顶即为当前元素的下一个更小元素

4.2 实现代码(伪代码)

algorithm StackApproach(Arr):
    // INPUT
    //   Arr = an array of integers
    // OUTPUT
    //   Returns the next smaller element for each element in Arr

    next_smaller <- initialize an array of size N with -1
    stack <- an empty stack
    for i <- N, N - 1, ..., 1:
        next_smaller[i] <- -1
        while not stack.empty() AND stack.top() >= Arr[i]:
            stack.pop()
        if not stack.empty():
            next_smaller[i] <- stack.top()
        stack.push(Arr[i])

    return next_smaller

4.3 时间复杂度分析

  • 时间复杂度:O(N)
  • 空间复杂度:O(N)

✅ 每个元素进栈一次、出栈最多一次,整体线性复杂度,效率远高于暴力解法。

5. 总结

我们讨论了如何为数组中每个元素找到右侧第一个更小的元素。

方法 时间复杂度 空间复杂度 适用场景
暴力解法 O(N²) O(N) 数据量小
栈解法 O(N) O(N) 数据量大推荐使用

✅ 推荐掌握栈解法,它是解决“下一个更大/更小”类问题的通用技巧,常用于面试和算法竞赛中。
⚠️ 踩坑提醒:遍历方向(从右到左)和栈的操作顺序容易写反,务必多做几遍手推练习。


原始标题:Finding the Next Smaller Element for Each Element in an Array