Leetcode刷题 2021.01.29
Leetcode950 按递增顺序显示卡牌
牌组中的每张卡牌都对应有一个唯一的整数。你可以按你想要的顺序对这套卡片进行排序。
最初,这些卡牌在牌组里是正面朝下的(即,未显示状态)。
现在,重复执行以下步骤,直到显示所有卡牌为止:
从牌组顶部抽一张牌,显示它,然后将其从牌组中移出。
如果牌组中仍有牌,则将下一张处于牌组顶部的牌放在牌组的底部。
如果仍有未显示的牌,那么返回步骤 1。否则,停止行动。
返回能以递增顺序显示卡牌的牌组顺序。
答案中的第一张牌被认为处于牌堆顶部。
乍看之下好像看不出什么规律,所以逆向思维反着来,这样就比较简单了,就是把每一次的队尾放到队头去然后再加上一个新的元素就可以了。java里可以使用LinkedList进行模拟。
class Solution {
public int[] deckRevealedIncreasing(int[] deck) {
LinkedList<Integer> queue = new LinkedList<>();
//先排下序
Arrays.sort(deck);
//倒着模拟,每次将队尾放到对头去,然后在队头加入一个新的元素
for(int i = deck.length - 1; i >= 0; i--){
if (!queue.isEmpty()){
queue.offerFirst(queue.pollLast());
}
queue.offerFirst(deck[i]);
}
//返回数组
int[] res = new int[deck.length];
int i = 0;
for(int ele : queue){
res[i++] = ele;
}
return res;
}
}
Leetcode1631 最小体力消耗路径
你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。
一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。
请你返回从左上角走到右下角的最小 体力消耗值 。
拿到这种题只会无脑dfs,然后直接TLE。看到题解说这道题也算是比较经典的题目了,还是题刷的不够多啊。有最小最大之类的,可以看看是否能用二分查找找到一个值。这题可以二分查找加搜索,或者使用并查集。
class Solution {
int[][] directions = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public int minimumEffortPath(int[][] heights) {
int m = heights.length, n = heights[0].length;
int l = 0, r = 1000000;
//二分查找,看看给定阈值能否有通路
while (l < r){
int mid = l + (r - l) / 2;
boolean[][] isVisited = new boolean[m][n];
if (dfs(heights, 0, 0, mid, isVisited)){
r = mid;
}else{
l = mid + 1;
}
}
return l;
}
//正常dfs模板
private boolean dfs(int[][] heights, int i, int j, int target, boolean[][] isVisited){
if (i == heights.length - 1 && j == heights[0].length - 1) return true;
isVisited[i][j] = true;
for(int k = 0; k < directions.length; k++){
int dx = i + directions[k][0], dy = j + directions[k][1];
if (dx >= 0 && dx < heights.length && dy >= 0 && dy < heights[0].length && !isVisited[dx][dy] && Math.abs(heights[i][j] - heights[dx][dy]) <= target){
if (dfs(heights, dx, dy, target, isVisited)) return true;
}
}
return false;
}
public int minimumEffortPath(int[][] heights) {
int m = heights.length, n = heights[0].length;
List<Edge> edges = new ArrayList<>();
//使用并查集,把矩阵的元素看成是图中的点,从小到大加入权值边,如果家了某个边连通的话,就是答案
//构建邻接权值边
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if (i + 1 < m) edges.add(new Edge(i * n + j, (i + 1) * n + j, Math.abs(heights[i][j] - heights[i + 1][j])));
if (j + 1 < n) edges.add(new Edge(i * n + j, i * n + j + 1, Math.abs(heights[i][j] - heights[i][j + 1])));
}
}
//对边从小到大进行排序
Collections.sort(edges, (x, y) -> (x.val - y.val));
UnionFind uf = new UnionFind(m * n);
//如果加了某个边连通的话,就是答案
for(Edge edge : edges){
int i = edge.i, j = edge.j, val = edge.val;
uf.union(i, j);
if (uf.find(0) == uf.find(m * n - 1)){
return val;
}
}
return 0;
}
//并查集模板
class UnionFind{
int[] parent;
public UnionFind(int n){
parent = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
public int find(int i){
if (parent[i] == i){
return i;
}
return parent[i] = find(parent[i]);
}
public void union(int i, int j){
int root1 = find(i);
int root2 = find(j);
if (root1 == root2) return;
parent[root1] = root2;
}
}
class Edge{
int i;
int j;
int val;
public Edge(int i, int j, int val){
this.i = i;
this.j = j;
this.val = val;
}
}
Leetcode974 和可被 K 整除的子数组
给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续、非空)子数组的数目。
其实好像还是不是很懂同余定理,看题解吧,这种看能否被k整除的题基本就是这样了。
class Solution {
//同余定理
public int subarraysDivByK(int[] A, int K) {
int n = A.length;
int[] map = new int[K];
map[0]++;
int res = 0, sum = 0;
for(int i = 0; i < n; i++){
sum += A[i];
//java里负数求余数会有问题,转换一下,记住就行了
int mod = (sum % K + K) % K;
res += map[mod];
map[mod]++;
}
return res;
}
}