刷题实践之数据结构| 豆包MarsCode AI 刷题

65 阅读4分钟

解题思路与代码实现

问题描述

小F在“双十一”期间购买了 N 件商品。每件商品有一个价格 p[i],小F可以获得的优惠取决于该商品之前的一件商品。如果某一件商品的价格 p[i] 大于等于前面的某个商品 p[j],则小F可以享受该商品 p[j] 的价格作为优惠,前提是 p[j] 是离 p[i] 最近的且满足条件的商品。

例如:

  • 给定价格数组 p = [9, 4, 5, 2, 4]
    • p[3] = 2 之前没有商品的价格小于等于 p[3],因此没有优惠。
    • p[2] = 5 可以享受最近的商品 p[1] = 4 的价格作为优惠。

任务是计算小F能获得的总优惠。

测试样例

  • 样例1:

    • 输入: N = 5, p = [9, 4, 5, 2, 4]
    • 输出: 6
  • 样例2:

    • 输入: N = 4, p = [1, 2, 3, 5]
    • 输出: 6
  • 样例3:

    • 输入: N = 4, p = [4, 3, 2, 1]
    • 输出: 0

解题思路

为了高效地找到每个商品 p[i] 最接近且价格小于等于它的前一个商品 p[j], 我们可以使用 单调栈(Monotonic Stack)。具体步骤如下:

  1. 初始化一个空栈,用于存储商品的索引。
  2. 遍历价格数组 p 从左到右:
    • 对于每个商品 p[i]弹出栈顶,直到找到第一个 p[j] ≤ p[i] 或者栈为空。
    • 如果栈不为空,那么 p[j] 就是满足条件且离 p[i] 最近的商品,将 p[j] 加入总优惠
    • 将当前索引 i 压入栈中
  3. **最终,**总优惠就是满足条件的所有 p[j] 之和。

通过这种方式,我们可以在 O(N) 的时间复杂度内完成整个过程。

代码实现

import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    public static int solution(int N, int[] p) {
        // write code here
        // 使用栈来存储索引
        Deque<Integer> stack = new ArrayDeque<>();
        long totalDiscount = 0;
        for (int i = 0; i < N; i++) {
            // 弹出栈顶直到找到 p[j] <= p[i] 或者栈为空
            while (!stack.isEmpty() && p[stack.peek()] > p[i]) {
                stack.pop();
            }
            if (!stack.isEmpty()) {
                // 最近的满足条件的 p[j]
                totalDiscount += p[stack.peek()];
            }
            // 将当前索引压入栈中
            stack.push(i);
        }
        return (int) totalDiscount;
    }

    public static void main(String[] args) {
        System.out.println(solution(5, new int[] { 9, 4, 5, 2, 4 }) == 6);
        System.out.println(solution(4, new int[] { 1, 2, 3, 5 }) == 6);
        System.out.println(solution(4, new int[] { 4, 3, 2, 1 }) == 0);
    }
}

代码说明

  1. 数据结构选择:

    • 使用 栈(Deque) 来存储商品的索引,以便快速找到每个商品之前最近且价格小于等于它的商品。
  2. 遍历过程:

    • 对于每个商品 p[i],不断弹出栈顶的索引 j,直到找到一个 p[j] ≤ p[i] 或者栈为空。
    • 如果栈不为空,说明存在一个满足条件的商品 p[j],将其价格 p[j] 加入 总优惠
    • 将当前索引 i 压入栈中,作为可能后续商品的 j
  3. 复杂度分析:

    • 时间复杂度: O(N),每个元素最多被压入和弹出栈一次。
    • 空间复杂度: O(N),最坏情况下,栈中可能存储所有的索引。

测试样例验证

  1. 样例1:

    • 输入: N = 5, p = [9, 4, 5, 2, 4]
    • 过程:
      • i=0: 无前置商品,优惠 0,栈:[0]
      • i=1: p[0]=9 > 4,弹出,栈为空,优惠 0,栈:[1]
      • i=2: p[1]=4 ≤ 5,优惠 4,栈:[2]
      • i=3: p[2]=5 > 2,弹出;p[1]=4 > 2,弹出,栈为空,优惠 0,栈:[3]
      • i=4: p[3]=2 ≤ 4,优惠 2,栈:[4]
    • 总优惠: 4 + 2 = 6
    • 输出: 6
  2. 样例2:

    • 输入: N = 4, p = [1, 2, 3, 5]
    • 过程:
      • i=0: 无前置商品,优惠 0,栈:[0]
      • i=1: p[0]=1 ≤ 2,优惠 1,栈:[1]
      • i=2: p[1]=2 ≤ 3,优惠 2,栈:[2]
      • i=3: p[2]=3 ≤ 5,优惠 3,栈:[3]
    • 总优惠: 1 + 2 + 3 = 6
    • 输出: 6
  3. 样例3:

    • 输入: N = 4, p = [4, 3, 2, 1]
    • 过程:
      • i=0: 无前置商品,优惠 0,栈:[0]
      • i=1: p[0]=4 > 3,弹出,栈为空,优惠 0,栈:[1]
      • i=2: p[1]=3 > 2,弹出,栈为空,优惠 0,栈:[2]
      • i=3: p[2]=2 > 1,弹出,栈为空,优惠 0,栈:[3]
    • 总优惠: 0
    • 输出: 0

结论

通过使用单调栈的方法,我们能够高效地找到每个商品前面最近且价格小于等于它的商品,从而计算出小F可以获得的总优惠。该算法的时间复杂度为 O(N),适用于大规模数据。