当前位置:   article > 正文

力扣刷题篇之每日一题(2023年11月完成)_力扣 集中刷题

力扣 集中刷题

前言

想着每天都要刷每日一题的,但每次刷过了也没啥留下的,之后也容易忘,不如记录下来,一些知识,解题技巧,有趣的,碎碎念的。。。工作日就每日都更新,周末的题可能会留到周一更新。

每日一题

1

2127. 参加会议的最多员工数 - 力扣(LeetCode)

第一天就难度升级

 

这题还是很好看懂的,这个i人啊必须有她喜欢的人favorite[i]坐到她的左右两边(一个圈子)她才会参加(返回数加一)。其实就是找到能满足条件的最大的圈子大小。

思路:

拓扑排序来构建邻接表,然后计算圈子的大小。下面是代码的一般流程:

  1. 统计每个人的入度,即有多少个人喜欢这个人。

  2. 将入度为0的人添加到一个队列中。

  3. 通过BFS(广度优先搜索)遍历队列,计算每个人所在的圈子深度。

  4. 统计两种类型的圈子:小圈子和大圈子。

  5. 对于小圈子,它的最大大小为2,然后加上与它相邀请的人的深度之和。

  6. 对于大圈子,计算最大的圈子大小。
  7. 最后返回小圈子和大圈子中的最大值。

通过拓扑排序和BFS来找到满足条件的最大的圈子大小。

这题很有意思,有点人际交往的东西在里面。

  1. class Solution {
  2. public int maximumInvitations(int[] favorite) {
  3. int n = favorite.length;
  4. int[] indegree = new int[n];
  5. for (int i = 0; i < n; i++)
  6. indegree[favorite[i]]++;
  7. int[] que = new int[n];
  8. int read = 0, write = 0;
  9. for (int i = 0; i < n; i++) {
  10. if (indegree[i] == 0)
  11. que[write++] = i;
  12. }
  13. int[] depth = new int[n];
  14. while (read < write) {
  15. int cur = que[read++];
  16. int next = favorite[cur];
  17. depth[next] = Math.max(depth[next], depth[cur] + 1);
  18. if (--indegree[next] == 0)
  19. que[write++] = next;
  20. }
  21. int sumOfSmallRings = 0;
  22. int bigRings = 0;
  23. for (int i = 0; i < n; i++) {
  24. if (indegree[i] > 0) {
  25. int maxSize = 1;
  26. indegree[i] = 0;
  27. for (int j = favorite[i]; j != i; j = favorite[j]) {
  28. maxSize++;
  29. indegree[j] = 0;
  30. }
  31. if (maxSize == 2)
  32. sumOfSmallRings += 2 + depth[i] + depth[favorite[i]];
  33. else
  34. bigRings = Math.max(bigRings, maxSize);
  35. }
  36. }
  37. return Math.max(sumOfSmallRings, bigRings);
  38. }
  39. }

2.

2103. 环和杆 - 力扣(LeetCode)

简单题了,舒服

容易,找出所有集齐 全部三种颜色 环的杆,并返回这种杆的数量。

数组g存储每根杆上套的所有环,时间空间复杂度均为O(n),n为字符串长度/2即环的个数

  1. class Solution {
  2. public int countPoints(String rings) {
  3. // 数组g存储每根杆上套的所有环,时间空间复杂度均为O(n),n为字符串长度/2即环的个数
  4. int n = rings.length() / 2, ans = 0;
  5. List[] g = new ArrayList[10];
  6. for (int i = 0; i < 10; i++) g[i] = new ArrayList<Character>();
  7. for (int i = 0; i < n; i++) g[rings.charAt(2 * i + 1) - '0'].add(rings.charAt(2 * i));
  8. for (int i = 0; i < 10; i++) if (g[i].contains('R') && g[i].contains('G') && g[i].contains('B')) ans++;
  9. return ans;
  10. }
  11. }

3.

117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)

中等题,但还是很简单的

这题其实就是层序遍历,填充他的next指针。

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public int val;
  5. public Node left;
  6. public Node right;
  7. public Node next;
  8. public Node() {}
  9. public Node(int _val) {
  10. val = _val;
  11. }
  12. public Node(int _val, Node _left, Node _right, Node _next) {
  13. val = _val;
  14. left = _left;
  15. right = _right;
  16. next = _next;
  17. }
  18. };
  19. */
  20. class Solution {
  21. public Node connect(Node root) {
  22. if(root==null){
  23. return root;
  24. }
  25. Node cur=root;
  26. while(cur!=null){//遍历完
  27. // dummy是每层的头,pre负责构建,cur是指代当前层
  28. Node dummy=new Node(0);
  29. Node pre = dummy;
  30. while(cur!=null){//到了NULL就说明每层结束了
  31. if(cur.left!=null){
  32. pre.next=cur.left;
  33. pre=pre.next;
  34. }
  35. if(cur.right!=null){
  36. pre.next=cur.right;
  37. pre=pre.next;
  38. }
  39. cur=cur.next;
  40. }
  41. cur=dummy.next;//指向下一层的第一个元素
  42. }
  43. return root;
  44. }
  45. }

4.

421. 数组中两个数的最大异或值 - 力扣(LeetCode)

这个题最容易想到的是O(n^2)的暴力解法。

为了更高效地找到数组中两个数的最大异或值,可以使用更优化的算法,例如基于前缀树(Trie)的方法。这种方法可以将时间复杂度优化到 O(n),从而提高算法的效率。

基于前缀树的方法涉及构建一个 Trie 数据结构,其中存储了数组中的数字的二进制表示。通过遍历数组中的每个数字,并在 Trie 中查找与当前数字的异或结果最大的另一个数字,可以找到数组中两个数的最大异或值。

前缀树的学习资料:

【【数据结构 10】Trie|前缀树|字典树】https://www.bilibili.com/video/BV1Az4y1S7c7?vd_source=70cd9a7d58eaf79ee46e9bddc1d0d53e

  1. class TrieNode {
  2. TrieNode[] children;
  3. TrieNode() {
  4. children = new TrieNode[2];
  5. }
  6. }
  7. class Solution {
  8. public int findMaximumXOR(int[] nums) {
  9. TrieNode root = new TrieNode();
  10. int maxXOR = 0;
  11. for (int num : nums) {
  12. insertNumber(root, num);
  13. int currXOR = findXOR(root, num);
  14. maxXOR = Math.max(maxXOR, currXOR);
  15. }
  16. return maxXOR;
  17. }
  18. private void insertNumber(TrieNode root, int num) {
  19. TrieNode curr = root;
  20. for (int i = 31; i >= 0; i--) {
  21. int bit = (num >> i) & 1;
  22. if (curr.children[bit] == null) {
  23. curr.children[bit] = new TrieNode();
  24. }
  25. curr = curr.children[bit];
  26. }
  27. }
  28. private int findXOR(TrieNode root, int num) {
  29. TrieNode curr = root;
  30. int xor = 0;
  31. for (int i = 31; i >= 0; i--) {
  32. int bit = (num >> i) & 1;
  33. int oppositeBit = bit == 1 ? 0 : 1;
  34. if (curr.children[oppositeBit] != null) {
  35. xor |= (1 << i);
  36. curr = curr.children[oppositeBit];
  37. } else {
  38. curr = curr.children[bit];
  39. }
  40. }
  41. return xor;
  42. }
  43. }

  1. class Solution {
  2. public int findMaximumXOR(int[] nums) {
  3. int max = Integer.MIN_VALUE;
  4. for(int n:nums)
  5. {
  6. max = Math.max(max,n);
  7. }
  8. int highestBitPos = 31 - Integer.numberOfLeadingZeros(max);
  9. int res = 0;
  10. int mask = 0;
  11. Set<Integer> set;
  12. for(int i=highestBitPos;i>=0;i--)
  13. {
  14. set = new HashSet<>();
  15. mask = mask | (1 << i);
  16. int candidateRes = res | (1 << i);
  17. for(int n:nums)
  18. {
  19. n = n & mask;
  20. if(set.contains(candidateRes ^ n))
  21. {
  22. res = candidateRes;
  23. break;
  24. }
  25. set.add(n);
  26. }
  27. }
  28. return res;
  29. }
  30. }

5.

187. 重复的DNA序列 - 力扣(LeetCode)

找长度为10,出现过两次及以上的子序列。

方法一,用哈希表

遍历字符串,把所有十个连续的子序列存进map里,每出现一次value加一,value等于2时就能加入ansl了。

  1. class Solution {
  2. public List<String> findRepeatedDnaSequences(String s) {
  3. int n = s.length();
  4. List<String> ansL = new ArrayList<>();
  5. if (n <= 10) return ansL;
  6. Map<String, Integer> map = new HashMap<>();
  7. for (int i = 0; i <= n - 10; i++) {
  8. String ans = s.substring(i, i + 10);
  9. map.put(ans, map.getOrDefault(ans, 0) + 1);
  10. if (map.get(ans) == 2) ansL.add(ans);
  11. }
  12. return ansL;
  13. }
  14. }

方法二、滑动窗口+哈希表

  1. import java.util.AbstractList;
  2. class Solution {
  3. public static List<String> findRepeatedDnaSequences(String s) {
  4. int n = s.length();
  5. Map<Character, Integer> cMap = new HashMap<>() {{
  6. put('A', 0);
  7. put('C', 1);
  8. put('G', 2);
  9. put('T', 3);
  10. }};
  11. return new AbstractList<String>() {
  12. private final List<String> list = new ArrayList<>();
  13. private final Map<Integer, Integer> map = new HashMap<>();
  14. @Override
  15. public String get(int index) {
  16. init();
  17. return list.get(index);
  18. }
  19. @Override
  20. public int size() {
  21. init();
  22. return list.size();
  23. }
  24. void init() {
  25. if (list.isEmpty() && n > 10) {
  26. findRepeatedDnaSequences();
  27. }
  28. }
  29. void findRepeatedDnaSequences() {
  30. int code = 0;
  31. for (int i = 0; i < 10; i++) {
  32. code = (code << 2) + cMap.get(s.charAt(i));
  33. }
  34. map.put(code, 1);
  35. for (int i = 10; i < n; i++) {
  36. code &= (1 << 18) - 1;
  37. code = (code << 2) + cMap.get(s.charAt(i));
  38. int count = map.getOrDefault(code, 0);
  39. if (count == 1) {
  40. list.add(s.substring(i - 9, i + 1));
  41. }
  42. map.put(code, count + 1);
  43. }
  44. }
  45. };
  46. }
  47. }

6

318. 最大单词长度乘积 - 力扣(LeetCode)

 位运算+暴力枚举:

首先,创建一个整型数组 store,用于存储每个字符串的二进制表示。

然后,遍历字符串数组 words,对每个字符串进行处理。

对于每个字符串 s,遍历其中的每个字符,并计算字符相对于字符 'a' 的偏移量。根据偏移量,使用位运算来设置相应的二进制位。

在内层循环中,首先计算 tag,表示字符在当前字符串的二进制表示中对应位的值。如果 tag 等于 0,说明该字符在当前字符串中是第一次出现,将相应的二进制位设置为 1。

完成字符串的二进制表示后,接下来的嵌套循环用于比较每对字符串的二进制表示。

对于每对字符串的二进制表示,使用位运算的与操作 (store[i] & store[p]),如果结果为 0,说明两个字符串没有相同的字母。

如果两个字符串没有相同的字母,则计算它们的长度乘积 res,并更新最大乘积 ans

最后,返回最大乘积 ans

  1. class Solution {
  2. public int maxProduct(String[] words) {
  3. //思路:直接用位运算
  4. int[] store=new int[words.length];
  5. for(int i=0;i<words.length;i++){//转二进制存储
  6. String s=words[i];
  7. for(Character c : s.toCharArray()){
  8. int tag=(store[i]%(1<<(c-'a'+1)))/(1<<(c-'a'));
  9. if(tag==0){
  10. store[i]=store[i]+(1<<(c-'a'));
  11. }
  12. }
  13. }
  14. int ans=0;
  15. for(int i=0;i<store.length-1;i++){
  16. for(int p=i+1;p<store.length;p++){
  17. if((store[i]&store[p])==0){//与运算,结果为0则说明两者没有相同字母
  18. int res=words[i].length()*words[p].length();
  19. ans=Math.max(res,ans);
  20. }
  21. }
  22. }
  23. return ans;
  24. }
  25. }

 

优化:

首先对每个字符串进行预处理,将其转换为一个包含字符出现情况的位向量(bitmask),出现的位在二进制上的偏移置为1,方便后续计算。然后,对于每一对字符串,如果它们的位向量按位与的结果为0,则它们没有相同的字母,可以计算它们的长度乘积并更新最大乘积。

  1. class Solution {
  2. public int maxProduct(String[] words) {
  3. int n = words.length;
  4. // 预处理,将每个字符串转换为位向量
  5. int[] bitmasks = new int[n];
  6. for (int i = 0; i < n; i++) {
  7. String word = words[i];
  8. int bitmask = 0;
  9. for (char c : word.toCharArray()) {
  10. bitmask |= 1 << (c - 'a'); // 将对应的位设置为1
  11. }
  12. bitmasks[i] = bitmask;
  13. }
  14. int maxProduct = 0;
  15. for (int i = 0; i < n - 1; i++) {
  16. for (int j = i + 1; j < n; j++) {
  17. // 如果两个字符串的位向量按位与的结果为0,则它们没有相同的字母
  18. if ((bitmasks[i] & bitmasks[j]) == 0) {
  19. int product = words[i].length() * words[j].length();
  20. maxProduct = Math.max(maxProduct, product);
  21. }
  22. }
  23. }
  24. return maxProduct;
  25. }
  26. }

7.

2586. 统计范围内的元音字符串数 - 力扣(LeetCode)

简单题,我哭死

判断首尾是不是元音字符串就好了。 

  1. class Solution {
  2. public int vowelStrings(String[] words, int left, int right) {
  3. int res=0;
  4. for(int i=left;i<=right;i++){
  5. String s=words[i];
  6. int n=s.length();
  7. if((s.charAt(0)=='a'||s.charAt(0)=='e'||s.charAt(0)=='i'||s.charAt(0)=='o'||s.charAt(0)=='u') && (s.charAt(n-1)=='a'||s.charAt(n-1)=='e'||s.charAt(n-1)=='i'||s.charAt(n-1)=='o'||s.charAt(n-1)=='u')) res++;
  8. }
  9. return res;
  10. }
  11. }

 

 8

2609. 最长平衡子字符串 - 力扣(LeetCode)

  • 在循环中,初始化变量 a 和 b 为 0,分别表示以 0 和 1 结尾的平衡子字符串的长度。
  • 在第一个 while 循环中,判断索引 idx 是否小于 n,并且字符串 s 在索引 idx 处的字符是否为 '0',如果是则进入循环体。
    • 将 a 自增 1,并且判断 a 是否大于等于 0。
    • 将索引 idx 自增 1。
  • 在第二个 while 循环中,同样判断索引 idx 是否小于 n,并且字符串 s 在索引 idx 处的字符是否为 '1',如果是则进入循环体。
    • 将 b 自增 1,并且判断 b 是否大于等于 0。
    • 将索引 idx 自增 1。
  • 计算当前平衡子字符串的长度,取 a 和 b 的较小值乘以 2,并将结果与 ans 进行比较,取较大值更新 ans
  1. class Solution {
  2. public int findTheLongestBalancedSubstring(String s) {
  3. int n = s.length(), idx = 0, ans = 0;
  4. while (idx < n) {
  5. int a = 0, b = 0;
  6. while (idx < n && s.charAt(idx) == '0' && ++a >= 0) idx++;
  7. while (idx < n && s.charAt(idx) == '1' && ++b >= 0) idx++;
  8. ans = Math.max(ans, Math.min(a, b) * 2);
  9. }
  10. return ans;
  11. }
  12. }

9

2258. 逃离火灾 - 力扣(LeetCode)

bfs,我先CV 

  1. // class Solution {
  2. // // 二分+bfs(预处理+验证路径)
  3. // // 将矩阵可达点预处理为着火时间,后续bfs搜索路径时,到达时间小于着火时间的为可走路径。
  4. // // 可暂停时间的最小值为0,最大值为无穷大,而无穷大状态即火焰扩张完所有可达位置后,
  5. // // 矩阵中仍存在从左上到右下角的安全路径,所以可暂停时间time可设置为火焰完成扩张时间+2,
  6. // // 即ans可能取[0,time]内任何值,在此区间内二分查找不存在可达路径的暂停时间最小值,
  7. // // 判定合法的方式就是对每个时间t在预处理过后的矩阵内搜索是否存在左上到右下的安全路径,
  8. // // 1、若t=0时不可到达目标点,则永远不可达成,返回-1,
  9. // // 2、若t=time,而在time-1时存在安全路径,而火焰完成扩张的时间是time-2,所以火焰不在扩张后仍存在安全路径,是否可达与暂停时间无关,返回1e9,
  10. // // 3、其余情况下则是火扩张至某时刻后目标点不在可达,那么二分求的是最小不可达时间r,r-1即为最大可达目标点的暂停时间。
  11. // int[][] g;
  12. // int m, n;
  13. // int[][] dir = {{-1,0},{0,1},{1,0},{0,-1}};
  14. // public int maximumMinutes(int[][] g) {
  15. // this.g = g;
  16. // m = g.length;
  17. // n = g[0].length;
  18. // Deque<int[]> que = new ArrayDeque<>();
  19. // boolean[][] vis = new boolean[m][n];
  20. // // 预处理矩阵,将所有墙壁点标记为-1,这样就可以用0代表空地,以及大于0的数字代表每个位置开始着火的时间。
  21. // // 将初始火源点存入队列,后续bfs按圈扩张,标记所有
  22. // for(int i = 0; i < m; ++i){
  23. // for(int j = 0; j < n; ++j){
  24. // if(g[i][j] == 2){
  25. // g[i][j] = -1;
  26. // }else if(g[i][j] == 1){
  27. // que.offer(new int[]{i, j});
  28. // vis[i][j] = true;
  29. // }
  30. // }
  31. // }
  32. // int time = 1;
  33. // // bfs分层模拟扩展随时间可被火焰蔓延的区域,在g矩阵标记着火时间点,time最终为火焰铺满所有可蔓延位置所需的时间。
  34. // while(!que.isEmpty()){
  35. // int len = que.size();
  36. // while(--len >= 0){
  37. // int[] p = que.poll();
  38. // for(int k = 0; k < 4; ++k){
  39. // int i = p[0] + dir[k][0], j = p[1] + dir[k][1];
  40. // if(i >= 0 && j >= 0 && i < m && j < n && !vis[i][j] && g[i][j] == 0){
  41. // g[i][j] = time;
  42. // que.offer(new int[]{i, j});
  43. // vis[i][j] = true;
  44. // }
  45. // }
  46. // }
  47. // ++time;
  48. // }
  49. // // 二分找到无法到达的最小暂停时间r,则r-1即为最大可暂停时间。
  50. // int l = 0, r = time;
  51. // while(l < r){
  52. // int mid = l + r >> 1;
  53. // if(!bfs(mid))
  54. // r = mid;
  55. // else
  56. // l = mid + 1;
  57. // }
  58. // // 若直接出发仍无法到达,则-1不可实现;若火焰完成扩张后+1秒仍可到达,因火焰不会在扩张,所以说明后续仍可到达。
  59. // // 否则返回最小无法到达暂停时间r-1即可。
  60. // if(r == 0)
  61. // return -1;
  62. // else if(r == time)
  63. // return 1000000000;
  64. // else
  65. // return r-1;
  66. // }
  67. // // bfs验证在预留t时间的火势状态下,是否能够从左上角走到右下角。
  68. // public boolean bfs(int t){
  69. // ++t;
  70. // boolean[][] vis = new boolean[m][n];
  71. // Deque<int[]> que = new ArrayDeque<>();
  72. // que.offer(new int[]{0,0});
  73. // vis[0][0] = true;
  74. // // 当
  75. // while(!que.isEmpty()){
  76. // int len = que.size();
  77. // while(len-- > 0){
  78. // int[] p = que.poll();
  79. // for(int k = 0; k < 4; ++k){
  80. // int i = p[0] + dir[k][0], j = p[1] + dir[k][1];
  81. // if(i >= 0 && j >= 0 && i < m && j < n && !vis[i][j] && (g[i][j] >= t || g[i][j] == 0)){
  82. // // 到达终点(m-1,n-1)则true。
  83. // if(i == m - 1 && j == n - 1)
  84. // return true;
  85. // if(g[i][j] > t || g[i][j] == 0)
  86. // que.offer(new int[]{i, j});
  87. // vis[i][j] = true;
  88. // }
  89. // }
  90. // }
  91. // // 若中途终点被火蔓延,则后续也无法再到达,直接false。
  92. // if(g[m-1][n-1] == t)
  93. // return false;
  94. // ++t;
  95. // }
  96. // return false;
  97. // }
  98. // }
  99. class Solution {
  100. static int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
  101. public int maximumMinutes(int[][] grid) {
  102. int m = grid.length, n = grid[0].length;
  103. int[][] fire = new int[m][n];
  104. int[][]people = new int[m][n];
  105. for (int i = 0; i < m; i++) {
  106. Arrays.fill(fire[i], -1);
  107. Arrays.fill(people[i], -1);
  108. }
  109. Deque<int[]> que = new ArrayDeque<>();
  110. for (int i = 0; i < m; i++) {
  111. for (int j = 0; j < n; j++) {
  112. if (grid[i][j] == 1) {
  113. fire[i][j] = 0;
  114. que.offer(new int[]{i, j, 0});
  115. }
  116. }
  117. }
  118. while (!que.isEmpty()) {
  119. int[] cur = que.poll();
  120. for (int[] d: directions) {
  121. int nx = cur[0] + d[0], ny = cur[1] + d[1];
  122. if (nx >= 0 && ny >= 0 && nx < m && ny < n && fire[nx][ny] == -1 && grid[nx][ny] == 0) {
  123. fire[nx][ny] = cur[2] + 1;
  124. que.offer(new int[]{nx, ny, cur[2] + 1});
  125. }
  126. }
  127. }
  128. people[0][0] = 0;
  129. que = new ArrayDeque<>();
  130. que.offer(new int[]{0, 0, 0});
  131. while (!que.isEmpty()) {
  132. int[] cur = que.poll();
  133. for (int[] d:directions) {
  134. int nx = cur[0] + d[0], ny = cur[1] + d[1];
  135. if (nx >= 0 && ny >= 0 && nx < m && ny < n && people[nx][ny] == -1 && grid[nx][ny] == 0) {
  136. people[nx][ny] = cur[2] + 1;
  137. if (nx == m - 1 && ny == n - 1) return fire[m - 1][n - 1] == -1 ? (int)1e9 : fire[m - 1][n - 1] - people[m - 1][n - 1] - ((fire[m - 2][n - 1] != fire[m - 1][n - 1] - 1 && people[m - 2][n - 1] == people[m - 1][n - 1] - 1 || fire[m - 1][n - 2] != fire[m - 1][n - 1] - 1 && people[m - 1][n - 2] == people[m - 1][n - 1] - 1) ? 0 : 1);
  138. if (people[nx][ny] >= fire[nx][ny] && fire[nx][ny] != -1) continue;
  139. que.offer(new int[]{nx, ny, cur[2] + 1});
  140. }
  141. }
  142. }
  143. return -1;
  144. }
  145. }

10.

2300. 咒语和药水的成功对数 - 力扣(LeetCode)


 

  1. // // class Solution {
  2. // // public int[] successfulPairs(int[] spells, int[] potions, long success) {
  3. // // int n=spells.length;
  4. // // int[] res=new int[n];
  5. // // for(int i=0;i<n;i++){
  6. // // int cnt=0;
  7. // // for(int j=0;j<potions.length;j++){
  8. // // if(spells[i]*potions[j]>=success) cnt++;
  9. // // }
  10. // // res[i]=cnt;
  11. // // }
  12. // // return res;
  13. // // }
  14. // // }
  15. // class Solution {
  16. // public int[] successfulPairs(int[] spells, int[] potions, long success) {
  17. // Arrays.sort(potions);
  18. // int N = spells.length;
  19. // int M = potions.length;
  20. // int[] ans = new int[N];
  21. // for (int i = 0; i < N; i++) {
  22. // int l = 0;
  23. // int r = M - 1;
  24. // int min = -1;
  25. // while (l <= r) {
  26. // int m = (l + r) >>> 1;
  27. // if ((long) potions[m] * spells[i] >= success) {
  28. // min = m;
  29. // r = m - 1;
  30. // } else {
  31. // l = m + 1;
  32. // }
  33. // }
  34. // if (min != -1) {
  35. // ans[i] = M - min;
  36. // }
  37. // }
  38. // return ans;
  39. // }
  40. // }
  41. class Solution {
  42. static int inf = (int)1e5;
  43. public int[] successfulPairs(int[] spells, int[] potions, long success){
  44. int max = 0;
  45. for(int x : spells){
  46. if(x > max) max = x;
  47. }
  48. int minPotion = (int)Math.min(inf, (success - 1) / max);
  49. int[] count = new int[max + 1];
  50. for(int potion:potions){
  51. if(potion > minPotion){
  52. ++count[(int)((success + potion - 1)/potion)];
  53. }
  54. }
  55. for(int i = 1; i <= max; i++){
  56. count[i] += count[i-1];
  57. }
  58. int n = spells.length;
  59. int[] result = new int[n];
  60. for(int i = 0; i < n; i++){
  61. result[i] = count[spells[i]];
  62. }
  63. return result;
  64. }
  65. }

11

765. 情侣牵手 - 力扣(LeetCode)

使用了贪心算法的思想。从左到右遍历数组,对于每一对夫妻,如果他们不坐在一起(即i位置的配偶不是y),则在后面的位置中找到y的位置j,然后交换i位置的配偶和j位置的人。交换次数加1,继续遍历下一对夫妻。最终返回交换次数。

该方法的时间复杂度为O(n^2),其中n是数组row的长度。

  1. class Solution {
  2. public int minSwapsCouples(int[] row) {
  3. int count = 0; // 交换次数计数器
  4. for (int i = 0; i < row.length; i += 2) {
  5. int x = row[i]; // 当前位置i的夫妻编号
  6. int y = x ^ 1; // x的配偶编号
  7. if (row[i + 1] != y) { // 如果i位置的配偶不是y,则需要进行交换
  8. for (int j = i + 2; j < row.length; j++) {
  9. if (row[j] == y) { // 在后面的位置找到y的位置j
  10. int temp = row[i + 1]; // 交换i位置的配偶和j位置的人
  11. row[i + 1] = row[j];
  12. row[j] = temp;
  13. count++; // 交换次数加1
  14. break;
  15. }
  16. }
  17. }
  18. }
  19. return count; // 返回交换次数
  20. }
  21. }

12

715. Range 模块 - 力扣(LeetCode)

讲实话,我看不懂一点,这个之后还要仔细看看

  1. // class RangeModule {
  2. // TreeMap<Integer, Integer> intervals;
  3. // public RangeModule() {
  4. // intervals = new TreeMap<Integer, Integer>();
  5. // }
  6. // public void addRange(int left, int right) {
  7. // Map.Entry<Integer, Integer> entry = intervals.higherEntry(left);
  8. // if (entry != intervals.firstEntry()) {
  9. // Map.Entry<Integer, Integer> start = entry != null ? intervals.lowerEntry(entry.getKey()) : intervals.lastEntry();
  10. // if (start != null && start.getValue() >= right) {
  11. // return;
  12. // }
  13. // if (start != null && start.getValue() >= left) {
  14. // left = start.getKey();
  15. // intervals.remove(start.getKey());
  16. // }
  17. // }
  18. // while (entry != null && entry.getKey() <= right) {
  19. // right = Math.max(right, entry.getValue());
  20. // intervals.remove(entry.getKey());
  21. // entry = intervals.higherEntry(entry.getKey());
  22. // }
  23. // intervals.put(left, right);
  24. // }
  25. // public boolean queryRange(int left, int right) {
  26. // Map.Entry<Integer, Integer> entry = intervals.higherEntry(left);
  27. // if (entry == intervals.firstEntry()) {
  28. // return false;
  29. // }
  30. // entry = entry != null ? intervals.lowerEntry(entry.getKey()) : intervals.lastEntry();
  31. // return entry != null && right <= entry.getValue();
  32. // }
  33. // public void removeRange(int left, int right) {
  34. // Map.Entry<Integer, Integer> entry = intervals.higherEntry(left);
  35. // if (entry != intervals.firstEntry()) {
  36. // Map.Entry<Integer, Integer> start = entry != null ? intervals.lowerEntry(entry.getKey()) : intervals.lastEntry();
  37. // if (start != null && start.getValue() >= right) {
  38. // int ri = start.getValue();
  39. // if (start.getKey() == left) {
  40. // intervals.remove(start.getKey());
  41. // } else {
  42. // intervals.put(start.getKey(), left);
  43. // }
  44. // if (right != ri) {
  45. // intervals.put(right, ri);
  46. // }
  47. // return;
  48. // } else if (start != null && start.getValue() > left) {
  49. // if (start.getKey() == left) {
  50. // intervals.remove(start.getKey());
  51. // } else {
  52. // intervals.put(start.getKey(), left);
  53. // }
  54. // }
  55. // }
  56. // while (entry != null && entry.getKey() < right) {
  57. // if (entry.getValue() <= right) {
  58. // intervals.remove(entry.getKey());
  59. // entry = intervals.higherEntry(entry.getKey());
  60. // } else {
  61. // intervals.put(right, entry.getValue());
  62. // intervals.remove(entry.getKey());
  63. // break;
  64. // }
  65. // }
  66. // }
  67. // }
  68. class RangeModule {
  69. public Range root;
  70. public RangeModule() {
  71. }
  72. public void addRange(int left, int right) {
  73. root = insert(root,left, right-1);
  74. }
  75. public boolean queryRange(int left, int right) {
  76. return query(root,left, right-1);
  77. }
  78. public void removeRange(int left, int right) {
  79. root = remove(root,left, right-1);
  80. }
  81. static class Range{
  82. public int left, right;
  83. public Range l, r;
  84. public Range(int left,int right){
  85. this.left = left;
  86. this.right = right;
  87. }
  88. }
  89. public Range remove(Range range,int left,int right){
  90. if(range == null) return null;
  91. if(right < range.left)
  92. range.l = remove(range.l, left, right);
  93. else if(left > range.right)
  94. range.r = remove(range.r, left, right);
  95. else if(left <= range.left && right >= range.right){
  96. Range l = remove(range.l, left, right);
  97. Range r = remove(range.r, left, right);
  98. if(l == null)
  99. return r;
  100. else if(r == null)
  101. return l;
  102. else{
  103. if(l.r == null){
  104. l.r = r;
  105. return l;
  106. }else{
  107. Range temp = r;
  108. while(temp.l!=null)
  109. temp=temp.l;
  110. temp.l = l;
  111. return r;
  112. }
  113. }
  114. }else if(left <= range.left){
  115. range.left = right+1;
  116. range.l = remove(range.l, left, right);
  117. }else if(right >= range.right){
  118. range.right = left - 1;
  119. range.r = remove(range.r, left, right);
  120. }else{
  121. Range newR = new Range(right+1, range.right);
  122. Range r = range.r;
  123. range.right = left - 1;
  124. newR.r = r;
  125. range.r = newR;
  126. }
  127. return range;
  128. }
  129. public boolean query(Range range,int left,int right){
  130. if(range == null)
  131. return false;
  132. if(right < range.left)
  133. return query(range.l,left,right);
  134. else if(left > range.right)
  135. return query(range.r,left,right);
  136. else if(left >= range.left && right <= range.right)
  137. return true;
  138. return false;
  139. }
  140. public Range insert(Range range,int left,int right){
  141. if(range == null)
  142. return new Range(left, right);
  143. if(right < range.left -1)
  144. range.l = insert(range.l, left, right);
  145. else if(left > range.right+1)
  146. range.r = insert(range.r, left, right);
  147. else{
  148. int[]info = new int[]{left, right};
  149. range.l = add(range.l, info, true);
  150. range.r = add(range.r, info, false);
  151. range.left = Math.min(range.left, info[0]);
  152. range.right = Math.max(range.right, info[1]);
  153. }
  154. return range;
  155. }
  156. public Range add(Range range,int [] info,boolean isleft){
  157. if(range == null)
  158. return null;
  159. if(isleft){
  160. if(range.right >= info[0] - 1){ //重叠
  161. info[0] = Math.min(info[0], range.left);
  162. return add(range.l, info, isleft);
  163. }else{
  164. range.r = add(range.r, info, isleft);
  165. return range;
  166. }
  167. }else if(!isleft){
  168. if(range.left <= info[1]+1){
  169. info[1] = Math.max(info[1], range.right);
  170. return add(range.r, info, isleft);
  171. }else{
  172. range.l = add (range.l, info, isleft);
  173. return range;
  174. }
  175. }
  176. return range;
  177. }
  178. }

13 

307. 区域和检索 - 数组可修改 - 力扣(LeetCode)

this.nums=nums;下面这个 通过了但是不好,遍历区间,效果不好

  1. class NumArray {
  2. private int[] nums;
  3. public NumArray(int[] nums) {
  4. this.nums = nums;
  5. }
  6. public void update(int index, int val) {
  7. nums[index] = val;
  8. }
  9. public int sumRange(int left, int right) {
  10. int sum = 0;
  11. for (int i = left; i <= right; i++) {
  12. sum += nums[i];
  13. }
  14. return sum; // Add this line to return the computed sum
  15. }
  16. }
  17. /**
  18. * Your NumArray object will be instantiated and called as such:
  19. * NumArray obj = new NumArray(nums);
  20. * obj.update(index,val);
  21. * int param_2 = obj.sumRange(left,right);
  22. */

加上双指针,也不太好

  1. class NumArray {
  2. private int[] nums;
  3. public NumArray(int[] nums) {
  4. this.nums = nums;
  5. }
  6. public void update(int index, int val) {
  7. nums[index] = val;
  8. }
  9. public int sumRange(int left, int right) {
  10. int sum = 0;
  11. if((left+right)%2==0){
  12. sum=nums[(left+right)/2];
  13. }
  14. while(left<right){
  15. sum=sum+nums[left]+nums[right];
  16. left++;
  17. right--;
  18. }
  19. return sum; // Add this line to return the computed sum
  20. }
  21. }
  22. /**
  23. * Your NumArray object will be instantiated and called as such:
  24. * NumArray obj = new NumArray(nums);
  25. * obj.update(index,val);
  26. * int param_2 = obj.sumRange(left,right);
  27. */

用上树状数组(Binary Indexed Tree,BIT)来实现动态数组的区间更新和区间查询,有所改进

  1. public class NumArray {
  2. private int[] nums; // 存储原始数组
  3. private int[] tree; // 树状数组,用于快速计算区间和
  4. public NumArray(int[] nums) {
  5. int n = nums.length;
  6. this.nums = nums;
  7. tree = new int[n + 1];
  8. // 初始化树状数组
  9. for (int i = 1; i <= n; i++) {
  10. tree[i] += nums[i - 1];
  11. int nxt = i + (i & -i); // 下一个关键区间的右端点
  12. if (nxt <= n) {
  13. tree[nxt] += tree[i];
  14. }
  15. }
  16. }
  17. public void update(int index, int val) {
  18. int delta = val - nums[index];
  19. nums[index] = val;
  20. // 更新树状数组
  21. for (int i = index + 1; i < tree.length; i += i & -i) {
  22. tree[i] += delta;
  23. }
  24. }
  25. // 计算从位置 1 到位置 i 的前缀和
  26. private int prefixSum(int i) {
  27. int s = 0;
  28. for (; i > 0; i &= i - 1) { // i -= i & -i 的另一种写法
  29. s += tree[i];
  30. }
  31. return s;
  32. }
  33. public int sumRange(int left, int right) {
  34. // 计算区间和
  35. return prefixSum(right + 1) - prefixSum(left);
  36. }
  37. }

 

 14

1334. 阈值距离内邻居最少的城市 - 力扣(LeetCode)

该算法通过 Floyd-Warshall 算法计算所有城市之间的最短路径,并统计每个城市到其他城市的距离是否满足条件。最后,返回满足条件的城市中,拥有最小邻居数量的城市。

这个算法的时间复杂度为 O(n^3),其中 n 是城市的数量。这是因为它使用了 Floyd-Warshall 算法,该算法的时间复杂度为 O(n^3)。对于小规模的城市数量可能是可接受的,但对于大规模的城市网络,可能会变得很慢。

  1. class Solution {
  2. public int findTheCity(int n, int[][] edges, int distanceThreshold) {
  3. // 初始化城市之间的距离矩阵,使用较大的值表示无穷大距离
  4. int[][] dis = new int[n][n];
  5. for (int i = 0; i < n; i++) {
  6. Arrays.fill(dis[i], Integer.MAX_VALUE / 2);
  7. }
  8. // 根据边数组更新直接相连城市之间的距离
  9. for (int[] e : edges) {
  10. dis[e[0]][e[1]] = e[2];
  11. dis[e[1]][e[0]] = e[2];
  12. }
  13. // 使用 Floyd-Warshall 算法计算所有城市之间的最短路径
  14. for (int k = 0; k < n; k++) {
  15. dis[k][k] = 0;
  16. for (int i = 0; i < n; i++) {
  17. for (int j = i + 1; j < n; j++) {
  18. // 更新城市 i 到城市 j 的最短路径
  19. dis[i][j] = dis[j][i] = Math.min(dis[i][j], dis[i][k] + dis[k][j]);
  20. }
  21. }
  22. }
  23. // 统计每个城市满足条件的邻居数量,并记录邻居数量最小的城市
  24. int[] ans = new int[]{Integer.MAX_VALUE, -1};
  25. for (int i = 0; i < n; i++) {
  26. int cnt = 0;
  27. for (int j = 0; j < n; j++) {
  28. // 统计满足条件的邻居数量
  29. if (dis[i][j] <= distanceThreshold) {
  30. cnt++;
  31. }
  32. }
  33. // 更新邻居数量最小的城市信息
  34. if (cnt <= ans[0]) {
  35. ans[0] = cnt;
  36. ans[1] = i;
  37. }
  38. }
  39. // 返回邻居数量最小的城市
  40. return ans[1];
  41. }
  42. }

15

2656. K 个元素的最大和 - 力扣(LeetCode)

今天好简单,我好爱

  1. class Solution {
  2. public int maximizeSum(int[] nums, int k) {
  3. int max=0;
  4. for(int n:nums){
  5. max=n>max? n:max;
  6. }
  7. return k*max+(k-1)*k/2;
  8. }
  9. }

16

2760. 最长奇偶子数组 - 力扣(LeetCode)

这个题要求从第一个偶数开始,偶奇偶奇偶奇这么往后排,找最长的 

使用一个迭代的方法,在遍历数组时,根据条件更新交替子数组的起始位置和最长长度。最后,处理最后一个交替子数组,返回最终的最长交替子数组的长度。 

  1. class Solution {
  2. public int longestAlternatingSubarray(int[] nums, int threshold) {
  3. int li = -1; // 记录当前交替子数组的起始位置
  4. int max = 0; // 记录最长交替子数组的长度
  5. int n = nums.length;
  6. for (int i = 0; i < n; i++) {
  7. if (nums[i] > threshold) {
  8. // 当前元素大于阈值,结束当前交替子数组
  9. max = li == -1 ? max : Math.max(max, i - li);
  10. li = -1;
  11. } else if (li == -1 && (nums[i] & 1) == 0) {
  12. // 第一个偶数,标记当前位置为交替子数组的起始位置
  13. li = i;
  14. } else if (li != -1 && (nums[i] & 1) == (nums[i - 1] & 1)) {
  15. // 当前元素与前一个元素奇偶性相同,结束当前交替子数组
  16. max = Math.max(max, i - li);
  17. // 更新起始位置,如果当前元素是偶数,则更新为当前位置,否则为 -1
  18. li = (nums[i] & 1) == 0 ? i : -1;
  19. }
  20. }
  21. // 处理最后一个交替子数组
  22. return li == -1 ? max : Math.max(max, n - li);
  23. }
  24. }

17

2736. 最大和查询 - 力扣(LeetCode)

  1. public class Solution {
  2. // BinarySet 类表示二元组 (x, y),实现 Comparable 接口用于排序
  3. public static class BinarySet implements Comparable<BinarySet> {
  4. int x;
  5. int y;
  6. BinarySet left;
  7. BinarySet right;
  8. public BinarySet(int x, int y) {
  9. this.x = x;
  10. this.y = y;
  11. }
  12. // 降序比较,用于排序
  13. @Override
  14. public int compareTo(BinarySet o) {
  15. return -(x + y) + (o.x + o.y);
  16. }
  17. }
  18. // 主要方法,处理查询
  19. public int[] maximumSumQueries(int[] nums1, int[] nums2, int[][] queries) {
  20. int[] result = new int[queries.length];
  21. BinarySet[] binarySets = new BinarySet[nums1.length];
  22. // 创建 BinarySet 数组并按照 (x + y) 降序排序
  23. for (int i = 0; i < nums1.length; i++) {
  24. binarySets[i] = new BinarySet(nums1[i], nums2[i]);
  25. }
  26. Arrays.sort(binarySets);
  27. // 构建二叉搜索树
  28. BinarySet root = binarySets[0];
  29. for (int i = 1; i < binarySets.length; i++) {
  30. buildTree(root, binarySets[i]);
  31. }
  32. // 处理查询
  33. for (int i = 0; i < queries.length; i++) {
  34. result[i] = search(root, new BinarySet(queries[i][0], queries[i][1]));
  35. }
  36. return result;
  37. }
  38. // 查询方法,返回满足条件的最大值,如果找不到则返回 -1
  39. public int search(BinarySet root, BinarySet q) {
  40. if (root.x < q.x && root.y < q.y) {
  41. return -1;
  42. } else if (root.x >= q.x && root.y >= q.y) {
  43. return root.x + root.y;
  44. } else if (root.x < q.x) {
  45. if (root.left == null) {
  46. return -1;
  47. }
  48. return search(root.left, q);
  49. } else {
  50. if (root.right == null) {
  51. return -1;
  52. }
  53. return search(root.right, q);
  54. }
  55. }
  56. // 构建二叉搜索树的辅助方法
  57. public void buildTree(BinarySet root, BinarySet added) {
  58. if (added.x > root.x) {
  59. if (root.left != null) {
  60. buildTree(root.left, added);
  61. } else {
  62. root.left = added;
  63. }
  64. } else if (added.y > root.y) {
  65. if (root.right != null) {
  66. buildTree(root.right, added);
  67. } else {
  68. root.right = added;
  69. }
  70. }
  71. }
  72. }

18

使用了一个二维数组 val 来记录每个数位和对应的最大两个值。在遍历数组时,对每个数字计算数位和,并更新 val 数组中的值。最后,遍历 val 数组,找到最大的两个值的和。

这个算法的时间复杂度是 O(n),其中 n 是数组的长度。使用了数组来记录每个数位和对应的最大两个值,避免了排序的开销。

  1. class Solution {
  2. public int maximumSum(int[] nums) {
  3. int[][] val = new int[100][2];
  4. for (int x : nums) {
  5. int t = x, cur = 0;
  6. while (t != 0) {
  7. cur += t % 10;
  8. t /= 10;
  9. }
  10. if (x >= val[cur][1]) { // 最大沦为次大, 更新最大
  11. val[cur][0] = val[cur][1];
  12. val[cur][1] = x;
  13. } else if (x > val[cur][0]) { // 更新次大
  14. val[cur][0] = x;
  15. }
  16. }
  17. int ans = -1;
  18. for (int i = 0; i < 100; i++) {
  19. if (val[i][0] != 0 && val[i][1] != 0) ans = Math.max(ans, val[i][0] + val[i][1]);
  20. }
  21. return ans;
  22. }
  23. }

19

689. 三个无重叠子数组的最大和 - 力扣(LeetCode)

使用了动态规划的思想,首先计算以每个点为起始的长度为 k 的子数组的和,然后计算左右两侧的最大值下标,最后遍历中间子数组的起始位置,计算三个子数组的和,更新最大和及其对应的下标。

  1. class Solution {
  2. public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
  3. int n = nums.length;
  4. int currentSum = 0;
  5. for(int i = 0 ; i < k - 1; i ++ ) {
  6. currentSum += nums[i];
  7. }
  8. int sz = n - k + 1;
  9. int[] arr = new int[sz];
  10. Arrays.fill(arr, 0);
  11. for(int i = k - 1 ; i < n ; i ++ ) {
  12. currentSum = currentSum + nums[i];
  13. if(i >= k) {
  14. currentSum -= nums[i-k];
  15. }
  16. arr[i-k+1] = currentSum;
  17. }
  18. /**
  19. * 现在我们得到了从每个点起始的k个元素的和
  20. * 需要知道如何选择起始点,a,b,c 使得他们的和最大
  21. * 并且a + k <= b && b + k <= c
  22. */
  23. // prefix: 某个节点之前的最大值下标,suffix:之后
  24. int[] suffix = new int[sz];
  25. int[] prefix = new int[sz];
  26. prefix[0] = 0;
  27. suffix[sz-1] = sz-1;
  28. for(int i = 1 ; i < sz ; i ++ ) {
  29. int prevPrefix = prefix[i-1];
  30. int nextSuffix = suffix[sz-i];
  31. if(arr[i] > arr[prevPrefix]) {
  32. prefix[i] = i;
  33. } else {
  34. prefix[i] = prevPrefix;
  35. }
  36. if(arr[sz-i-1] >= arr[nextSuffix]) {
  37. suffix[sz-i-1] = sz-i-1;
  38. } else {
  39. suffix[sz-i-1] = nextSuffix;
  40. }
  41. }
  42. int ans = -1;
  43. int l=0, m=0, r=0;
  44. for(int mid = k ; mid + k < sz ; mid ++ ) {
  45. int left = mid - k ;
  46. int right = mid + k;
  47. int sum = arr[prefix[left]] + arr[mid] + arr[suffix[right]];
  48. if(sum > ans) {
  49. ans = sum;
  50. l = prefix[left] ;
  51. r = suffix[right];
  52. m = mid;
  53. }
  54. }
  55. // System.out.println(ans);
  56. return new int[]{l, m, r};
  57. }
  58. }

20

53. 最大子数组和 - 力扣(LeetCode)

采用了动态规划的思想。在遍历数组的过程中,通过不断更新 count 变量,记录当前连续子数组的和,并将其与当前元素的值比较,选择较大的值。同时,使用 ans 变量记录遍历过程中的最大子数组和。最终返回 ans 即为数组的最大子数组和。

  1. class Solution {
  2. public int maxSubArray(int[] nums) {
  3. int ans = Integer.MIN_VALUE;
  4. int count = 0;
  5. // 遍历数组
  6. for (int i = 0; i < nums.length; i++) {
  7. // 更新当前连续子数组的和,如果之前的和为负数,则从当前元素开始重新计算
  8. count = Math.max(count + nums[i], nums[i]);
  9. // 更新最大和
  10. ans = Math.max(ans, count);
  11. }
  12. return ans;
  13. }
  14. }

21

2216. 美化数组的最少删除数 - 力扣(LeetCode)

 

通过维护 pos 变量记录当前操作的位置,curPos 指针遍历数组。当 pos 为偶数时,尝试删除相邻的相同元素,如果删除后的数组没有相邻的相同元素,pos 加 1;当 pos 为奇数时,直接移动到下一个位置。最终,根据剩余元素的奇偶性返回删除的元素个数。 

  1. class Solution {
  2. public int minDeletion(int[] nums) {
  3. int len = nums.length;
  4. int pos = 0; // 记录当前操作的位置
  5. int curPos = 0; // 遍历数组的指针
  6. // 遍历数组
  7. while (curPos < len) {
  8. if ((pos & 1) == 0) {
  9. // 当 pos 为偶数时,尝试删除相邻的相同元素
  10. if (curPos + 1 == len || nums[curPos] != nums[curPos + 1]) {
  11. pos++;
  12. }
  13. } else {
  14. // 当 pos 为奇数时,直接移动到下一个位置
  15. pos++;
  16. }
  17. curPos++;
  18. }
  19. int delCount = curPos - pos; // 计算删除的元素个数
  20. return ((len - delCount) & 1) == 0 ? delCount : delCount + 1; // 判断最终剩余元素的奇偶性
  21. }
  22. }

 22

 2304. 网格中的最小路径代价 - 力扣(LeetCode)

 

动态规划算法,用于计算从二维网格底部到顶部的最小路径代价。通过迭代从倒数第二行到第一行,计算每个位置的最小代价,最终得到从顶部到底部的最小路径代价。

  1. class Solution {
  2. public int minPathCost(int[][] grid, int[][] moveCost) {
  3. int m = grid.length, n = grid[0].length;
  4. // 从倒数第二行迭代到第一行
  5. for (int i = m - 2; i >= 0; i--) {
  6. for (int j = 0; j < n; j++) {
  7. int[] cost = moveCost[grid[i][j]]; // 获取当前单元格的移动成本
  8. int res = Integer.MAX_VALUE;
  9. // 找到下一行的最小成本
  10. for (int k = 0; k < n; k++) {
  11. res = Math.min(res, grid[i + 1][k] + cost[k]);
  12. }
  13. // 使用最小成本更新当前单元格
  14. grid[i][j] += res;
  15. }
  16. }
  17. // 从第一行找到最小成本
  18. int ans = Integer.MAX_VALUE;
  19. for (int res : grid[0]) {
  20. ans = Math.min(ans, res);
  21. }
  22. return ans;
  23. }
  24. }

23

1410. HTML 实体解析器 - 力扣(LeetCode)

  1. class Solution {
  2. private static char[][] key = {{'q','u','o','t'}, {'a', 'p', 'o', 's'} , {'a', 'm', 'p'}, {'g', 't'}, {'l', 't'}, {'f', 'r', 'a', 's', 'l'}};
  3. private static int[] len = {5, 5, 4, 3, 3, 6};
  4. private static char[] value = {'\"', '\'', '&', '>', '<', '/'};
  5. static char[] result;
  6. public String entityParser(String text) {
  7. char[] cs = text.toCharArray();
  8. int n = cs.length;
  9. result = new char[n];
  10. int index = 0;
  11. for (int i = 0; i < n; i++) {
  12. if (cs[i] == '&' && i + 3 < n) {
  13. boolean special = false;
  14. for (int j = 0; j < 6; j++) {
  15. if(i + len[j] + 1 > n || cs[i + len[j] ] != ';') continue;
  16. if (isSpecial(cs, i + 1, key[j])) {
  17. result[index++]= value[j];
  18. i += len[j];
  19. special = true;
  20. break;
  21. }
  22. }
  23. if (!special)
  24. result[index++]= cs[i];
  25. }else
  26. result[index++]= cs[i];
  27. }
  28. return new String(result, 0, index);
  29. }
  30. private boolean isSpecial(char[] cs, int i, char[] target) {
  31. for (char c : target)
  32. if (cs[i++] != c) return false;
  33. return true;
  34. }
  35. }

24

2824. 统计和小于目标的下标对数目 - 力扣(LeetCode)

双重循环n方 

  1. class Solution {
  2. public int countPairs(List<Integer> nums, int target) {
  3. int n = nums.size(), ans = 0;
  4. for (int i = 0; i < n - 1; i++)
  5. for (int j = i + 1; j < n; j++)
  6. if (nums.get(i) + nums.get(j) < target)
  7. ans++;
  8. return ans;
  9. }
  10. }

先排好序 

  1. class Solution {
  2. public int countPairs(List<Integer> nums, int target) {
  3. int[] numsArray = new int[nums.size()];
  4. for (int i = 0; i < nums.size(); i++) {
  5. numsArray[i] = nums.get(i);
  6. }
  7. Arrays.sort(numsArray);
  8. int cnt = 0;
  9. int start = 0;
  10. while (start < numsArray.length) {
  11. int end = start + 1;
  12. while (end < numsArray.length && numsArray[start] + numsArray[end] < target) {
  13. cnt++;
  14. end++;
  15. }
  16. start++;
  17. }
  18. return cnt;
  19. }
  20. }

25

1457. 二叉树中的伪回文路径 - 力扣(LeetCode)

使用 status 变量来表示当前路径中每个数字的出现次数的奇偶性。通过异或运算 (^) 来更新状态。如果路径的某个数字出现次数为奇数,那么对应位上的状态就是1,否则是0。

在叶子节点处,通过判断 status 是否是 status & (-status),如果是,则说明路径中最多只有一个数字出现次数为奇数,即可以构成伪回文路径。

递归过程中,将左右子树的状态相加,最终得到从根节点到叶子节点的伪回文路径数量。

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. // 主函数,计算伪回文路径的数量
  18. public int pseudoPalindromicPaths(TreeNode root) {
  19. return dfs(root, 0);
  20. }
  21. // 递归函数,计算以当前节点为起点的伪回文路径的数量
  22. public int dfs(TreeNode cur, int status) {
  23. if (cur == null) {
  24. return 0;
  25. }
  26. // 使用异或运算更新当前节点的状态
  27. status ^= 1 << cur.val;
  28. // 如果当前节点是叶子节点,判断路径是否为伪回文路径
  29. if (cur.left == null && cur.right == null) {
  30. return status == (status & (-status)) ? 1 : 0;
  31. }
  32. // 递归遍历左右子树
  33. return dfs(cur.left, status) + dfs(cur.right, status);
  34. }
  35. }

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. int ans = 0;
  18. public int pseudoPalindromicPaths (TreeNode root) {
  19. dfs(root,0);
  20. return ans;
  21. }
  22. public void dfs(TreeNode root, int status){
  23. if(root == null){
  24. return ;
  25. }
  26. status ^= 1 << root.val;
  27. if(root.left == null && root.right == null){
  28. if(status == 0 || (status&(status-1))==0){
  29. ans++;
  30. }
  31. }
  32. if(root.left!=null) dfs(root.left, status);
  33. if(root.right!=null) dfs(root.right, status);
  34. }
  35. }

26

828. 统计子串中的唯一字符 - 力扣(LeetCode)

  1. class Solution {
  2. public int uniqueLetterString(String s) {
  3. // 假设一个字母出现 2 次或者3 次,下标分别为 i,j,k,这里相当于记录 i 的下标
  4. // 出现 2 次的时候 i 相当于 -1。
  5. int[] prePreIndex = new int[128];
  6. int[] preIndex = new int[128];// 这里相当于记录 j 的下标
  7. Arrays.fill(prePreIndex, -1);
  8. Arrays.fill(preIndex, -1);
  9. char[] chars = s.toCharArray();
  10. int res = 0;
  11. for (int i = 0; i < chars.length; i++) {
  12. int cur = chars[i];
  13. // 这里只计算一个字母出现 2 次以及 2 次以上的(不包含最后一个字母构成的个数)。
  14. if (preIndex[cur] > -1)
  15. res += (preIndex[cur] - prePreIndex[cur]) * (i - preIndex[cur]);
  16. // 更新 prePreIndex 和 preIndex 。
  17. prePreIndex[cur] = preIndex[cur];
  18. preIndex[cur] = i;
  19. }
  20. // 这里再来计算字母只出现 1 次的情况以及重复的时候最后一个字母构成的个数。
  21. for (int i = 0; i < 128; i++) {
  22. if (preIndex[i] > -1)
  23. res += (preIndex[i] - prePreIndex[i]) * (s.length() - preIndex[i]);
  24. }
  25. return res;
  26. }
  27. }

27 

907. 子数组的最小值之和 - 力扣(LeetCode)

  1. class Solution {
  2. public int sumSubarrayMins(int[] arr) {
  3. long result = 0;
  4. int[] stack = new int[arr.length]; // 使用数组结构模拟堆栈,里面存储arr数组的下标,为了便于计算“管辖区域”的跨度
  5. int head = 0, tail = head, mod = (int) (1e9 + 7); // 配合模拟堆栈的head指针和tail指针
  6. for (int i = 0; i <= arr.length; i++) {
  7. int num = (i == arr.length) ? 0 : arr[i]; // 如果arr数组遍历到最后一个元素,则还需要模拟结尾元素0,为了让stack中元素都出栈
  8. while (head != tail && arr[stack[tail - 1]] > num) {
  9. int n = stack[--tail]; // 待计算数字arr[n]的【数组下标】
  10. int h = (head != tail) ? stack[tail - 1] : -1; // arr[n]的“辐射区域”head头的【数组下标】(开区间)
  11. int t = i; // arr[n]的“辐射区域”tail尾的【数组下标】(开区间)
  12. result = (result + (long) (n - h) * (t - n) * arr[n]) % mod;
  13. }
  14. stack[tail++] = i;
  15. }
  16. return (int) result;
  17. }
  18. }

28

1670. 设计前中后队列 - 力扣(LeetCode)

  1. class FrontMiddleBackQueue {
  2. LinkedList<Integer> preq;
  3. LinkedList<Integer> proq;
  4. int pren,pron;
  5. public FrontMiddleBackQueue() {
  6. pren=0;
  7. pron=0;
  8. preq=new LinkedList<Integer>();
  9. proq=new LinkedList<Integer>();
  10. }
  11. public void pushFront(int val) {
  12. preq.offerFirst(val);
  13. pren++;
  14. if(pren>pron){
  15. proq.offerFirst(preq.pollLast());
  16. pren--;
  17. pron++;
  18. }
  19. }
  20. public void pushMiddle(int val) {
  21. preq.offerLast(val);
  22. pren++;
  23. if(pren>pron){
  24. proq.offerFirst(preq.pollLast());
  25. pren--;
  26. pron++;
  27. }
  28. }
  29. public void pushBack(int val) {
  30. proq.offerLast(val);
  31. pron++;
  32. if(pron>pren+1){
  33. preq.offerLast(proq.pollFirst());
  34. pren++;
  35. pron--;
  36. }
  37. }
  38. public int popFront() {
  39. int res=-1;
  40. if(preq.isEmpty()&&!proq.isEmpty()){
  41. res=proq.pollFirst();
  42. pron--;
  43. }else if(!preq.isEmpty()){
  44. res=preq.pollFirst();
  45. pren--;
  46. if(pron>pren+1){
  47. preq.offerLast(proq.pollFirst());
  48. pren++;
  49. pron--;
  50. }
  51. }
  52. // System.out.println(res);
  53. return res;
  54. }
  55. public int popMiddle() {
  56. int res=-1;
  57. if(pron>pren){
  58. res=proq.pollFirst();
  59. pron--;
  60. }else if(pron==pren&&pron!=0){
  61. res=preq.pollLast();
  62. pren--;
  63. if(pron>pren+1){
  64. preq.offerLast(proq.pollFirst());
  65. pren++;
  66. pron--;
  67. }
  68. }
  69. // System.out.println(res);
  70. return res;
  71. }
  72. public int popBack() {
  73. int res=-1;
  74. if(!proq.isEmpty()){
  75. res=proq.pollLast();
  76. pron--;
  77. if(pren>pron){
  78. proq.offerFirst(preq.pollLast());
  79. pren--;
  80. pron++;
  81. }
  82. }
  83. // System.out.println(res);
  84. return res;
  85. }
  86. }
  87. /**
  88. * Your FrontMiddleBackQueue object will be instantiated and called as such:
  89. * FrontMiddleBackQueue obj = new FrontMiddleBackQueue();
  90. * obj.pushFront(val);
  91. * obj.pushMiddle(val);
  92. * obj.pushBack(val);
  93. * int param_4 = obj.popFront();
  94. * int param_5 = obj.popMiddle();
  95. * int param_6 = obj.popBack();
  96. */

29

2336. 无限集中的最小数字 - 力扣(LeetCode)

  1. class SmallestInfiniteSet {
  2. boolean[] used;
  3. int idx;
  4. public SmallestInfiniteSet(){
  5. used = new boolean[1001];
  6. idx = 1;
  7. }
  8. public int popSmallest(){
  9. while(used[idx]) ++idx;
  10. used[idx] = true;
  11. return idx;
  12. }
  13. public void addBack(int num){
  14. if(used[num]) used[num] = false;
  15. if(idx > num) idx = num;
  16. }
  17. }
  18. /**
  19. * Your SmallestInfiniteSet object will be instantiated and called as such:
  20. * SmallestInfiniteSet obj = new SmallestInfiniteSet();
  21. * int param_1 = obj.popSmallest();
  22. * obj.addBack(num);
  23. */

30

1657. 确定两个字符串是否接近 - 力扣(LeetCode)

长度相等 字符集相等 频率数组相等

  1. class Solution {
  2. public boolean closeStrings(String word1, String word2) {
  3. int n = word1.length();
  4. if (n != word2.length()) return false;
  5. if (word1.equals(word2)) return true;
  6. int[] count1 = new int[128];
  7. int[] count2 = new int[128];
  8. byte[] bytes = new byte[n];
  9. word1.getBytes(0, n, bytes, 0); // Faster than String.toCharArray().
  10. for (byte b : bytes) count1[b]++;
  11. word2.getBytes(0, n, bytes, 0);
  12. for (byte b : bytes) count2[b]++;
  13. int maxFreq = 0;
  14. for (int i = 'a'; i <= 'z'; i++)
  15. maxFreq = Math.max(maxFreq, Math.max(count1[i], count2[i]));
  16. byte[] freq = new byte[maxFreq + 1];
  17. int count = 0;
  18. for (int i = 'a'; i <= 'z'; i++) {
  19. int c1 = count1[i];
  20. int c2 = count2[i];
  21. if (c1 == 0 ^ c2 == 0) return false;
  22. if (c1 == 0) continue;
  23. int f1 = ++freq[c1];
  24. int f2 = --freq[c2];
  25. if (f1 == 1) count++; else if (f1 == 0) count--;
  26. if (f2 == -1) count++; else if (f2 == 0) count--;
  27. }
  28. return count == 0;
  29. }
  30. }

总结

写完这一个月,老实说我觉得收获不大,每天都是不一样的题型,还是得系统性刷才有效果。然后我觉得我刷题还不太行,稍微难一点就是cv侠了,还要加油啊,下个月继续吧。当然这个我之后还要再都看一遍的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/796066
推荐阅读
相关标签
  

闽ICP备14008679号