csdn:https://blog.csdn.net/renweiyi1487/article/details/109276885
题目一: 5546. 按键持续时间最长的键
LeetCode 设计了一款新式键盘,正在测试其可用性。测试人员将会点击一系列键(总计 n
个),每次一个。
给你一个长度为 n 的字符串 keysPressed
,其中 keysPressed[i]
表示测试序列中第 i 个被按下的键。releaseTimes
是一个升序排列的列表,其中 releaseTimes[i]
表示松开第 i 个键的时间。字符串和数组的 下标都从 0 开始 。第 0 个键在时间为 0 时被按下,接下来每个键都 恰好 在前一个键松开时被按下。
测试人员想要找出按键 持续时间最长 的键。第 i 次按键的持续时间为 releaseTimes[i] - releaseTimes[i - 1]
,第 0 次按键的持续时间为 releaseTimes[0]
。
注意,测试期间,同一个键可以在不同时刻被多次按下,而每次的持续时间都可能不同。
请返回按键 持续时间最长 的键,如果有多个这样的键,则返回 按字母顺序排列最大 的那个键。
一次遍历使用额外空间
class Solution {
public char slowestKey(int[] releaseTimes, String keysPressed) {
int n = releaseTimes.length;
// 存储keysPressed[i]的持续时间
int[] div = new int[n];
div[0] = releaseTimes[0];
for (int i = 1; i < n; i++) {
div[i] = releaseTimes[i] - releaseTimes[i-1];
}
// 计算持续时间的最大值
int max = div[0];
// 记录最大值对应的下标
int index = 0;
for (int i = 1; i < n; i++) {
if (div[i] > max) {
max = div[i];
index = i;
} else if (div[i] == max) {
if (keysPressed.charAt(i) > keysPressed.charAt(index))
index = i;
}
}
return keysPressed.charAt(index);
}
}
一次遍历不使用额外空间
class Solution {
public char slowestKey(int[] releaseTimes, String keysPressed) {
int n = releaseTimes.length;
int max = releaseTimes[0];
int index = 0;
for (int i = 1; i < n; i++) {
int time = releaseTimes[i] - releaseTimes[i - 1];
if (time > max) {
max = time;
index = i;
} else if (time == max){
if (keysPressed.charAt(i) > keysPressed.charAt(index))
index = i;
}
}
return keysPressed.charAt(index);
}
}
题目二:
如果一个数列由至少两个元素组成,且每两个连续元素之间的差值都相同,那么这个序列就是 等差数列 。更正式地,数列 s
是等差数列,只需要满足:对于每个有效的 i
, s[i+1] - s[i] == s[1] - s[0]
都成立。
例如,下面这些都是 等差数列 :
1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
下面的数列 不是等差数列 :
1, 1, 2, 5, 7
给你一个由 n
个整数组成的数组 nums
,和两个由 m
个整数组成的数组 l
和 r
,后两个数组表示 m
组范围查询,其中第 i 个查询对应范围 [l[i], r[i]]
。所有数组的下标都是 从 0 开始 的。
返回 boolean
元素构成的答案列表 answer
。如果子数组 nums[l[i]], nums[l[i]+1], ... , nums[r[i]]
可以 重新排列 形成 等差数列 ,answer[i]
的值就是 true
;否则answer[i]
的值就是 false
。
示例:
示例 1:
输入:nums = [4,6,5,9,3,7], l = [0,0,2], r = [2,3,5]
输出:[true,false,true]
解释:
第 0 个查询,对应子数组 [4,6,5] 。可以重新排列为等差数列 [6,5,4] 。
第 1 个查询,对应子数组 [4,6,5,9] 。无法重新排列形成等差数列。
第 2 个查询,对应子数组 [5,9,3,7] 。可以重新排列为等差数列 [3,5,7,9]。
示例 2:
输入:nums = [-12,-9,-3,-12,-6,15,20,-25,-20,-15,-10], l = [0,1,6,4,8,7], r = [4,4,9,7,9,10]
输出:[false,true,false,false,true,true]
暴力法
class Solution {
public List<Boolean> checkArithmeticSubarrays(int[] nums, int[] l, int[] r) {
int m = l.length;
List<Boolean> ans = new ArrayList<>();
for (int i = 0; i < m; i++) {
int[] arr = copy(nums, l[i], r[i]);
Arrays.sort(arr);
ans.add(isEquArr(arr));
}
return ans;
}
private boolean isEquArr(int[] arr) {
int n = arr.length;
if (n <= 2) return true;
int div = (arr[1] - arr[0]);
for (int i = 1; i < arr.length; i++) {
if ((arr[i] - arr[i-1]) != div) {
return false;
}
}
return true;
}
private int[] copy(int[] nums, int left, int right) {
int[] ans = new int[right - left +1];
for (int i = left; i <= right; i++) {
ans[i - left] = nums[i];
}
return ans;
}
}
题目三: 5548. 最小体力消耗路径
链接: https://leetcode-cn.com/problems/path-with-minimum-effort
你准备参加一场远足活动。给你一个二维 rows x columns
的地图 heights ,其中 heights[row][col]
表示格子 (row, col)
的高度。一开始你在最左上角的格子 (0, 0)
,且你希望去最右下角的格子 (rows-1, columns-1)
(注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。
一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。
请你返回从左上角走到右下角的最小 体力消耗值 。
示例:
输入:heights = [[1,2,2],[3,8,2],[5,3,5]]
输出:2
解释:路径 [1,3,5,3,5] 连续格子的差值绝对值最大为 2 。
这条路径比路径 [1,2,2,2,5] 更优,因为另一条路劲差值最大值为 3 。
提示:
- rows == heights.length
- columns == heights[i].length
- 1 <= rows, columns <= 100
- 1 <= heights[i][j] <= 10^6
bfs + 二分查找
由提示可知高度差的范围为[0, 99999],可以通过二分查找查找到达终点的所有路径中的最小体力,每选取一次mid
通过能否能到达终点来调整最小体力值所在的范围。如果能达到终点说明选取的mid
大了所以应该调整右指针,如果选取的mid
到达不了终点说明选取的mid小了应该调整左指针。
class Solution {
// 定义一个方向数组控制移动的方向
int[][] directions = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public int minimumEffortPath(int[][] heights) {
// 地图的行数
int rows = heights.length;
// 地图的列数
int cols = heights[0].length;
// 定义范围用于二分查找注意循环不变量,[l,r] 高度差的范围为 [0, 999999]
int l = 0, r = 999999;
while (l <= r) {
int mid = l + (r - l) / 2; // 防止溢出,这题不会出现,mid表示走这条路当前能提供的最小体力值
// 定义一个辅助数组记录已经走过的格子
boolean[][] seen = new boolean[rows][cols];
// 队列,用于广度优先搜索
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{0,0});
seen[0][0] = true;
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0];
int y = cur[1];
for (int[] dir : directions) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || seen[nx][ny] == true)
continue;
// 下一个位置有效,则计算到下一个位置的所需体力值
// 走到下一步所需要的体力值,小于等于 能提供的最小体力值说明能继续走
if (Math.abs(heights[nx][ny] - heights[x][y]) <= mid) {
queue.add(new int[]{nx,ny});
seen[nx][ny] = true;
}
}
}
// 走到了终点说明当前路径需要的最小体力值范围在[l,mid -1]
if (seen[rows-1][cols - 1]) {
r = mid - 1;
} else { // 没有走到终点说明当前提供的最小体力不够,所以查找值应在[mid+1, r]
l = mid + 1;
}
}
return l;// 也可以是r+1;
}
}
dfs + 二分查找
思路和bfs差不多,代码如下
class Solution {
int[][] dirs = new int[][]{{1,0},{0,1},{-1,0},{0,-1}};
int rows;
int cols;
public int minimumEffortPath(int[][] heights) {
rows = heights.length;
cols = heights[0].length;
int l = 0, r = 999999;
while (l <= r) {
boolean[][] seen = new boolean[rows][cols];
int mid = l + (r - l)/2;
if (dfs(heights, 0, 0, mid, seen)) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return l;
}
// 检测从(x,y)是否能到达终点
private boolean dfs(int[][] heights, int x, int y, int mid , boolean[][] seen) {
// 到达终点
if (x == rows - 1 && y == cols - 1) {
return true;
}
seen[x][y] = true;
for (int[] dir : dirs) {
int nx = x + dir[0];
int ny = y + dir[1];
if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || seen[nx][ny]
|| Math.abs(heights[x][y] - heights[nx][ny]) > mid)
continue;
if (dfs(heights, nx, ny, mid, seen)) {
return true;
}
}
return false;
}
}
题目四: 5156. 矩阵转换后的秩
链接: https://leetcode-cn.com/problems/rank-transform-of-a-matrix/
待补坑。。。