数据结构与算法(Java) -单调队列单调栈题单

单调队列(灵神笔记)

239 滑动窗口最大值

239. 滑动窗口最大值 - 力扣(LeetCode)

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        /*示例1 队列的变化
        * 1. 1  2. 3    3. 3 -1 -3  4. 5    5. 5 3  6. 6    7. 7
        * 注意如果是第3步 将2加入队列 滑动窗口长度(i-队首下标)大于3 删除队首元素
        * 保证队首元素为滑动窗口最大值 队列单调递减
        */ 
        int n = nums.length;
        int[] ans = new int[n - k + 1];
        Deque<Integer> d = new ArrayDeque<>();
        for (int i = 0; i < n; i++) {
            //进
            while (!d.isEmpty() && nums[i] >= nums[d.peekLast()]) {
                d.pollLast();
            }
            d.addLast(i);
            //出
            if (i - d.peekFirst() >= k) {
                d.pollFirst();
            }
            //更新答案
            if (i >= k - 1) {
                ans[i - k + 1] = nums[d.peekFirst()];
            }
        }
        return ans;
    }
}

1438 绝对差不超过限制的最长连续子数组

1438. 绝对差不超过限制的最长连续子数组 - 力扣(LeetCode)

给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit

如果不存在满足条件的子数组,则返回 0

示例 1:

输入:nums = [8,2,4,7], limit = 4
输出:2 
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4. 
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4. 
因此,满足题意的最长子数组的长度为 2 。

示例 2:

输入:nums = [10,1,2,4,7,2], limit = 5
输出:4 
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。

示例 3:

输入:nums = [4,2,2,2,4,4,2,2], limit = 0
输出:3

提示:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^9
  • 0 <= limit <= 10^9
class Solution {
    public int longestSubarray(int[] nums, int limit) {
        //利用两个双端队列,max从左到右单调递增,队首为最大值,min相反
        Deque<Integer> maxdeque = new ArrayDeque<>();
        Deque<Integer> mindeque = new ArrayDeque<>();
        int n = nums.length;
        int right = 0, left = 0, ans = 0;
        //枚举right
        while (right < n) {
            while (!maxdeque.isEmpty() && nums[right] > maxdeque.peekLast()) {
                maxdeque.removeLast();
            }
            while (!mindeque.isEmpty() && nums[right] < mindeque.peekLast()) {
                mindeque.removeLast();
            }
            maxdeque.addLast(nums[right]);
            mindeque.addLast(nums[right]);
            //移动left
            while (maxdeque.peekFirst() - mindeque.peekFirst() > limit) {
                //更新队内元素
                if (maxdeque.peekFirst() == nums[left]) {
                    maxdeque.removeFirst();
                }
                if (mindeque.peekFirst() == nums[left]) {
                    mindeque.removeFirst();
                }
                left++;
            }
            ans = Math.max(ans, right - left + 1);
            right++;
        }
        return ans;
    }
}

2398 预算内的最多机器人数目

2398. 预算内的最多机器人数目 - 力扣(LeetCode)

你有 n 个机器人,给你两个下标从 0 开始的整数数组 chargeTimesrunningCosts ,两者长度都为 n 。第 i 个机器人充电时间为 chargeTimes[i] 单位时间,花费 runningCosts[i] 单位时间运行。再给你一个整数 budget

运行 k 个机器人 总开销max(chargeTimes) + k * sum(runningCosts) ,其中 max(chargeTimes) 是这 k 个机器人中最大充电时间,sum(runningCosts) 是这 k 个机器人的运行时间之和。

请你返回在 不超过 budget 的前提下,你 最多 可以 连续 运行的机器人数目为多少。

示例 1:

输入:chargeTimes = [3,6,1,3,4], runningCosts = [2,1,3,4,5], budget = 25
输出:3
解释:
可以在 budget 以内运行所有单个机器人或者连续运行 2 个机器人。
选择前 3 个机器人,可以得到答案最大值 3 。总开销是 max(3,6,1) + 3 * sum(2,1,3) = 6 + 3 * 6 = 24 ,小于 25 。
可以看出无法在 budget 以内连续运行超过 3 个机器人,所以我们返回 3 。

示例 2:

输入:chargeTimes = [11,12,19], runningCosts = [10,8,7], budget = 19
输出:0
解释:即使运行任何一个单个机器人,还是会超出 budget,所以我们返回 0 。

提示:

  • chargeTimes.length == runningCosts.length == n
  • 1 <= n <= 5 * 104
  • 1 <= chargeTimes[i], runningCosts[i] <= 105
  • 1 <= budget <= 1015
class Solution {
    /**
        chargeTimes  3 6 1 3 4
        runningCosts 2 1 3 4 5
        与239滑动窗口最大值一样 队首依然是维护chargeTimes的最大元素
        仅仅将固定大小的滑动窗口改为了不固定大小的双指针
     */

    public int maximumRobots(int[] chargeTimes, int[] runningCosts, long budget) {
        int ans = 0;
        Deque<Integer> q = new ArrayDeque<Integer>();
        long sum = 0L;
        for (int left = 0, right = 0; right < chargeTimes.length; right++) {
            //进
            while (!q.isEmpty() && chargeTimes[right] >= chargeTimes[q.peekLast()]) {
                q.pollLast();
            }
            q.addLast(right);
            sum += runningCosts[right];
            //出 
            while (!q.isEmpty() && chargeTimes[q.peekFirst()] + (right - left + 1) * sum > budget) {
                //及时清除无用的数据
                if (q.peekFirst() == left) {
                    q.pollFirst();
                }
                sum -= runningCosts[left++];
            }
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

将[子数组]改为[子序列]便是接下来的题目

1383 最大的团队表现值(附加)(堆)

1383. 最大的团队表现值 - 力扣(LeetCode)

给定两个整数 nk,以及两个长度为 n 的整数数组 speed efficiency。现有 n 名工程师,编号从 1n。其中 speed[i]efficiency[i] 分别代表第 i 位工程师的速度和效率。

从这 n 名工程师中最多选择 k 名不同的工程师,使其组成的团队具有最大的团队表现值。

团队表现值 的定义为:一个团队中「所有工程师速度的和」乘以他们「效率值中的最小值」。

请你返回该团队的最大团队表现值,由于答案可能很大,请你返回结果对 10^9 + 7 取余后的结果。

示例 1:

输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 2
输出:60
解释:
我们选择工程师 2(speed=10 且 efficiency=4)和工程师 5(speed=5 且 efficiency=7)。他们的团队表现值为 performance = (10 + 5) * min(4, 7) = 60 。

示例 2:

输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 3
输出:68
解释:
此示例与第一个示例相同,除了 k = 3 。我们可以选择工程师 1 ,工程师 2 和工程师 5 得到最大的团队表现值。表现值为 performance = (2 + 10 + 5) * min(5, 4, 7) = 68 。

示例 3:

输入:n = 6, speed = [2,10,3,1,5,8], efficiency = [5,4,3,9,7,2], k = 4
输出:72

提示:

  • 1 <= k <= n <= 10^5
  • speed.length == n
  • efficiency.length == n
  • 1 <= speed[i] <= 10^5
  • 1 <= efficiency[i] <= 10^8
class Solution {
    public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
       long maxP = 0;
       int[][] engineers = new int[n][2];
       for (int i = 0; i < n; i++) {
           engineers[i][0] = speed[i];
           engineers[i][1] = efficiency[i];
       } 
       //排序
       Arrays.sort(engineers, (a, b) -> b[1] - a[1]);
       PriorityQueue<Integer> pq = new PriorityQueue<>();
       long sumSpeed = 0;
       int minEfficiency = Integer.MAX_VALUE;
       for (int i = 0; i < n; i++) {
           //取出队首元素(即原有工程师中的最低速度)
           if (i >= k) {
               int preS = pq.poll();
               sumSpeed -= preS;
           }
           int[] engineer = engineers[i];
           pq.add(engineer[0]);
           sumSpeed += engineer[0];
           minEfficiency = engineer[1];
           long curP = sumSpeed * minEfficiency;
           maxP = Math.max(maxP, curP);
       }
       return (int) (maxP % (1000000007));
    }
}

862 和至少为K的最短子数组

862. 和至少为 K 的最短子数组 - 力扣(LeetCode)

给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1

子数组 是数组中 连续 的一部分。

示例 1:

输入:nums = [1], k = 1
输出:1

示例 2:

输入:nums = [1,2], k = 4
输出:-1

示例 3:

输入:nums = [2,-1,2], k = 3
输出:3

提示:

  • 1 <= nums.length <= 105
  • -105 <= nums[i] <= 105
  • 1 <= k <= 109
class Solution {
    public int shortestSubarray(int[] nums, int k) {
        int n = nums.length;
        long[] pre = new long[n + 1];
        pre[0] = 0;
        for (int i = 0; i < n; i++) {
            pre[i + 1] = pre[i] + nums[i];
        }
        Deque<Integer> q = new ArrayDeque<>();
        int ans = n + 1;
        for (int i = 0; i <= n; i++) {
            long cur = pre[i];
            while (!q.isEmpty() && cur - pre[q.peekFirst()] >= k) {
                ans = Math.min(ans, i - q.pollFirst());
            }
            while (!q.isEmpty() && cur <= pre[q.peekLast()]) {
                q.pollLast();
            }
            q.addLast(i);
        }
        return ans > n ? -1 : ans;
    }
}

1499 满足不等式的最大值

参考题解:1499. 满足不等式的最大值 - 力扣(LeetCode)

给你一个数组 points 和一个整数 k 。数组中每个元素都表示二维平面上的点的坐标,并按照横坐标 x 的值从小到大排序。也就是说 points[i] = [xi, yi] ,并且在 1 <= i < j <= points.length 的前提下, xi < xj 总成立。

请你找出 yi + yj + |xi - xj|最大值,其中 |xi - xj| <= k1 <= i < j <= points.length

题目测试数据保证至少存在一对能够满足 |xi - xj| <= k 的点。

示例 1:

输入:points = [[1,3],[2,0],[5,10],[6,-10]], k = 1
输出:4
解释:前两个点满足 |xi - xj| <= 1 ,代入方程计算,则得到值 3 + 0 + |1 - 2| = 4 。第三个和第四个点也满足条件,得到值 10 + -10 + |5 - 6| = 1 。
没有其他满足条件的点,所以返回 4 和 1 中最大的那个。

示例 2:

输入:points = [[0,0],[3,0],[9,2]], k = 3
输出:3
解释:只有前两个点满足 |xi - xj| <= 3 ,代入方程后得到值 0 + 0 + |0 - 3| = 3 。

提示:

  • 2 <= points.length <= 10^5
  • points[i].length == 2
  • -10^8 <= points[i][0], points[i][1] <= 10^8
  • 0 <= k <= 2 * 10^8
  • 对于所有的1 <= i < j <= points.lengthpoints[i][0] < points[j][0] 都成立。也就是说,xi 是严格递增的。
class Solution {
    /** yi+yj+abs(xi-xj)=yi+yj+xj-xi=(yi-xi)+(xj+yj)
        不满足abs(xi-xj)<=k 即xj-xi>k 弹他 
        想要得到xi我们需要用队列来保存
        将(xj,yj-xj)入队 保证队列始终是单调递减
        yj-xj不低于队尾的数据 弹他
     */
    public int findMaxValueOfEquation(int[][] points, int k) {
        int ans = Integer.MIN_VALUE;
        Deque<int[]> q = new ArrayDeque<>();
        for (var p : points) {
            int x = p[0], y = p[1];
            while (!q.isEmpty() && x - q.peekFirst()[0] > k) {
                q.pollFirst();
            }
            if (!q.isEmpty()) {
                ans = Math.max(ans, x + y + q.peekFirst()[1]);
            }
            while (!q.isEmpty() && q.peekLast()[1] <= y - x) {
                q.pollLast();
            }
            q.addLast(new int[]{x, y - x});
        }
        return ans;
    }
}

2944 购买水果需要的最少金币数

参考视频:单调队列优化 DP【力扣双周赛 118】_哔哩哔哩_bilibili

你在一个水果超市里,货架上摆满了玲琅满目的奇珍异果。

给你一个下标从 1 开始的数组 prices ,其中 prices[i] 表示你购买第 i 个水果需要花费的金币数目。

水果超市有如下促销活动:

  • 如果你花费 price[i] 购买了水果 i ,那么接下来的 i 个水果你都可以免费获得。

注意 ,即使你 可以 免费获得水果 j ,你仍然可以花费 prices[j] 个金币去购买它以便能免费获得接下来的 j 个水果。

请你返回获得所有水果所需要的 最少 金币数。

示例 1:

输入:prices = [3,1,2]
输出:4
解释:你可以按如下方法获得所有水果:
- 花 3 个金币购买水果 1 ,然后免费获得水果 2 。
- 花 1 个金币购买水果 2 ,然后免费获得水果 3 。
- 免费获得水果 3 。
注意,虽然你可以免费获得水果 2 ,但你还是花 1 个金币去购买它,因为这样的总花费最少。
购买所有水果需要最少花费 4 个金币。

示例 2:

输入:prices = [1,10,1,1]
输出:2
解释:你可以按如下方法获得所有水果:
- 花 1 个金币购买水果 1 ,然后免费获得水果 2 。
- 免费获得水果 2 。
- 花 1 个金币购买水果 3 ,然后免费获得水果 4 。
- 免费获得水果 4 。
购买所有水果需要最少花费 2 个金币。

提示:

  • 1 <= prices.length <= 1000
  • 1 <= prices[i] <= 105

记忆化搜索

class Solution {
    /** 哪些水果是免费的
        i+1,i+2...2i+1
        下一个购买的水果 j [i+1,2i+1]
        i+1到j-1都是免费获得的
     */

    public int minimumCoins(int[] prices) {
        int n = prices.length;
        int[] cache = new int[(n + 1) / 2];
        return dfs(prices, cache, 1);
    }

    private int dfs(int[] prices, int[] cache, int i) {
        int n = prices.length;
        // if (i > n) {
        //     return 0; // 递归边界
        // }
        if (i * 2 >= n) {
            return prices[i - 1]; // i从1开始 优化递归边界
        }
        if (cache[i] != 0) {
            return cache[i];
        }
        int ans = Integer.MAX_VALUE;
        for (int j = i + 1; j <= 2 * i + 1; j++) {
            ans = Math.min(ans, dfs(prices, cache, j));
        }
        return cache[i] = prices[i - 1] + ans;
    }
}

1:1翻译成递推

class Solution {
    /** 哪些水果是免费的
        i+1,i+2...2i+1
        下一个购买的水果 j [i+1,2i+1]
        i+1到j-1都是免费获得的
     */

    public int minimumCoins(int[] prices) {
        int n = prices.length;
        for (int i = (n + 1) / 2 - 1; i > 0; i--) {
            int mn = Integer.MAX_VALUE;
            for (int j = i; j <= i * 2; j++) {
                mn = Math.min(mn, prices[j]);
            }
            prices[i - 1] += mn;
        }
        return prices[0];
        //int[] f = new int[n + 1];
        // for (int i = n; i > 0; i--) {
        //     if (i * 2 >= n) {
        //         f[i] = prices[i - 1];
        //     } else {
        //         int mn = Integer.MAX_VALUE;
        //         for (int j = i + 1; j <= 2 * i + 1; j++) {
        //             mn = Math.min(mn, f[j]);
        //         }
        //         f[i] = mn + prices[i - 1];
        //     }
        // }
        // return f[1];
    }
}

单调队列优化

class Solution {
    /** 求滑动窗口最小值min(f[i+1:i*2+2])
        左边的小 淘汰掉 右边的大
        保证队尾元素始终最小 即队列单调递减
     */
    public int minimumCoins(int[] prices) {
        int n = prices.length;
        Deque<int[]> q = new ArrayDeque<>();
        q.addLast(new int[]{n + 1, 0});// 哨兵
        for (int i = n; i > 0; i--) {
            while (q.peekLast()[0] > 2 * i + 1) {
                q.pollLast();// 右边离开窗口
            }
            int f = prices[i - 1] + q.peekLast()[1];
            while (f <= q.peekFirst()[1]) {
                q.pollFirst();
            }
            q.addFirst(new int[]{i, f});// 左边进入窗口
        }
        return q.peekFirst()[1];
    }
}

单调栈(灵神笔记)

739 每日温度

739. 每日温度 - 力扣(LeetCode)

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

提示:

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] ans = new int[n];
        int[] st = new int[n];
        int top = -1;
        for (int i = 0; i < n; i++) {
            int t = temperatures[i];
            //遍历到更大的元素就弹出栈顶元素 
            while (top >= 0 && t > temperatures[st[top]]) {
                int j = st[top--];
                ans[j] = i - j;
            }
            st[++top] = i;
        }
        return ans;
    }
}

42 接雨水

42. 接雨水 - 力扣(LeetCode)

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

数据结构与算法(Java) -单调队列单调栈题单_第1张图片

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        Deque<Integer> st = new ArrayDeque<>();
        for (int i = 0; i < height.length; i++) {
            //遍历到右边的板子 可以接水
            while (!st.isEmpty() && height[i] >= height[st.peek()]) {
                //需要三个下标 栈顶元素(右边板子) 栈顶下面的元素(中间板子) 左边的板子
                //例如 5 2 1 0 4 1 1 6
                //遍历到4 栈从下到上为 5 4 bottomHeight=2
                int bottomHeight = height[st.pop()];
                if (st.isEmpty()) {
                    break;
                }
                //返回队首元素(左板子)
                int left = st.peek();
                //雨水面积的高
                int dh = Math.min(height[left], height[i]) - bottomHeight;
                ans += dh * (i - left - 1);
            }
            st.push(i);
        }
        return ans;
    }
}

1475 商品折扣后的最终价格

1475. 商品折扣后的最终价格 - 力扣(LeetCode)

给你一个数组 prices ,其中 prices[i] 是商店里第 i 件商品的价格。

商店里正在进行促销活动,如果你要买第 i 件商品,那么你可以得到与 prices[j] 相等的折扣,其中 j 是满足 j > iprices[j] <= prices[i]最小下标 ,如果没有满足条件的 j ,你将没有任何折扣。

请你返回一个数组,数组中第 i 个元素是折扣后你购买商品 i 最终需要支付的价格。

示例 1:

输入:prices = [8,4,6,2,3]
输出:[4,2,4,2,3]
解释:
商品 0 的价格为 price[0]=8 ,你将得到 prices[1]=4 的折扣,所以最终价格为 8 - 4 = 4 。
商品 1 的价格为 price[1]=4 ,你将得到 prices[3]=2 的折扣,所以最终价格为 4 - 2 = 2 。
商品 2 的价格为 price[2]=6 ,你将得到 prices[3]=2 的折扣,所以最终价格为 6 - 2 = 4 。
商品 3 和 4 都没有折扣。

示例 2:

输入:prices = [1,2,3,4,5]
输出:[1,2,3,4,5]
解释:在这个例子中,所有商品都没有折扣。

示例 3:

输入:prices = [10,1,1,6]
输出:[9,0,1,6]

提示:

  • 1 <= prices.length <= 500
  • 1 <= prices[i] <= 10^3
class Solution {
    public int[] finalPrices(int[] prices) {
        /** 题目让我们找到i右边最近的满足prices[j]<=prices[i]的元素
            相当于 找到j左边最近的比起本身更大的元素
         */
        Deque<Integer> st = new ArrayDeque<>();
        int n = prices.length;
        int[] ans = new int[n];
        for (int i = 0; i < n; i++) {
            //遍历到的prices[i]<=栈顶元素 弹他
            while (!st.isEmpty() && prices[i] <= prices[st.peekLast()]) {
                int temp = st.peekLast();
                st.removeLast();
                ans[temp] = prices[temp] - prices[i];
            }
            st.add(i);
            ans[i] = prices[i];
        }
        return ans;
    }
}

901 股票价格跨度

901. 股票价格跨度 - 力扣(LeetCode)

设计一个算法收集某些股票的每日报价,并返回该股票当日价格的 跨度

当日股票价格的 跨度 被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

  • 例如,如果未来 7 天股票的价格是 [100,80,60,70,60,75,85],那么股票跨度将是 [1,1,1,2,1,4,6]

实现 StockSpanner 类:

  • StockSpanner() 初始化类对象。
  • int next(int price) 给出今天的股价 price ,返回该股票当日价格的 跨度

示例:

输入:
["StockSpanner", "next", "next", "next", "next", "next", "next", "next"]
[[], [100], [80], [60], [70], [60], [75], [85]]
输出:
[null, 1, 1, 1, 2, 1, 4, 6]

解释:
StockSpanner stockSpanner = new StockSpanner();
stockSpanner.next(100); // 返回 1
stockSpanner.next(80);  // 返回 1
stockSpanner.next(60);  // 返回 1
stockSpanner.next(70);  // 返回 2
stockSpanner.next(60);  // 返回 1
stockSpanner.next(75);  // 返回 4 ,因为截至今天的最后 4 个股价 (包括今天的股价 75) 都小于或等于今天的股价。
stockSpanner.next(85);  // 返回 6

提示:

  • 1 <= price <= 105
  • 最多调用 next 方法 104
class StockSpanner {
    private final Deque<int[]> stack = new ArrayDeque<>();
    private int curDay = -1;

    public StockSpanner() {
        //无需判断栈为空
        stack.push(new int[]{-1, Integer.MAX_VALUE});
    }
    
    public int next(int price) {
        while (price >= stack.peek()[1]) {
            stack.pop();
        }
        int ans = ++curDay - stack.peek()[0];
        stack.push(new int[]{curDay, price});
        return ans;
    }
}

/**
 * Your StockSpanner object will be instantiated and called as such:
 * StockSpanner obj = new StockSpanner();
 * int param_1 = obj.next(price);
 */

1019 链表中的下一个更大节点

1019. 链表中的下一个更大节点 - 力扣(LeetCode)

给定一个长度为 n 的链表 head

对于列表中的每个节点,查找下一个 更大节点 的值。也就是说,对于每个节点,找到它旁边的第一个节点的值,这个节点的值 严格大于 它的值。

返回一个整数数组 answer ,其中 answer[i] 是第 i 个节点( 从1开始 )的下一个更大的节点的值。如果第 i 个节点没有下一个更大的节点,设置 answer[i] = 0

示例 1:

数据结构与算法(Java) -单调队列单调栈题单_第2张图片

输入:head = [2,1,5]
输出:[5,5,0]

示例 2:

数据结构与算法(Java) -单调队列单调栈题单_第3张图片

输入:head = [2,7,4,3,5]
输出:[7,0,5,5,0]

提示:

  • 链表中节点数为 n
  • 1 <= n <= 104
  • 1 <= Node.val <= 109
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public int[] nextLargerNodes(ListNode head) {
        int n = 0;
        for (ListNode cur = head; cur != null; cur = cur.next) {
            n++;
        }
        int[] ans = new int[n];
        Deque<Integer> st = new ArrayDeque<>();// 保存节点下标
        int i = 0;
        for (ListNode cur = head; cur != null; cur = cur.next) {
            while (!st.isEmpty() && ans[st.peek()] < cur.val) {
                ans[st.pop()] = cur.val;// 更新答案
            }
            st.push(i);
            ans[i++] = cur.val;
        }
        for (var index : st) {
            ans[index] = 0;
        }
        return ans;
    }
}

84 柱状图中最大的矩形

84. 柱状图中最大的矩形 - 力扣(LeetCode)

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

数据结构与算法(Java) -单调队列单调栈题单_第4张图片

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

数据结构与算法(Java) -单调队列单调栈题单_第5张图片

输入: heights = [2,4]
输出: 4

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104
class Solution {
    public int largestRectangleArea(int[] heights) {
        int ans = 0;
        Deque<Integer> st = new ArrayDeque<>();
        int n = heights.length;
        //l[i]为左边最近的比其小的下标 r[i]为右边最近的比其小的下标
        int[] l = new int[n], r = new int[n];
        Arrays.fill(l, -1);// 初始化-1
        Arrays.fill(r, n);// 初始化n
        for (int i = 0; i < n; i++) {
            while (!st.isEmpty() && heights[i] < heights[st.peekLast()]) {
                r[st.pollLast()] = i;
            }
            st.addLast(i);
        }
        st.clear();
        for (int i = n - 1; i >= 0; i--) {
            while (!st.isEmpty() && heights[i] < heights[st.peekLast()]) {
                l[st.pollLast()] = i;
            }
            st.addLast(i);
        }
        for (int i = 0; i < n; i++) {
            int h = heights[i];
            ans = Math.max(ans, (r[i] - l[i] - 1) * h);
        }
        return ans;
    }
}

你可能感兴趣的:(java,算法,数据结构,笔记,leetcode)