• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
第八天
博客园    首页    新随笔    联系   管理    订阅  订阅

单调栈

// ⚠️ 始终用 ArrayDeque 替代 Stack(性能差3-5倍)
Deque<Integer> stack = new ArrayDeque<>(); 
int[] res = new int[nums.length];
Arrays.fill(res, -1); // 默认无更大元素

for (int i = 0; i < nums.length; i++) {
    // 核心:当前元素 > 栈顶 → 栈顶的"下一个更大"就是nums[i]
    while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {
        int idx = stack.pop();
        res[idx] = nums[i]; // 或 i - idx(距离)
    }
    stack.push(i); // 存索引!非值!
}
⚠️ 三大铁律(违反必错)
栈中存索引,不存值:索引可反推值、位置、距离;值丢失位置信息。
比较用 > / <,不用 >= / <=:等号处理决定“相等元素是否视为更大”,需根据题意调整(见下文#84题)。
循环结束后栈非空 ≠ 错误:剩余元素确实没有“下一个更大”,已由初始化 -1 覆盖。
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    Map<Integer, Integer> map = new HashMap<>();
    Deque<Integer> stack = new ArrayDeque<>();
    
    // 预处理nums2:建立"值→下一个更大值"映射
    for (int num : nums2) {
        while (!stack.isEmpty() && num > stack.peek()) {
            map.put(stack.pop(), num);
        }
        stack.push(num); // 此题可存值(因nums1查询用值)
    }
    
    int[] res = new int[nums1.length];
    for (int i = 0; i < nums1.length; i++) {
        res[i] = map.getOrDefault(nums1[i], -1);
    }
    return res;
}

  BFS

public int bfs(int[][] grid, int startR, int startC) {
    int rows = grid.length, cols = grid[0].length;
    boolean[][] visited = new boolean[rows][cols];
    Deque<int[]> queue = new ArrayDeque<>(); // ⚠️ 不用LinkedList
    
    queue.offer(new int[]{startR, startC});
    visited[startR][startC] = true; // ⚠️ 入队时标记!非出队时!
    int steps = 0;
    
    while (!queue.isEmpty()) {
        int size = queue.size(); // 当前层节点数
        for (int i = 0; i < size; i++) {
            int[] cur = queue.poll();
            if (/* 到达目标 */) return steps;
            
            for (int[] dir : DIRS) {
                int nr = cur[0] + dir[0], nc = cur[1] + dir[1];
                if (nr >= 0 && nr < rows && nc >= 0 && nc < cols 
                    && !visited[nr][nc] && grid[nr][nc] == 0) {
                    visited[nr][nc] = true; // ⚠️ 入队前标记防重复入队
                    queue.offer(new int[]{nr, nc});
                }
            }
        }
        steps++; // 处理完一层
    }
    return -1; // 不可达
}

  

步骤1:状态定义(最关键)

  • 问自己:dp[i] 或 dp[i][j] 代表什么具体含义?
  • 中等题常见模式:
    • dp[i]:以第i个元素结尾/开头的最优解
    • dp[i][j]:前i个物品容量为j / s1前i字符与s2前j字符
    • dp[i][mask]:状态压缩(中等题较少见)
  • 检验标准:能否用一句话清晰描述?若模糊则重新定义。

步骤2:转移方程

  • 思考路径:当前状态如何由更小规模子问题推导?
  • 中等题特征:通常只有1-2个来源,不会出现复杂枚举。
  • 边界处理:显式初始化base case,避免循环内特判。

步骤3:遍历顺序

  • 铁律:计算dp[i]时,其所依赖的状态必须已计算。
  • 快速判断:
    • 依赖i-1 → 正序
    • 依赖i+1 → 倒序
    • 二维依赖左上 → 正序行列
    • 二维依赖左+上 → 正序行列(可优化)
    • 一维背包依赖上一行 → 倒序(防覆盖)

步骤4:空间优化(可选但推荐)

  • 滚动数组:二维→一维,注意遍历方向
  • 变量替代:一维→O(1),仅当只依赖前1-2个状态时
  • 面试策略:先写未优化版确保正确,再口述优化思路;若时间充裕再实现。
  • // 【基础版】O(N²) - 面试首选展示DP思维
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n]; // dp[i]: 以nums[i]结尾的LIS长度
        Arrays.fill(dp, 1);   // base case: 单个元素长度为1
    // 【二维版】清晰展示状态转移
    public int coinChange2D(int[] coins, int amount) {
        int n = coins.length;
        int[][] dp = new int[n + 1][amount + 1];
        // base case: 0元需0枚;其他初始化为极大值
        for (int j = 1; j <= amount; j++) dp[0][j] = amount + 1;
        
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                dp[i][j] = dp[i-1][j]; // 不选第i种硬币
                if (j >= coins[i-1]) {
                    dp[i][j] = Math.min(dp[i][j], dp[i][j - coins[i-1]] + 1); // 选(可重复)
                }
            }
        }
        return dp[n][amount] > amount ? -1 : dp[n][amount];
    }
    
    // 【一维优化版】生产级写法
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;
        
        for (int coin : coins) {
            for (int j = coin; j <= amount; j++) { // ⚠️ 正序!完全背包特征
                dp[j] = Math.min(dp[j], dp[j - coin] + 1);
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
    

      

    int maxLen = 1; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[j] < nums[i]) { dp[i] = Math.max(dp[i], dp[j] + 1); } } maxLen = Math.max(maxLen, dp[i]); } return maxLen; } // 【进阶版】O(N log N) - 贪心+二分,体现算法深度 public int lengthOfLISOptimized(int[] nums) { List<Integer> tails = new ArrayList<>(); // tails[k]: 长度为k+1的LIS最小末尾 for (int num : nums) { int pos = Collections.binarySearch(tails, num); if (pos < 0) pos = -(pos + 1); // 插入位置 if (pos == tails.size()) { tails.add(num); // 扩展最长长度 } else { tails.set(pos, num); // 替换使末尾更小 } } return tails.size(); }

      

posted on 2026-08-09 18:40  小白小泡泡  阅读(3)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3