「这是我参与2022首次更文挑战的第36天,活动详情查看:2022首次更文挑战」。
LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode
901. 股票价格跨度
编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。
今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。
示例:
输入: ["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出: [null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1,
S.next(80) 被调用并返回 1,
S.next(60) 被调用并返回 1,
S.next(70) 被调用并返回 2,
S.next(60) 被调用并返回 1,
S.next(75) 被调用并返回 4,
S.next(85) 被调用并返回 6。
注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。
提示:
- 调用
StockSpanner.next(int price)时,将有1 <= price <= 10^5。 - 每个测试用例最多可以调用
10000次StockSpanner.next。 - 在所有测试用例中,最多调用
150000次StockSpanner.next。 - 此问题的总时间限制减少了 50%。
PS:
做两个栈,一个代表价格,一个代表权重(也就是连续日)
这个栈我们将他作为单调增栈
当栈不为空的时候,我们先遍历栈内的元素,只要是小于等于当前价格的,都拿出来,把权值添加到w
当栈内循环完以后,证明已经遍历完所有的符合条件的个数
class StockSpanner {
Stack<Integer> prices, weights;
public StockSpanner() {
prices = new Stack();
weights = new Stack();
}
public int next(int price) {
int w = 1;
while (!prices.isEmpty() && prices.peek() <= price) {
prices.pop();
w += weights.pop();
}
prices.push(price);
weights.push(w);
return w;
}
}
904. 水果成篮
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
示例 1:
输入: fruits = [1,2,1]
输出: 3
解释: 可以采摘全部 3 棵树。
示例 2:
输入: fruits = [0,1,2,2]
输出: 3
解释: 可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入: fruits = [1,2,3,2,2]
输出: 4
解释: 可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。
示例 4:
输入: fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出: 5
解释: 可以采摘 [1,2,1,1,2] 这五棵树。
提示:
1 <= fruits.length <= 1050 <= fruits[i] < fruits.length
class Solution {
public int totalFruit(int[] tree) {
// 可以开始的树
List<Integer> blockLefts = new ArrayList();
// 除了第一个,后面的每一个如果相邻的不一样 才可以添加到里面
for (int i = 0; i < tree.length; ++i)
if (i == 0 || tree[i-1] != tree[i])
blockLefts.add(i);
// 添加树的长度,为了方便循环
blockLefts.add(tree.length);
int ans = 0, i = 0;
search: while (true) {
// types : 树的类型,set不记录重复的值
// weight : 记录树的总数(因为当前blockleft记录的是下标,所以我们这里直接用下标相减,就是不同的两个树的下标差值,也就是距离,如果这中间超过种类数量了,直接可以跳过循环)
Set<Integer> types = new HashSet();
int weight = 0;
for (int j = i; j < blockLefts.size() - 1; ++j) {
// 添加类型到type
types.add(tree[blockLefts.get(j)]);
weight += blockLefts.get(j+1) - blockLefts.get(j);
// 如果有三个类型直接否定,然后从当前位置的上一个开始
if (types.size() >= 3) {
i = j - 1;
continue search;
}
// 记录最大值
ans = Math.max(ans, weight);
}
break;
}
return ans;
}
}
905. 按奇偶排序数组
给定一个非负整数数组 A,返回一个数组,在该数组中, A 的所有偶数元素之后跟着所有奇数元素。
你可以返回满足此条件的任何数组作为答案。
示例:
输入: [3,1,2,4]
输出: [2,4,3,1]
输出 [4,2,3,1],[2,4,1,3] 和 [4,2,1,3] 也会被接受。
提示:
1 <= A.length <= 50000 <= A[i] <= 5000
PS:
用一个下标记录新数组
先把所有偶数放到数组里面,跟着下标++
然后放置奇数
一个数 & 1 == 0 证明这个数是偶数,二进制最后一位是0就是偶数
class Solution {
public int[] sortArrayByParity(int[] nums) {
int[] temp = new int[nums.length];
int index = 0;
for (int i : nums) {
if((i & 1) == 0) {
temp[index++] = i;
}
}
for (int i : nums) {
if((i & 1) == 1) {
temp[index++] = i;
}
}
return temp;
}
}
907. 子数组的最小值之和
难度中等311收藏分享切换为英文接收动态反馈
给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。
由于答案可能很大,因此 返回答案模 10^9 + 7 。
示例 1:
输入: arr = [3,1,2,4]
输出: 17
解释: 子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。
示例 2:
输入: arr = [11,81,94,43,3]
输出: 444
提示:
1 <= arr.length <= 3 * 1041 <= arr[i] <= 3 * 104
class Solution {
/*
如果当前坐标左右找最小值都可以等于的话,会出现重复的现象
eg:12121212
121 2 1212
12121 2 12
这种情况就会出现重复
左面允许等于
121 2 12 12
12121 2 12
没有重复现象
*/
public int sumSubarrayMins(int[] arr) {
int n = arr.length;
Deque<Integer> deque1 = new ArrayDeque<Integer>();
int[] prev = new int[n];
for (int i = 0; i < n; i++) {
while (!deque1.isEmpty() && arr[i] < arr[deque1.peekLast()]) {
deque1.removeLast();
}
prev[i] = deque1.isEmpty() ? -1 :deque1.peekLast();
deque1.addLast(i);
}
Deque<Integer> deque2 = new ArrayDeque<Integer>();
int[] next = new int[n];
for (int i = n - 1; i >= 0; i--) {
while (!deque2.isEmpty() && arr[i] <= arr[deque2.peekLast()] ) {
deque2.removeLast();
}
next[i] = deque2.isEmpty() ? n : deque2.peekLast();
deque2.addLast(i);
}
long res = 0;
for (int i = 0; i < n; i++) {
res += (long)(i - prev[i]) * (next[i] - i) % 1_000_000_007 * arr[i] % 1_000_000_007;
res %= 1_000_000_007;
}
return (int) res;
}
}
908. 最小差值 I
给你一个整数数组 nums,和一个整数 k 。
在一个操作中,您可以选择 0 <= i < nums 的任何索引 i 。将 nums[i] 改为 nums[i] + x ,其中 x 是一个范围为 [-k, k] 的整数。对于每个索引 i ,最多 只能 应用 一次 此操作。
nums 的 分数 是 nums 中最大和最小元素的差值。
在对nums中的每个索引最多应用一次上述操作后,返回 nums 的最低 分数 。
示例 1:
输入: nums = [1], k = 0
输出: 0
解释: 分数是 max(nums) - min(nums) = 1 - 1 = 0。
示例 2:
输入: nums = [0,10], k = 2
输出: 6
解释: 将 nums 改为 [2,8]。分数是 max(nums) - min(nums) = 8 - 2 = 6。
示例 3:
输入: nums = [1,3,6], k = 3
输出: 0
解释: 将 nums 改为 [4,4,4]。分数是 max(nums) - min(nums) = 4 - 4 = 0。
提示:
1 <= nums.length <= 1040 <= nums[i] <= 1040 <= k <= 104
PS:
因为这里是找的最大最小值的差
所以我们肯定是找最大的和最小的进行操作,其他相对小的值,如果极端值能处理,较小值肯定能处理
最大最小的差肯定能代表其他的操作
我们把最大值-k,最小值+k 然后看这两个的差,换句话说是最大值和最小值的差 在减去两个k
这里要注意有可能最大值和最小值的差小于两个k,就会造成最大值最小值的差为0
class Solution {
public int smallestRangeI(int[] nums, int k) {
int max = nums[0], min = nums[0];
for (int i :nums) {
max = Math.max(max,i);
min = Math.min(min, i);
}
return Math.max(0,max - min - 2*k);
}
}
909. 蛇梯棋
给你一个大小为 n x n 的整数矩阵 board ,方格按从 1 到 n2 编号,编号遵循 转行交替方式 ****,从左下角开始 (即,从 board[n - 1][0] 开始)每一行交替方向。
玩家从棋盘上的方格 1 (总是在最后一行、第一列)开始出发。
每一回合,玩家需要从当前方格 curr 开始出发,按下述要求前进:
-
选定目标方格
next,目标方格的编号符合范围[curr + 1, min(curr + 6, n2)]。- 该选择模拟了掷 六面体骰子 的情景,无论棋盘大小如何,玩家最多只能有 6 个目的地。
-
传送玩家:如果目标方格
next处存在蛇或梯子,那么玩家会传送到蛇或梯子的目的地。否则,玩家传送到目标方格next。 -
当玩家到达编号
n2的方格时,游戏结束。
r 行 c 列的棋盘,按前述方法编号,棋盘格中可能存在 “蛇” 或 “梯子”;如果 board[r][c] != -1,那个蛇或梯子的目的地将会是 board[r][c]。编号为 1 和 n2 的方格上没有蛇或梯子。
注意,玩家在每回合的前进过程中最多只能爬过蛇或梯子一次:就算目的地是另一条蛇或梯子的起点,玩家也 不能 继续移动。
- 举个例子,假设棋盘是
[[-1,4],[-1,3]],第一次移动,玩家的目标方格是2。那么这个玩家将会顺着梯子到达方格3,但 不能 顺着方格3上的梯子前往方格4。
返回达到编号为 n2 的方格所需的最少移动次数,如果不可能,则返回 -1。
示例 1:
输入: board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]]
输出: 4
解释:
首先,从方格 1 [第 5 行,第 0 列] 开始。
先决定移动到方格 2 ,并必须爬过梯子移动到到方格 15 。
然后决定移动到方格 17 [第 3 行,第 4 列],必须爬过蛇到方格 13 。
接着决定移动到方格 14 ,且必须通过梯子移动到方格 35 。
最后决定移动到方格 36 , 游戏结束。
可以证明需要至少 4 次移动才能到达最后一个方格,所以答案是 4 。
示例 2:
输入: board = [[-1,-1],[-1,3]]
输出: 1
提示:
n == board.length == board[i].length2 <= n <= 20grid[i][j]的值是-1或在范围[1, n2]内- 编号为
1和n2的方格上没有蛇或梯子
PS;
经典BFS
BFS队列传递的就是路径和步数
只不过这个是如果走到捷径的话,就可以走捷径
只要新地方没有被访问过,就可以BFS访问这个地方
如果遍历到终点,直接返回步数
class Solution {
public int snakesAndLadders(int[][] board) {
int n = board.length;
boolean[] vis = new boolean[n * n + 1];
Queue<int[]> queue = new LinkedList<int[]>();
queue.offer(new int[]{1, 0});
while (!queue.isEmpty()) {
int[] p = queue.poll();
for (int i = 1; i <= 6; ++i) {
int nxt = p[0] + i;
if (nxt > n * n) { // 超出边界
break;
}
int[] rc = id2rc(nxt, n); // 得到下一步的行列
if (board[rc[0]][rc[1]] > 0) { // 存在蛇或梯子
nxt = board[rc[0]][rc[1]];
}
if (nxt == n * n) { // 到达终点
return p[1] + 1;
}
if (!vis[nxt]) {
vis[nxt] = true;
queue.offer(new int[]{nxt, p[1] + 1}); // 扩展新状态
}
}
}
return -1;
}
public int[] id2rc(int id, int n) {
int r = (id - 1) / n, c = (id - 1) % n;
if (r % 2 == 1) {
c = n - 1 - c;
}
return new int[]{n - 1 - r, c};
}
}
910. 最小差值 II
给你一个整数数组 nums,和一个整数 k 。
对于每个下标 i(0 <= i < nums.length),将 nums[i] 变成 ****nums[i] + k 或 nums[i] - k 。
nums 的 分数 是 nums 中最大元素和最小元素的差值。
在更改每个下标对应的值之后,返回 nums 的最小 分数 。
示例 1:
输入: nums = [1], k = 0
输出: 0
解释: 分数 = max(nums) - min(nums) = 1 - 1 = 0 。
示例 2:
输入: nums = [0,10], k = 2
输出: 6
解释: 将数组变为 [2, 8] 。分数 = max(nums) - min(nums) = 8 - 2 = 6 。
示例 3:
输入: nums = [1,3,6], k = 3
输出: 3
解释: 将数组变为 [4, 6, 3] 。分数 = max(nums) - min(nums) = 6 - 3 = 3 。
提示:
1 <= nums.length <= 1040 <= nums[i] <= 1040 <= k <= 104
PS:
这个题的 Ⅰ 版本是添加k绝对值范围内的任意值,当时是只考虑最大值和最小值就可以了
这个题只能是 增减k 所以这里我们需要循环每一个位置,然后把每一个位置的最大值和最小值进行处理,并且记录
每次都与极大值和极小值进行比较
class Solution {
public int smallestRangeII(int[] A, int K) {
int N = A.length;
Arrays.sort(A);
int ans = A[N-1] - A[0];
for (int i = 0; i < A.length - 1; ++i) {
int a = A[i], b = A[i+1];
int high = Math.max(A[N-1] - K, a + K);
int low = Math.min(A[0] + K, b - K);
ans = Math.min(ans, high - low);
}
return ans;
}
}