之前一直看重面试中的算法,好像有些不太重视日常的的算法能力积累,所以从2024.7.14我要重拾学习算法的初心,每日不多,在日常常见算法巩固的同时每天再把leetcode的每日一题做了即可,在此记录一下。坚持就是胜利

不定期更新。

24.07.14. 保持城市天际线

链接

public int maxIncreaseKeepingSkyline(int[][] grid) {
int n = grid.length;
int[] colmax = new int[n];
int[] rowmax = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
rowmax[i] = Math.max(rowmax[i], grid[i][j]);
colmax[j] = Math.max(colmax[j], grid[i][j]);
}
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
ans+=Math.min(rowmax[i],colmax[j])-grid[i][j];
}
}
return ans;
}

24.07.15. 账户合并

链接

class Solution {
public static int MAXN=10001;
public static int[] father=new int[MAXN];
public static int emailsCount;
public static void build(){
for(int i=0;i<=emailsCount;i++){
father[i]=i;
}
}

public static int find(int i){
if(father[i]!=i){
father[i]=find(father[i]);
}
return father[i];
}
public static boolean isSameSet(int x, int y) {
return find(x) == find(y);
}

public static void union(int x, int y) {
father[find(x)] = find(y);
}
public List<List<String>> accountsMerge(List<List<String>> accounts) {
Map<String,Integer> emailToIndex=new HashMap<String,Integer>();
Map<String,String> emailToName=new HashMap<String,String>();
emailsCount=0;
for(List<String> account:accounts){
String name=account.get(0);
int size=account.size();
for(int i=1;i<size;i++){
String email=account.get(i);
if(!emailToIndex.containsKey(email)){
emailToIndex.put(email,emailsCount++);
emailToName.put(email,name);
}
}
}
build();
for(List<String> account:accounts){
String firstEmail=account.get(1);
int firstIndex=emailToIndex.get(firstEmail);
int size=account.size();
for(int i=2;i<size;i++){
String nextEmail=account.get(i);
int nextIndex=emailToIndex.get(nextEmail);
union(firstIndex,nextIndex);
}
}
Map<Integer, List<String>> indexToEmails = new HashMap<Integer, List<String>>();
for (String email : emailToIndex.keySet()) {
int index = find(emailToIndex.get(email));
List<String> account = indexToEmails.getOrDefault(index, new ArrayList<String>());
account.add(email);
indexToEmails.put(index, account);
}
List<List<String>> merged = new ArrayList<List<String>>();
for (List<String> emails : indexToEmails.values()) {
Collections.sort(emails);
String name = emailToName.get(emails.get(0));
List<String> account = new ArrayList<String>();
account.add(name);
account.addAll(emails);
merged.add(account);
}
return merged;

}
}

24.07.16. 找到两个数组中的公共元素

链接

public int[] findIntersectionValues(int[] nums1, int[] nums2) {
HashSet<Integer> s1 = new HashSet<>();
for (int x : nums1) {
s1.add(x);
}
HashSet<Integer> s2 = new HashSet<>();
for (int x : nums2) {
s2.add(x);
}

int[] res = new int[2];
for (int x : nums1) {
if (s2.contains(x)) {
res[0]++;
}
}
for (int x : nums2) {
if (s1.contains(x)) {
res[1]++;
}
}
return res;
}

24.07.17.关闭分部的可行集合数目

链接

public int numberOfSets(int n, int maxDistance, int[][] roads) {
int[][] g = new int[n][n];
for (int[] row : g) {
Arrays.fill(row, Integer.MAX_VALUE / 2); // 防止加法溢出
}
for (int[] e : roads) {
int x = e[0];
int y = e[1];
int wt = e[2];
g[x][y] = Math.min(g[x][y], wt);
g[y][x] = Math.min(g[y][x], wt);
}
int ans = 0;
int[][] f = new int[n][n];
next:
for (int s = 0; s < (1 << n); s++) {
for (int i = 0; i < n; i++) {
if ((s >> i & 1) == 1) {
System.arraycopy(g[i], 0, f[i], 0, n);
}
}

// Floyd 算法(只考虑在 s 中的节点)
for (int k = 0; k < n; k++) {
if ((s >> k & 1) == 0) continue;
for (int i = 0; i < n; i++) {
if ((s >> i & 1) == 0) continue;
for (int j = 0; j < n; j++) {
f[i][j] = Math.min(f[i][j], f[i][k] + f[k][j]);
}
}
}
// 判断保留的节点之间的最短路是否均不超过 maxDistance
for (int i = 0; i < n; i++) {
if ((s >> i & 1) == 0) continue;
for (int j = 0; j < i; j++) {
if ((s >> j & 1) == 1 && f[i][j] > maxDistance) {
continue next;
}
}
}
ans++;
}
return ans;
}

24.07.18.访问消失节点的最少时间

链接

public int[] minimumTime(int n, int[][] edges, int[] disappear) {
List<int[]>[] g = new ArrayList[n]; // 稀疏图用邻接表
Arrays.setAll(g, i -> new ArrayList<>());
for (int[] e : edges) {
int x = e[0];
int y = e[1];
int wt = e[2];
g[x].add(new int[]{y, wt});
g[y].add(new int[]{x, wt});
}
int[] dis = new int[n];
Arrays.fill(dis, -1);
dis[0] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (a[0] - b[0]));
pq.offer(new int[]{0, 0});
while(!pq.isEmpty()){
int[] p=pq.poll();
int dx=p[0];
int x=p[1];
if(dx>dis[x]){
continue;
}
for (int[] e : g[x]) {
int y = e[0];
int newDis = dx + e[1];
if (newDis < disappear[y] && (dis[y] < 0 || newDis < dis[y])) {
dis[y] = newDis; // 更新 x 的邻居的最短路
pq.offer(new int[]{newDis, y});
}
}
}
return dis;
}

24.07.19.得到更多分数的最少关卡数目

链接

public int minimumLevels(int[] possible) {
int n = possible.length;
int tot = 0;
for (int x : possible) {
tot += x == 1 ? 1 : -1;
}
int pre=0;
for(int i=0;i<n-1;i++){
pre+=possible[i]==1?1:-1;
if(2*pre>tot){
return i+1;
}
}
return -1;
}

24.07.20.将石头分散到网格图的最少移动次数

链接

public int minimumMoves(int[][] grid) {
return dfs(grid, 0, 0);
}
private int dfs(int[][] grid, int x, int y) {
if(x >= 3) {
return 0;
}
if(y >= 3) {
return dfs(grid, x + 1, 0);
}
if(grid[x][y] != 0) {
return dfs(grid, x, y + 1);
}
int res = Integer.MAX_VALUE;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if(i == x && j == y) {
continue;
}
if(grid[i][j] <= 1) {
continue;
}
grid[i][j] -= 1;
res = Math.min(res, dfs(grid, x, y + 1) + Math.abs(i - x) + Math.abs(j - y));
grid[i][j] += 1;
}
}
return res;
}

24.07.21.删除一次得到子数组最大和

链接

public int maximumSum(int[] arr) {
int n=arr.length;
int[][] dp=new int[n][2];
dp[0][0]=arr[0];
dp[0][1]=0;
int ans=arr[0];
for(int i=1;i<n;i++){
dp[i][0]=Math.max(dp[i-1][0],0)+arr[i];
dp[i][1]=Math.max(dp[i-1][1]+arr[i],dp[i-1][0]);
ans = Math.max(ans,Math.max(dp[i][0],dp[i][1]));
}
return ans;
}

24.07.22.引爆最多的炸弹

链接

public int maximumDetonation(int[][] bombs) {
int n=bombs.length;
Map<Integer,List<Integer>> edges=new HashMap<>();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i!=j&&isConnected(bombs,i,j)){
edges.putIfAbsent(i,new ArrayList<Integer>());
edges.get(i).add(j);
}
}
}
int res=0;

for(int i=0;i<n;i++){
boolean[] visited =new boolean[n];
int cnt=1;
int[] queue=new int[n];
int l=0,r=0;
queue[r++]=i;
visited[i]=true;
while(l<r){
int cdix = queue[l++];
for(int ndix:edges.getOrDefault(cdix,new ArrayList<>())){
if(visited[ndix]){
continue;
}
++cnt;
queue[r++]=ndix;
visited[ndix]=true;
}
}
res=Math.max(cnt,res);
}
return res;
}
// 判断炸弹 u 能否引爆炸弹 v
public boolean isConnected(int[][] bombs, int u, int v) {
long dx = bombs[u][0] - bombs[v][0];
long dy = bombs[u][1] - bombs[v][1];
return (long) bombs[u][2] * bombs[u][2] >= dx * dx + dy * dy;
}

24.07.23.求出所有子序列的能量和

链接

private Map<Long, Integer> f = new HashMap<>();
private final int mod = (int) 1e9 + 7;
private int[] nums;

public int sumOfPowers(int[] nums, int k) {
Arrays.sort(nums);
this.nums = nums;
return dfs(0, nums.length, k, Integer.MAX_VALUE);
}

private int dfs(int i, int j, int k, int mi) {
if (i >= nums.length) {
return k == 0 ? mi : 0;
}
if (nums.length - i < k) {
return 0;
}
long key = (1L * mi) << 18 | (i << 12) | (j << 6) | k;
if (f.containsKey(key)) {
return f.get(key);
}
int ans = dfs(i + 1, j, k, mi);
if (j == nums.length) {
ans += dfs(i + 1, i, k - 1, mi);
} else {
ans += dfs(i + 1, i, k - 1, Math.min(mi, nums[i] - nums[j]));
}
ans %= mod;
f.put(key, ans);
return ans;
}

24.07.24.重新放置石块

链接

public List<Integer> relocateMarbles(int[] nums, int[] moveFrom, int[] moveTo) {
List<Integer> ans = new ArrayList<Integer>();
Map<Integer, Boolean> mp = new HashMap<Integer, Boolean>();

for (int i = 0; i < nums.length; i++) {
mp.put(nums[i], true);
}

for (int i = 0; i < moveFrom.length; i++) {
mp.remove(moveFrom[i]);
mp.put(moveTo[i], true);
}

for (Map.Entry<Integer, Boolean> entry : mp.entrySet()) {
ans.add(entry.getKey());
}
Collections.sort(ans);
return ans;
}

24.07.25.生成特殊数字的最少操作

链接

public int minimumOperations(String num) {
int n = num.length();
boolean find0 = false, find5 = false;
for (int i = n - 1; i >= 0; --i) {
if (num.charAt(i) == '0' || num.charAt(i) == '5') {
if (find0) {
return n - i - 2;
}
if (num.charAt(i) == '0') {
find0 = true;
} else {
find5 = true;
}
} else if (num.charAt(i) == '2' || num.charAt(i) == '7') {
if (find5) {
return n - i - 2;
}
}
}
if (find0) {
return n - 1;
}
return n;
}

24.07.26.找出分区值

链接

public int findValueOfPartition(int[] nums) {
Arrays.sort(nums);
int res = Integer.MAX_VALUE;
for (int i = 1; i < nums.length; i++) {
res = Math.min(res, nums[i] - nums[i - 1]);
}
return res;
}

24.07.27.满足距离约束且字典序最小的字符串

链接

public String getSmallestString(String s, int k) {
char[] ans = s.toCharArray();
for (int i = 0; i < s.length(); ++i) {
int dis = Math.min(s.charAt(i) - 'a', 'z' - s.charAt(i) + 1);
if (dis <= k) {
ans[i] = 'a';
k -= dis;
} else {
ans[i] -= k;
break;
}
}
return new String(ans);
}

24.07.28.掉落的方块

链接

public List<Integer> fallingSquares(int[][] positions) {
int n = positions.length;
List<Integer> heights = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
int left1 = positions[i][0], right1 = positions[i][0] + positions[i][1] - 1;
int height = positions[i][1];
for (int j = 0; j < i; j++) {
int left2 = positions[j][0], right2 = positions[j][0] + positions[j][1] - 1;
if (right1 >= left2 && right2 >= left1) {
height = Math.max(height, heights.get(j) + positions[i][1]);
}
}
heights.add(height);
}
for (int i = 1; i < n; i++) {
heights.set(i, Math.max(heights.get(i), heights.get(i - 1)));
}
return heights;
}

24.07.29.棒球比赛

链接

public int calPoints(String[] operations) {
Stack<String> stack=new Stack<>();
for (int i = 0; i < operations.length; i++) {
if (operations[i].equals("D")){
stack.push(String.valueOf(Integer.parseInt(stack.peek())*2));
}else if (operations[i].equals("C")){
stack.pop();
} else if (operations[i].equals("+")) {
String pop1 = stack.pop();
String pop2=stack.pop();
stack.push(pop2);
stack.push(pop1);
stack.push(String.valueOf(Integer.parseInt(pop1)+Integer.parseInt(pop2)));
}else {
stack.push(operations[i]);
}
}
int count=0;
for (String s : stack) {
count+=Integer.parseInt(s);
}
return count;
}

24.07.30.双模幂运算

链接

 public List<Integer> getGoodIndices(int[][] variables, int target) {
List<Integer> ans=new ArrayList<>();
for(int i=0;i<variables.length;i++){
int[] v=variables[i];
if(pow(pow(v[0],v[1],10),v[2],v[3])==target){
ans.add(i);
}
}
return ans;
}

public int pow(int x,int n,int mod){
int res=1;
while(n>0){
if(n%2>0){
res=res*x%mod;
}
x=x*x%mod;
n/=2;
}
return res;
}

24.07.31.覆盖所有点的最少矩形数目

链接

public int minRectanglesToCoverPoints(int[][] points, int w) {
Arrays.sort(points,(a,b)->a[0]-b[0]);
int ans=0;
int x2=-1;
for(int[] p:points){
if(p[0]>x2){
ans++;
x2=p[0]+w;
}
}
return ans;
}

24.08.01.心算挑战

链接

public int maxmiumScore(int[] cards, int cnt) {
Arrays.sort(cards);
int s=0;
int n=cards.length;
for(int i=n-cnt;i<n;i++){
s+=cards[i];
}
if(s%2==0){
return s;
}
int x=cards[n-cnt];
int ans= replaceSum(cards,cnt,s,x);
for(int i=n-cnt+1;i<n;i++){
if(cards[i]%2!=x%2){
ans = Math.max(ans, replaceSum(cards, cnt, s, cards[i])); // 替换
break;
}
}
return ans;
}

private int replaceSum(int[] cards, int cnt, int s, int x) {
for (int i = cards.length - cnt - 1; i >= 0; i--) {
if (cards[i] % 2 != x % 2) { // 找到一个最大的奇偶性和 x 不同的数
return s - x + cards[i]; // 用 cards[i] 替换 s
}
}
return 0;
}

24.08.02.直角三角形

链接

public long numberOfRightTriangles(int[][] grid) {
int n=grid[0].length;
int[] colSum = new int[n];
Arrays.fill(colSum, -1); // 提前减一
for (int[] row : grid) {
for (int j = 0; j < n; j++) {
colSum[j] += row[j];
}
}
long ans=0;
for(int[] row:grid){
int rowSum=-1;
for(int x:row){
rowSum+=x;
}
for(int j=0;j<row.length;j++){
if(row[j]==1){
ans+=rowSum*colSum[j];
}
}
}
return ans;
}

24.08.03.正方形中的最多点数

链接

public int maxPointsInsideSquare(int[][] points, String s) {
int[] min1 = new int[26];
Arrays.fill(min1, 1000000001);
int min2 = 1000000001, n = s.length();
for (int i = 0; i < n; ++i) {
int x = points[i][0], y = points[i][1], j = s.charAt(i) - 'a';
int d = Math.max(Math.abs(x), Math.abs(y));
if (d < min1[j]) {
min2 = Math.min(min2, min1[j]);
min1[j] = d;
} else if (d < min2) {
min2 = d;
}
}
int res = 0;
for (int d : min1) {
if (d < min2) {
++res;
}
}
return res;
}

24.08.04.另一棵树的子树

链接

public boolean isSubtree(TreeNode s, TreeNode t) {
if (t == null) return true; // t 为 null 一定都是 true
if (s == null) return false; // 这里 t 一定不为 null, 只要 s 为 null,肯定是 false
return isSubtree(s.left, t) || isSubtree(s.right, t) || isSameTree(s,t);
}

/**
* 判断两棵树是否相同
*/
public boolean isSameTree(TreeNode s, TreeNode t){
if (s == null && t == null) return true;
if (s == null || t == null) return false;
if (s.val != t.val) return false;
return isSameTree(s.left, t.left) && isSameTree(s.right, t.right);
}

24.08.05.不含连续1的非负整数

链接

char s[];
int dp[][];

public int findIntegers(int n) {
s = Integer.toBinaryString(n).toCharArray();
var m = s.length;
dp = new int[m][2];
for (var i = 0; i < m; i++) Arrays.fill(dp[i], -1);
return f(0, false, true);
}

int f(int i, boolean pre1, boolean isLimit) {
if (i == s.length) return 1;
if (!isLimit && dp[i][pre1 ? 1 : 0] >= 0) return dp[i][pre1 ? 1 : 0];
var up = isLimit ? s[i] - '0' : 1;
var res = f(i + 1, false, isLimit && up == 0); // 填 0
if (!pre1 && up == 1) res += f(i + 1, true, isLimit); // 填 1
if (!isLimit) dp[i][pre1 ? 1 : 0] = res;
return res;
}

24.08.06.找出所有稳定的二进制数组 I

public int numberOfStableArrays(int zero, int one, int limit) {
final long MOD = 1000000007;
long[][][] dp = new long[zero + 1][one + 1][2];
for (int i = 0; i <= Math.min(zero, limit); i++) {
dp[i][0][0] = 1;
}
for (int j = 0; j <= Math.min(one, limit); j++) {
dp[0][j][1] = 1;
}
for (int i = 1; i <= zero; i++) {
for (int j = 1; j <= one; j++) {
if (i > limit) {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1] - dp[i - limit - 1][j][1];
} else {
dp[i][j][0] = dp[i - 1][j][0] + dp[i - 1][j][1];
}
dp[i][j][0] = (dp[i][j][0] % MOD + MOD) % MOD;
if (j > limit) {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0] - dp[i][j - limit - 1][0];
} else {
dp[i][j][1] = dp[i][j - 1][1] + dp[i][j - 1][0];
}
dp[i][j][1] = (dp[i][j][1] % MOD + MOD) % MOD;
}
}
return (int) ((dp[zero][one][0] + dp[zero][one][1]) % MOD);
}

24.08.07.找出所有稳定的二进制数组 II

static final int MOD = 1000000007;
int[][][] memo;
int limit;

public int numberOfStableArrays(int zero, int one, int limit) {
this.memo = new int[zero + 1][one + 1][2];
for (int i = 0; i <= zero; i++) {
for (int j = 0; j <= one; j++) {
Arrays.fill(memo[i][j], -1);
}
}
this.limit = limit;
return (dp(zero, one, 0) + dp(zero, one, 1)) % MOD;
}

public int dp(int zero, int one, int lastBit) {
if (zero == 0) {
return (lastBit == 0 || one > limit) ? 0 : 1;
} else if (one == 0) {
return (lastBit == 1 || zero > limit) ? 0 : 1;
}

if (memo[zero][one][lastBit] == -1) {
int res = 0;
if (lastBit == 0) {
res = (dp(zero - 1, one, 0) + dp(zero - 1, one, 1))% MOD;
if (zero > limit) {
res = (res - dp(zero - limit - 1, one, 1) + MOD) % MOD;
}
} else {
res = (dp(zero, one - 1, 0) + dp(zero, one - 1, 1)) % MOD;
if (one > limit) {
res = (res - dp(zero, one - limit - 1, 0) + MOD) % MOD;
}
}
memo[zero][one][lastBit] = res % MOD;
}
return memo[zero][one][lastBit];
}

24.08.08.找出与数组相加的整数 I

24.08.09.找出与数组相加的整数 II

链接

public int minimumAddedInteger(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;
Arrays.sort(nums1);
Arrays.sort(nums2);
for(int i:new int[]{2,1,0}){
int left=i+1,right=1;
while(left<m&&right<n){
if(nums1[left]-nums2[right]==nums1[i] - nums2[0]){
++right;
}
++left;
}
if(right==n){
return nums2[0]-nums1[i];
}
}
return 0;

}

24.08.10.找到Alice和Bob可以相遇的建筑

链接

class Solution {
int[] zd;

public int[] leftmostBuildingQueries(int[] heights, int[][] queries) {
int n = heights.length;
zd = new int[n * 4];
build(1, n, 1, heights);

int m = queries.length;
int[] ans = new int[m];
for (int i = 0; i < m; i++) {
int a = queries[i][0];
int b = queries[i][1];
if (a > b) {
int temp = a;
a = b;
b = temp;
}

if (a == b || heights[a] < heights[b]) {
ans[i] = b;
continue;
}

ans[i] = query(b + 1, heights[a], 1, n, 1) - 1;
}
return ans;
}

public void build(int l, int r, int rt, int[] heights) {
if (l == r) {
zd[rt] = heights[l - 1];
return;
}

int mid = (l + r) >> 1;
build(l, mid, rt << 1, heights);
build(mid + 1, r, rt << 1 | 1, heights);
zd[rt] = Math.max(zd[rt << 1], zd[rt << 1 | 1]);
}

public int query(int pos, int val, int l, int r, int rt) {
if (val >= zd[rt]) {
return 0;
}

if (l == r) {
return l;
}

int mid = (l + r) >> 1;
if (pos <= mid) {
int res = query(pos, val, l, mid, rt << 1);
if (res != 0) {
return res;
}
}
return query(pos, val, mid + 1, r, rt << 1 | 1);
}
}

24.08.11.不相交的线

链接

public int maxUncrossedLines(int[] nums1, int[] nums2) {
int n=nums1.length;
int m=nums2.length;
int[][] dp=new int[n+1][m+1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(nums1[i-1]==nums2[j-1]){
dp[i][j]=dp[i-1][j-1]+1;
}else{
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[n][m];
}

24.08.12.实现一个魔法字典

链接

class MagicDictionary {
int N = 100 * 100, M = 26, idx = 0;
int[][] tr = new int[N][M];
boolean[] isEnd = new boolean[N * M];
void add(String s) {
int p = 0;
for (int i = 0; i < s.length(); i++) {
int u = s.charAt(i) - 'a';
if (tr[p][u] == 0) tr[p][u] = ++idx;
p = tr[p][u];
}
isEnd[p] = true;
}
boolean query(String s, int idx, int p, int limit) {
if (limit < 0) return false;
if (idx == s.length()) return isEnd[p] && limit == 0;
int u = s.charAt(idx) - 'a';
for (int i = 0; i < 26; i++) {
if (tr[p][i] == 0) continue;
if (query(s, idx + 1, tr[p][i], i == u ? limit : limit - 1)) return true;
}
return false;
}
public void buildDict(String[] ss) {
for (String s : ss) add(s);
}
public boolean search(String s) {
return query(s, 0, 0, 1);
}
}

24.08.13.特殊数组I

链接

public boolean isArraySpecial(int[] nums) {
if(nums.length==1){
return true;
}
for(int i=1;i<nums.length;i++){
if((nums[i]+nums[i-1])%2==0){
return false;
}
}
return true;
}