赞
踩
想着每天都要刷每日一题的,但每次刷过了也没啥留下的,之后也容易忘,不如记录下来,一些知识,解题技巧,有趣的,碎碎念的。。。工作日就每日都更新,周末的题可能会留到周一更新。
1
2127. 参加会议的最多员工数 - 力扣(LeetCode)
第一天就难度升级
这题还是很好看懂的,这个i人啊必须有她喜欢的人favorite[i]坐到她的左右两边(一个圈子)她才会参加(返回数加一)。其实就是找到能满足条件的最大的圈子大小。
思路:
拓扑排序来构建邻接表,然后计算圈子的大小。下面是代码的一般流程:
统计每个人的入度,即有多少个人喜欢这个人。
将入度为0的人添加到一个队列中。
通过BFS(广度优先搜索)遍历队列,计算每个人所在的圈子深度。
统计两种类型的圈子:小圈子和大圈子。
对于小圈子,它的最大大小为2,然后加上与它相邀请的人的深度之和。
最后返回小圈子和大圈子中的最大值。
通过拓扑排序和BFS来找到满足条件的最大的圈子大小。
这题很有意思,有点人际交往的东西在里面。
- class Solution {
- public int maximumInvitations(int[] favorite) {
- int n = favorite.length;
- int[] indegree = new int[n];
- for (int i = 0; i < n; i++)
- indegree[favorite[i]]++;
-
- int[] que = new int[n];
- int read = 0, write = 0;
- for (int i = 0; i < n; i++) {
- if (indegree[i] == 0)
- que[write++] = i;
-
- }
-
- int[] depth = new int[n];
- while (read < write) {
- int cur = que[read++];
- int next = favorite[cur];
- depth[next] = Math.max(depth[next], depth[cur] + 1);
- if (--indegree[next] == 0)
- que[write++] = next;
-
- }
- int sumOfSmallRings = 0;
- int bigRings = 0;
- for (int i = 0; i < n; i++) {
- if (indegree[i] > 0) {
- int maxSize = 1;
- indegree[i] = 0;
-
- for (int j = favorite[i]; j != i; j = favorite[j]) {
- maxSize++;
- indegree[j] = 0;
- }
-
- if (maxSize == 2)
- sumOfSmallRings += 2 + depth[i] + depth[favorite[i]];
- else
- bigRings = Math.max(bigRings, maxSize);
-
- }
- }
- return Math.max(sumOfSmallRings, bigRings);
- }
- }

2.
简单题了,舒服
容易,找出所有集齐 全部三种颜色 环的杆,并返回这种杆的数量。
数组g存储每根杆上套的所有环,时间空间复杂度均为O(n),n为字符串长度/2即环的个数
- class Solution {
- public int countPoints(String rings) {
- // 数组g存储每根杆上套的所有环,时间空间复杂度均为O(n),n为字符串长度/2即环的个数
- int n = rings.length() / 2, ans = 0;
- List[] g = new ArrayList[10];
- for (int i = 0; i < 10; i++) g[i] = new ArrayList<Character>();
- for (int i = 0; i < n; i++) g[rings.charAt(2 * i + 1) - '0'].add(rings.charAt(2 * i));
- for (int i = 0; i < 10; i++) if (g[i].contains('R') && g[i].contains('G') && g[i].contains('B')) ans++;
- return ans;
- }
- }
3.
117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)
中等题,但还是很简单的
这题其实就是层序遍历,填充他的next指针。
- /*
- // Definition for a Node.
- class Node {
- public int val;
- public Node left;
- public Node right;
- public Node next;
- public Node() {}
-
- public Node(int _val) {
- val = _val;
- }
- public Node(int _val, Node _left, Node _right, Node _next) {
- val = _val;
- left = _left;
- right = _right;
- next = _next;
- }
- };
- */
-
- class Solution {
- public Node connect(Node root) {
- if(root==null){
- return root;
- }
- Node cur=root;
- while(cur!=null){//遍历完
- // dummy是每层的头,pre负责构建,cur是指代当前层
- Node dummy=new Node(0);
- Node pre = dummy;
- while(cur!=null){//到了NULL就说明每层结束了
- if(cur.left!=null){
- pre.next=cur.left;
- pre=pre.next;
- }
- if(cur.right!=null){
- pre.next=cur.right;
- pre=pre.next;
-
- }
- cur=cur.next;
- }
- cur=dummy.next;//指向下一层的第一个元素
- }
- return root;
- }
- }

4.
421. 数组中两个数的最大异或值 - 力扣(LeetCode)
这个题最容易想到的是O(n^2)的暴力解法。
为了更高效地找到数组中两个数的最大异或值,可以使用更优化的算法,例如基于前缀树(Trie)的方法。这种方法可以将时间复杂度优化到 O(n),从而提高算法的效率。
基于前缀树的方法涉及构建一个 Trie 数据结构,其中存储了数组中的数字的二进制表示。通过遍历数组中的每个数字,并在 Trie 中查找与当前数字的异或结果最大的另一个数字,可以找到数组中两个数的最大异或值。
前缀树的学习资料:
【【数据结构 10】Trie|前缀树|字典树】https://www.bilibili.com/video/BV1Az4y1S7c7?vd_source=70cd9a7d58eaf79ee46e9bddc1d0d53e
- class TrieNode {
- TrieNode[] children;
-
- TrieNode() {
- children = new TrieNode[2];
- }
- }
-
- class Solution {
- public int findMaximumXOR(int[] nums) {
- TrieNode root = new TrieNode();
- int maxXOR = 0;
-
- for (int num : nums) {
- insertNumber(root, num);
- int currXOR = findXOR(root, num);
- maxXOR = Math.max(maxXOR, currXOR);
- }
-
- return maxXOR;
- }
-
- private void insertNumber(TrieNode root, int num) {
- TrieNode curr = root;
-
- for (int i = 31; i >= 0; i--) {
- int bit = (num >> i) & 1;
-
- if (curr.children[bit] == null) {
- curr.children[bit] = new TrieNode();
- }
-
- curr = curr.children[bit];
- }
- }
-
- private int findXOR(TrieNode root, int num) {
- TrieNode curr = root;
- int xor = 0;
-
- for (int i = 31; i >= 0; i--) {
- int bit = (num >> i) & 1;
- int oppositeBit = bit == 1 ? 0 : 1;
-
- if (curr.children[oppositeBit] != null) {
- xor |= (1 << i);
- curr = curr.children[oppositeBit];
- } else {
- curr = curr.children[bit];
- }
- }
-
- return xor;
- }
- }

- class Solution {
- public int findMaximumXOR(int[] nums) {
-
- int max = Integer.MIN_VALUE;
-
- for(int n:nums)
- {
- max = Math.max(max,n);
- }
-
-
- int highestBitPos = 31 - Integer.numberOfLeadingZeros(max);
-
- int res = 0;
- int mask = 0;
-
- Set<Integer> set;
-
- for(int i=highestBitPos;i>=0;i--)
- {
- set = new HashSet<>();
- mask = mask | (1 << i);
- int candidateRes = res | (1 << i);
- for(int n:nums)
- {
- n = n & mask;
- if(set.contains(candidateRes ^ n))
- {
- res = candidateRes;
- break;
- }
- set.add(n);
- }
- }
-
- return res;
-
-
-
- }
- }

5.
找长度为10,出现过两次及以上的子序列。
方法一,用哈希表
遍历字符串,把所有十个连续的子序列存进map里,每出现一次value加一,value等于2时就能加入ansl了。
- class Solution {
- public List<String> findRepeatedDnaSequences(String s) {
- int n = s.length();
- List<String> ansL = new ArrayList<>();
- if (n <= 10) return ansL;
- Map<String, Integer> map = new HashMap<>();
- for (int i = 0; i <= n - 10; i++) {
- String ans = s.substring(i, i + 10);
- map.put(ans, map.getOrDefault(ans, 0) + 1);
- if (map.get(ans) == 2) ansL.add(ans);
- }
- return ansL;
- }
- }
方法二、滑动窗口+哈希表
- import java.util.AbstractList;
- class Solution {
- public static List<String> findRepeatedDnaSequences(String s) {
- int n = s.length();
- Map<Character, Integer> cMap = new HashMap<>() {{
- put('A', 0);
- put('C', 1);
- put('G', 2);
- put('T', 3);
- }};
- return new AbstractList<String>() {
- private final List<String> list = new ArrayList<>();
-
- private final Map<Integer, Integer> map = new HashMap<>();
-
- @Override
- public String get(int index) {
- init();
- return list.get(index);
- }
-
- @Override
- public int size() {
- init();
- return list.size();
- }
-
- void init() {
- if (list.isEmpty() && n > 10) {
- findRepeatedDnaSequences();
- }
- }
-
- void findRepeatedDnaSequences() {
- int code = 0;
- for (int i = 0; i < 10; i++) {
- code = (code << 2) + cMap.get(s.charAt(i));
- }
- map.put(code, 1);
- for (int i = 10; i < n; i++) {
- code &= (1 << 18) - 1;
- code = (code << 2) + cMap.get(s.charAt(i));
- int count = map.getOrDefault(code, 0);
- if (count == 1) {
- list.add(s.substring(i - 9, i + 1));
- }
- map.put(code, count + 1);
- }
- }
- };
- }
- }

6
位运算+暴力枚举:
首先,创建一个整型数组 store
,用于存储每个字符串的二进制表示。
然后,遍历字符串数组 words
,对每个字符串进行处理。
对于每个字符串 s
,遍历其中的每个字符,并计算字符相对于字符 'a' 的偏移量。根据偏移量,使用位运算来设置相应的二进制位。
在内层循环中,首先计算 tag
,表示字符在当前字符串的二进制表示中对应位的值。如果 tag
等于 0,说明该字符在当前字符串中是第一次出现,将相应的二进制位设置为 1。
完成字符串的二进制表示后,接下来的嵌套循环用于比较每对字符串的二进制表示。
对于每对字符串的二进制表示,使用位运算的与操作 (store[i] & store[p])
,如果结果为 0,说明两个字符串没有相同的字母。
如果两个字符串没有相同的字母,则计算它们的长度乘积 res
,并更新最大乘积 ans
。
最后,返回最大乘积 ans
。
- class Solution {
- public int maxProduct(String[] words) {
- //思路:直接用位运算
- int[] store=new int[words.length];
- for(int i=0;i<words.length;i++){//转二进制存储
- String s=words[i];
- for(Character c : s.toCharArray()){
- int tag=(store[i]%(1<<(c-'a'+1)))/(1<<(c-'a'));
- if(tag==0){
- store[i]=store[i]+(1<<(c-'a'));
- }
- }
- }
- int ans=0;
- for(int i=0;i<store.length-1;i++){
- for(int p=i+1;p<store.length;p++){
- if((store[i]&store[p])==0){//与运算,结果为0则说明两者没有相同字母
- int res=words[i].length()*words[p].length();
- ans=Math.max(res,ans);
- }
- }
- }
- return ans;
- }
- }

优化:
首先对每个字符串进行预处理,将其转换为一个包含字符出现情况的位向量(bitmask),出现的位在二进制上的偏移置为1,方便后续计算。然后,对于每一对字符串,如果它们的位向量按位与的结果为0,则它们没有相同的字母,可以计算它们的长度乘积并更新最大乘积。
- class Solution {
- public int maxProduct(String[] words) {
- int n = words.length;
-
- // 预处理,将每个字符串转换为位向量
- int[] bitmasks = new int[n];
- for (int i = 0; i < n; i++) {
- String word = words[i];
- int bitmask = 0;
- for (char c : word.toCharArray()) {
- bitmask |= 1 << (c - 'a'); // 将对应的位设置为1
- }
- bitmasks[i] = bitmask;
- }
-
- int maxProduct = 0;
- for (int i = 0; i < n - 1; i++) {
- for (int j = i + 1; j < n; j++) {
- // 如果两个字符串的位向量按位与的结果为0,则它们没有相同的字母
- if ((bitmasks[i] & bitmasks[j]) == 0) {
- int product = words[i].length() * words[j].length();
- maxProduct = Math.max(maxProduct, product);
- }
- }
- }
-
- return maxProduct;
- }
- }

7.
2586. 统计范围内的元音字符串数 - 力扣(LeetCode)
简单题,我哭死
判断首尾是不是元音字符串就好了。
- class Solution {
- public int vowelStrings(String[] words, int left, int right) {
- int res=0;
- for(int i=left;i<=right;i++){
- String s=words[i];
- int n=s.length();
- 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++;
- }
- return res;
- }
- }
8
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
。- class Solution {
- public int findTheLongestBalancedSubstring(String s) {
- int n = s.length(), idx = 0, ans = 0;
- while (idx < n) {
- int a = 0, b = 0;
- while (idx < n && s.charAt(idx) == '0' && ++a >= 0) idx++;
- while (idx < n && s.charAt(idx) == '1' && ++b >= 0) idx++;
- ans = Math.max(ans, Math.min(a, b) * 2);
- }
- return ans;
- }
- }
9
bfs,我先CV
- // class Solution {
- // // 二分+bfs(预处理+验证路径)
- // // 将矩阵可达点预处理为着火时间,后续bfs搜索路径时,到达时间小于着火时间的为可走路径。
- // // 可暂停时间的最小值为0,最大值为无穷大,而无穷大状态即火焰扩张完所有可达位置后,
- // // 矩阵中仍存在从左上到右下角的安全路径,所以可暂停时间time可设置为火焰完成扩张时间+2,
- // // 即ans可能取[0,time]内任何值,在此区间内二分查找不存在可达路径的暂停时间最小值,
- // // 判定合法的方式就是对每个时间t在预处理过后的矩阵内搜索是否存在左上到右下的安全路径,
- // // 1、若t=0时不可到达目标点,则永远不可达成,返回-1,
- // // 2、若t=time,而在time-1时存在安全路径,而火焰完成扩张的时间是time-2,所以火焰不在扩张后仍存在安全路径,是否可达与暂停时间无关,返回1e9,
- // // 3、其余情况下则是火扩张至某时刻后目标点不在可达,那么二分求的是最小不可达时间r,r-1即为最大可达目标点的暂停时间。
- // int[][] g;
- // int m, n;
- // int[][] dir = {{-1,0},{0,1},{1,0},{0,-1}};
- // public int maximumMinutes(int[][] g) {
- // this.g = g;
- // m = g.length;
- // n = g[0].length;
- // Deque<int[]> que = new ArrayDeque<>();
- // boolean[][] vis = new boolean[m][n];
- // // 预处理矩阵,将所有墙壁点标记为-1,这样就可以用0代表空地,以及大于0的数字代表每个位置开始着火的时间。
- // // 将初始火源点存入队列,后续bfs按圈扩张,标记所有
- // for(int i = 0; i < m; ++i){
- // for(int j = 0; j < n; ++j){
- // if(g[i][j] == 2){
- // g[i][j] = -1;
- // }else if(g[i][j] == 1){
- // que.offer(new int[]{i, j});
- // vis[i][j] = true;
- // }
- // }
- // }
- // int time = 1;
- // // bfs分层模拟扩展随时间可被火焰蔓延的区域,在g矩阵标记着火时间点,time最终为火焰铺满所有可蔓延位置所需的时间。
- // while(!que.isEmpty()){
- // int len = que.size();
- // while(--len >= 0){
- // int[] p = que.poll();
- // for(int k = 0; k < 4; ++k){
- // int i = p[0] + dir[k][0], j = p[1] + dir[k][1];
- // if(i >= 0 && j >= 0 && i < m && j < n && !vis[i][j] && g[i][j] == 0){
- // g[i][j] = time;
- // que.offer(new int[]{i, j});
- // vis[i][j] = true;
- // }
- // }
- // }
- // ++time;
- // }
- // // 二分找到无法到达的最小暂停时间r,则r-1即为最大可暂停时间。
- // int l = 0, r = time;
- // while(l < r){
- // int mid = l + r >> 1;
- // if(!bfs(mid))
- // r = mid;
- // else
- // l = mid + 1;
- // }
- // // 若直接出发仍无法到达,则-1不可实现;若火焰完成扩张后+1秒仍可到达,因火焰不会在扩张,所以说明后续仍可到达。
- // // 否则返回最小无法到达暂停时间r-1即可。
- // if(r == 0)
- // return -1;
- // else if(r == time)
- // return 1000000000;
- // else
- // return r-1;
- // }
- // // bfs验证在预留t时间的火势状态下,是否能够从左上角走到右下角。
- // public boolean bfs(int t){
- // ++t;
- // boolean[][] vis = new boolean[m][n];
- // Deque<int[]> que = new ArrayDeque<>();
- // que.offer(new int[]{0,0});
- // vis[0][0] = true;
- // // 当
- // while(!que.isEmpty()){
- // int len = que.size();
- // while(len-- > 0){
- // int[] p = que.poll();
- // for(int k = 0; k < 4; ++k){
- // int i = p[0] + dir[k][0], j = p[1] + dir[k][1];
- // if(i >= 0 && j >= 0 && i < m && j < n && !vis[i][j] && (g[i][j] >= t || g[i][j] == 0)){
- // // 到达终点(m-1,n-1)则true。
- // if(i == m - 1 && j == n - 1)
- // return true;
- // if(g[i][j] > t || g[i][j] == 0)
- // que.offer(new int[]{i, j});
- // vis[i][j] = true;
- // }
- // }
- // }
- // // 若中途终点被火蔓延,则后续也无法再到达,直接false。
- // if(g[m-1][n-1] == t)
- // return false;
- // ++t;
- // }
- // return false;
- // }
- // }
-
- class Solution {
- static int[][] directions = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
- public int maximumMinutes(int[][] grid) {
- int m = grid.length, n = grid[0].length;
- int[][] fire = new int[m][n];
- int[][]people = new int[m][n];
- for (int i = 0; i < m; i++) {
- Arrays.fill(fire[i], -1);
- Arrays.fill(people[i], -1);
- }
- Deque<int[]> que = new ArrayDeque<>();
- for (int i = 0; i < m; i++) {
- for (int j = 0; j < n; j++) {
- if (grid[i][j] == 1) {
- fire[i][j] = 0;
- que.offer(new int[]{i, j, 0});
- }
- }
- }
- while (!que.isEmpty()) {
- int[] cur = que.poll();
- for (int[] d: directions) {
- int nx = cur[0] + d[0], ny = cur[1] + d[1];
- if (nx >= 0 && ny >= 0 && nx < m && ny < n && fire[nx][ny] == -1 && grid[nx][ny] == 0) {
- fire[nx][ny] = cur[2] + 1;
- que.offer(new int[]{nx, ny, cur[2] + 1});
- }
- }
- }
-
- people[0][0] = 0;
- que = new ArrayDeque<>();
- que.offer(new int[]{0, 0, 0});
- while (!que.isEmpty()) {
- int[] cur = que.poll();
- for (int[] d:directions) {
- int nx = cur[0] + d[0], ny = cur[1] + d[1];
- if (nx >= 0 && ny >= 0 && nx < m && ny < n && people[nx][ny] == -1 && grid[nx][ny] == 0) {
- people[nx][ny] = cur[2] + 1;
- 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);
- if (people[nx][ny] >= fire[nx][ny] && fire[nx][ny] != -1) continue;
- que.offer(new int[]{nx, ny, cur[2] + 1});
- }
- }
- }
- return -1;
- }
- }

10.
2300. 咒语和药水的成功对数 - 力扣(LeetCode)
- // // class Solution {
- // // public int[] successfulPairs(int[] spells, int[] potions, long success) {
- // // int n=spells.length;
- // // int[] res=new int[n];
- // // for(int i=0;i<n;i++){
- // // int cnt=0;
- // // for(int j=0;j<potions.length;j++){
- // // if(spells[i]*potions[j]>=success) cnt++;
- // // }
- // // res[i]=cnt;
- // // }
- // // return res;
- // // }
- // // }
-
- // class Solution {
-
- // public int[] successfulPairs(int[] spells, int[] potions, long success) {
- // Arrays.sort(potions);
- // int N = spells.length;
- // int M = potions.length;
- // int[] ans = new int[N];
- // for (int i = 0; i < N; i++) {
- // int l = 0;
- // int r = M - 1;
- // int min = -1;
- // while (l <= r) {
- // int m = (l + r) >>> 1;
- // if ((long) potions[m] * spells[i] >= success) {
- // min = m;
- // r = m - 1;
- // } else {
- // l = m + 1;
- // }
- // }
- // if (min != -1) {
- // ans[i] = M - min;
- // }
- // }
- // return ans;
- // }
- // }
-
- class Solution {
- static int inf = (int)1e5;
- public int[] successfulPairs(int[] spells, int[] potions, long success){
- int max = 0;
- for(int x : spells){
- if(x > max) max = x;
- }
- int minPotion = (int)Math.min(inf, (success - 1) / max);
- int[] count = new int[max + 1];
- for(int potion:potions){
- if(potion > minPotion){
- ++count[(int)((success + potion - 1)/potion)];
- }
- }
- for(int i = 1; i <= max; i++){
- count[i] += count[i-1];
- }
- int n = spells.length;
- int[] result = new int[n];
- for(int i = 0; i < n; i++){
- result[i] = count[spells[i]];
- }
- return result;
- }
- }

11
使用了贪心算法的思想。从左到右遍历数组,对于每一对夫妻,如果他们不坐在一起(即i位置的配偶不是y),则在后面的位置中找到y的位置j,然后交换i位置的配偶和j位置的人。交换次数加1,继续遍历下一对夫妻。最终返回交换次数。
该方法的时间复杂度为O(n^2),其中n是数组row的长度。
- class Solution {
- public int minSwapsCouples(int[] row) {
- int count = 0; // 交换次数计数器
- for (int i = 0; i < row.length; i += 2) {
- int x = row[i]; // 当前位置i的夫妻编号
- int y = x ^ 1; // x的配偶编号
- if (row[i + 1] != y) { // 如果i位置的配偶不是y,则需要进行交换
- for (int j = i + 2; j < row.length; j++) {
- if (row[j] == y) { // 在后面的位置找到y的位置j
- int temp = row[i + 1]; // 交换i位置的配偶和j位置的人
- row[i + 1] = row[j];
- row[j] = temp;
- count++; // 交换次数加1
- break;
- }
- }
- }
- }
- return count; // 返回交换次数
- }
- }

12
讲实话,我看不懂一点,这个之后还要仔细看看
- // class RangeModule {
- // TreeMap<Integer, Integer> intervals;
-
- // public RangeModule() {
- // intervals = new TreeMap<Integer, Integer>();
- // }
-
- // public void addRange(int left, int right) {
- // Map.Entry<Integer, Integer> entry = intervals.higherEntry(left);
- // if (entry != intervals.firstEntry()) {
- // Map.Entry<Integer, Integer> start = entry != null ? intervals.lowerEntry(entry.getKey()) : intervals.lastEntry();
- // if (start != null && start.getValue() >= right) {
- // return;
- // }
- // if (start != null && start.getValue() >= left) {
- // left = start.getKey();
- // intervals.remove(start.getKey());
- // }
- // }
- // while (entry != null && entry.getKey() <= right) {
- // right = Math.max(right, entry.getValue());
- // intervals.remove(entry.getKey());
- // entry = intervals.higherEntry(entry.getKey());
- // }
- // intervals.put(left, right);
- // }
-
- // public boolean queryRange(int left, int right) {
- // Map.Entry<Integer, Integer> entry = intervals.higherEntry(left);
- // if (entry == intervals.firstEntry()) {
- // return false;
- // }
- // entry = entry != null ? intervals.lowerEntry(entry.getKey()) : intervals.lastEntry();
- // return entry != null && right <= entry.getValue();
- // }
-
- // public void removeRange(int left, int right) {
- // Map.Entry<Integer, Integer> entry = intervals.higherEntry(left);
- // if (entry != intervals.firstEntry()) {
- // Map.Entry<Integer, Integer> start = entry != null ? intervals.lowerEntry(entry.getKey()) : intervals.lastEntry();
- // if (start != null && start.getValue() >= right) {
- // int ri = start.getValue();
- // if (start.getKey() == left) {
- // intervals.remove(start.getKey());
- // } else {
- // intervals.put(start.getKey(), left);
- // }
- // if (right != ri) {
- // intervals.put(right, ri);
- // }
- // return;
- // } else if (start != null && start.getValue() > left) {
- // if (start.getKey() == left) {
- // intervals.remove(start.getKey());
- // } else {
- // intervals.put(start.getKey(), left);
- // }
- // }
- // }
- // while (entry != null && entry.getKey() < right) {
- // if (entry.getValue() <= right) {
- // intervals.remove(entry.getKey());
- // entry = intervals.higherEntry(entry.getKey());
- // } else {
- // intervals.put(right, entry.getValue());
- // intervals.remove(entry.getKey());
- // break;
- // }
- // }
- // }
- // }
-
- class RangeModule {
-
- public Range root;
- public RangeModule() {
-
- }
-
- public void addRange(int left, int right) {
- root = insert(root,left, right-1);
- }
-
- public boolean queryRange(int left, int right) {
- return query(root,left, right-1);
- }
-
- public void removeRange(int left, int right) {
- root = remove(root,left, right-1);
- }
-
- static class Range{
- public int left, right;
- public Range l, r;
-
- public Range(int left,int right){
- this.left = left;
- this.right = right;
- }
- }
-
- public Range remove(Range range,int left,int right){
-
- if(range == null) return null;
-
- if(right < range.left)
- range.l = remove(range.l, left, right);
- else if(left > range.right)
-
- range.r = remove(range.r, left, right);
- else if(left <= range.left && right >= range.right){
-
- Range l = remove(range.l, left, right);
- Range r = remove(range.r, left, right);
- if(l == null)
- return r;
- else if(r == null)
- return l;
- else{
- if(l.r == null){
- l.r = r;
- return l;
- }else{
- Range temp = r;
- while(temp.l!=null)
- temp=temp.l;
- temp.l = l;
- return r;
- }
- }
-
- }else if(left <= range.left){
- range.left = right+1;
- range.l = remove(range.l, left, right);
-
- }else if(right >= range.right){
- range.right = left - 1;
- range.r = remove(range.r, left, right);
-
- }else{
-
- Range newR = new Range(right+1, range.right);
- Range r = range.r;
- range.right = left - 1;
- newR.r = r;
- range.r = newR;
- }
-
- return range;
- }
- public boolean query(Range range,int left,int right){
-
- if(range == null)
- return false;
-
- if(right < range.left)
- return query(range.l,left,right);
- else if(left > range.right)
- return query(range.r,left,right);
- else if(left >= range.left && right <= range.right)
- return true;
-
- return false;
- }
-
- public Range insert(Range range,int left,int right){
-
- if(range == null)
- return new Range(left, right);
-
- if(right < range.left -1)
- range.l = insert(range.l, left, right);
-
- else if(left > range.right+1)
- range.r = insert(range.r, left, right);
-
- else{
- int[]info = new int[]{left, right};
- range.l = add(range.l, info, true);
- range.r = add(range.r, info, false);
- range.left = Math.min(range.left, info[0]);
- range.right = Math.max(range.right, info[1]);
- }
- return range;
- }
- public Range add(Range range,int [] info,boolean isleft){
-
- if(range == null)
- return null;
-
- if(isleft){
- if(range.right >= info[0] - 1){ //重叠
- info[0] = Math.min(info[0], range.left);
- return add(range.l, info, isleft);
- }else{
- range.r = add(range.r, info, isleft);
- return range;
- }
- }else if(!isleft){
- if(range.left <= info[1]+1){
- info[1] = Math.max(info[1], range.right);
- return add(range.r, info, isleft);
- }else{
- range.l = add (range.l, info, isleft);
- return range;
- }
- }
- return range;
- }
- }

13
307. 区域和检索 - 数组可修改 - 力扣(LeetCode)
this.nums=nums;下面这个 通过了但是不好,遍历区间,效果不好
- class NumArray {
- private int[] nums;
-
- public NumArray(int[] nums) {
- this.nums = nums;
- }
-
- public void update(int index, int val) {
- nums[index] = val;
- }
-
- public int sumRange(int left, int right) {
- int sum = 0;
- for (int i = left; i <= right; i++) {
- sum += nums[i];
- }
- return sum; // Add this line to return the computed sum
- }
- }
-
- /**
- * Your NumArray object will be instantiated and called as such:
- * NumArray obj = new NumArray(nums);
- * obj.update(index,val);
- * int param_2 = obj.sumRange(left,right);
- */

加上双指针,也不太好
- class NumArray {
- private int[] nums;
-
- public NumArray(int[] nums) {
- this.nums = nums;
- }
-
- public void update(int index, int val) {
- nums[index] = val;
- }
-
- public int sumRange(int left, int right) {
- int sum = 0;
- if((left+right)%2==0){
- sum=nums[(left+right)/2];
- }
-
- while(left<right){
- sum=sum+nums[left]+nums[right];
- left++;
- right--;
- }
- return sum; // Add this line to return the computed sum
- }
- }
-
- /**
- * Your NumArray object will be instantiated and called as such:
- * NumArray obj = new NumArray(nums);
- * obj.update(index,val);
- * int param_2 = obj.sumRange(left,right);
- */

用上树状数组(Binary Indexed Tree,BIT)来实现动态数组的区间更新和区间查询,有所改进
- public class NumArray {
- private int[] nums; // 存储原始数组
- private int[] tree; // 树状数组,用于快速计算区间和
-
- public NumArray(int[] nums) {
- int n = nums.length;
- this.nums = nums;
- tree = new int[n + 1];
-
- // 初始化树状数组
- for (int i = 1; i <= n; i++) {
- tree[i] += nums[i - 1];
- int nxt = i + (i & -i); // 下一个关键区间的右端点
- if (nxt <= n) {
- tree[nxt] += tree[i];
- }
- }
- }
-
- public void update(int index, int val) {
- int delta = val - nums[index];
- nums[index] = val;
-
- // 更新树状数组
- for (int i = index + 1; i < tree.length; i += i & -i) {
- tree[i] += delta;
- }
- }
-
- // 计算从位置 1 到位置 i 的前缀和
- private int prefixSum(int i) {
- int s = 0;
- for (; i > 0; i &= i - 1) { // i -= i & -i 的另一种写法
- s += tree[i];
- }
- return s;
- }
-
- public int sumRange(int left, int right) {
- // 计算区间和
- return prefixSum(right + 1) - prefixSum(left);
- }
- }

14
1334. 阈值距离内邻居最少的城市 - 力扣(LeetCode)
该算法通过 Floyd-Warshall 算法计算所有城市之间的最短路径,并统计每个城市到其他城市的距离是否满足条件。最后,返回满足条件的城市中,拥有最小邻居数量的城市。
这个算法的时间复杂度为 O(n^3),其中 n 是城市的数量。这是因为它使用了 Floyd-Warshall 算法,该算法的时间复杂度为 O(n^3)。对于小规模的城市数量可能是可接受的,但对于大规模的城市网络,可能会变得很慢。
- class Solution {
- public int findTheCity(int n, int[][] edges, int distanceThreshold) {
- // 初始化城市之间的距离矩阵,使用较大的值表示无穷大距离
- int[][] dis = new int[n][n];
- for (int i = 0; i < n; i++) {
- Arrays.fill(dis[i], Integer.MAX_VALUE / 2);
- }
-
- // 根据边数组更新直接相连城市之间的距离
- for (int[] e : edges) {
- dis[e[0]][e[1]] = e[2];
- dis[e[1]][e[0]] = e[2];
- }
-
- // 使用 Floyd-Warshall 算法计算所有城市之间的最短路径
- for (int k = 0; k < n; k++) {
- dis[k][k] = 0;
- for (int i = 0; i < n; i++) {
- for (int j = i + 1; j < n; j++) {
- // 更新城市 i 到城市 j 的最短路径
- dis[i][j] = dis[j][i] = Math.min(dis[i][j], dis[i][k] + dis[k][j]);
- }
- }
- }
-
- // 统计每个城市满足条件的邻居数量,并记录邻居数量最小的城市
- int[] ans = new int[]{Integer.MAX_VALUE, -1};
- for (int i = 0; i < n; i++) {
- int cnt = 0;
- for (int j = 0; j < n; j++) {
- // 统计满足条件的邻居数量
- if (dis[i][j] <= distanceThreshold) {
- cnt++;
- }
- }
- // 更新邻居数量最小的城市信息
- if (cnt <= ans[0]) {
- ans[0] = cnt;
- ans[1] = i;
- }
- }
-
- // 返回邻居数量最小的城市
- return ans[1];
- }
- }

15
2656. K 个元素的最大和 - 力扣(LeetCode)
今天好简单,我好爱
- class Solution {
- public int maximizeSum(int[] nums, int k) {
- int max=0;
- for(int n:nums){
- max=n>max? n:max;
- }
- return k*max+(k-1)*k/2;
- }
- }
16
这个题要求从第一个偶数开始,偶奇偶奇偶奇这么往后排,找最长的
使用一个迭代的方法,在遍历数组时,根据条件更新交替子数组的起始位置和最长长度。最后,处理最后一个交替子数组,返回最终的最长交替子数组的长度。
- class Solution {
- public int longestAlternatingSubarray(int[] nums, int threshold) {
- int li = -1; // 记录当前交替子数组的起始位置
- int max = 0; // 记录最长交替子数组的长度
- int n = nums.length;
-
- for (int i = 0; i < n; i++) {
- if (nums[i] > threshold) {
- // 当前元素大于阈值,结束当前交替子数组
- max = li == -1 ? max : Math.max(max, i - li);
- li = -1;
- } else if (li == -1 && (nums[i] & 1) == 0) {
- // 第一个偶数,标记当前位置为交替子数组的起始位置
- li = i;
- } else if (li != -1 && (nums[i] & 1) == (nums[i - 1] & 1)) {
- // 当前元素与前一个元素奇偶性相同,结束当前交替子数组
- max = Math.max(max, i - li);
- // 更新起始位置,如果当前元素是偶数,则更新为当前位置,否则为 -1
- li = (nums[i] & 1) == 0 ? i : -1;
- }
- }
-
- // 处理最后一个交替子数组
- return li == -1 ? max : Math.max(max, n - li);
- }
- }

17
- public class Solution {
-
- // BinarySet 类表示二元组 (x, y),实现 Comparable 接口用于排序
- public static class BinarySet implements Comparable<BinarySet> {
- int x;
- int y;
- BinarySet left;
- BinarySet right;
-
- public BinarySet(int x, int y) {
- this.x = x;
- this.y = y;
- }
-
- // 降序比较,用于排序
- @Override
- public int compareTo(BinarySet o) {
- return -(x + y) + (o.x + o.y);
- }
- }
-
- // 主要方法,处理查询
- public int[] maximumSumQueries(int[] nums1, int[] nums2, int[][] queries) {
- int[] result = new int[queries.length];
- BinarySet[] binarySets = new BinarySet[nums1.length];
-
- // 创建 BinarySet 数组并按照 (x + y) 降序排序
- for (int i = 0; i < nums1.length; i++) {
- binarySets[i] = new BinarySet(nums1[i], nums2[i]);
- }
- Arrays.sort(binarySets);
-
- // 构建二叉搜索树
- BinarySet root = binarySets[0];
- for (int i = 1; i < binarySets.length; i++) {
- buildTree(root, binarySets[i]);
- }
-
- // 处理查询
- for (int i = 0; i < queries.length; i++) {
- result[i] = search(root, new BinarySet(queries[i][0], queries[i][1]));
- }
- return result;
- }
-
- // 查询方法,返回满足条件的最大值,如果找不到则返回 -1
- public int search(BinarySet root, BinarySet q) {
- if (root.x < q.x && root.y < q.y) {
- return -1;
- } else if (root.x >= q.x && root.y >= q.y) {
- return root.x + root.y;
- } else if (root.x < q.x) {
- if (root.left == null) {
- return -1;
- }
- return search(root.left, q);
- } else {
- if (root.right == null) {
- return -1;
- }
- return search(root.right, q);
- }
- }
-
- // 构建二叉搜索树的辅助方法
- public void buildTree(BinarySet root, BinarySet added) {
- if (added.x > root.x) {
- if (root.left != null) {
- buildTree(root.left, added);
- } else {
- root.left = added;
- }
- } else if (added.y > root.y) {
- if (root.right != null) {
- buildTree(root.right, added);
- } else {
- root.right = added;
- }
- }
- }
- }

18
使用了一个二维数组 val
来记录每个数位和对应的最大两个值。在遍历数组时,对每个数字计算数位和,并更新 val
数组中的值。最后,遍历 val
数组,找到最大的两个值的和。
这个算法的时间复杂度是 O(n),其中 n 是数组的长度。使用了数组来记录每个数位和对应的最大两个值,避免了排序的开销。
- class Solution {
- public int maximumSum(int[] nums) {
- int[][] val = new int[100][2];
- for (int x : nums) {
- int t = x, cur = 0;
- while (t != 0) {
- cur += t % 10;
- t /= 10;
- }
- if (x >= val[cur][1]) { // 最大沦为次大, 更新最大
- val[cur][0] = val[cur][1];
- val[cur][1] = x;
- } else if (x > val[cur][0]) { // 更新次大
- val[cur][0] = x;
- }
- }
- int ans = -1;
- for (int i = 0; i < 100; i++) {
- if (val[i][0] != 0 && val[i][1] != 0) ans = Math.max(ans, val[i][0] + val[i][1]);
- }
- return ans;
- }
- }

19
689. 三个无重叠子数组的最大和 - 力扣(LeetCode)
使用了动态规划的思想,首先计算以每个点为起始的长度为 k
的子数组的和,然后计算左右两侧的最大值下标,最后遍历中间子数组的起始位置,计算三个子数组的和,更新最大和及其对应的下标。
- class Solution {
- public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
- int n = nums.length;
- int currentSum = 0;
- for(int i = 0 ; i < k - 1; i ++ ) {
- currentSum += nums[i];
- }
- int sz = n - k + 1;
- int[] arr = new int[sz];
- Arrays.fill(arr, 0);
- for(int i = k - 1 ; i < n ; i ++ ) {
- currentSum = currentSum + nums[i];
- if(i >= k) {
- currentSum -= nums[i-k];
- }
- arr[i-k+1] = currentSum;
- }
- /**
- * 现在我们得到了从每个点起始的k个元素的和
- * 需要知道如何选择起始点,a,b,c 使得他们的和最大
- * 并且a + k <= b && b + k <= c
- */
-
-
- // prefix: 某个节点之前的最大值下标,suffix:之后
- int[] suffix = new int[sz];
- int[] prefix = new int[sz];
- prefix[0] = 0;
- suffix[sz-1] = sz-1;
- for(int i = 1 ; i < sz ; i ++ ) {
- int prevPrefix = prefix[i-1];
- int nextSuffix = suffix[sz-i];
- if(arr[i] > arr[prevPrefix]) {
- prefix[i] = i;
- } else {
- prefix[i] = prevPrefix;
- }
-
- if(arr[sz-i-1] >= arr[nextSuffix]) {
- suffix[sz-i-1] = sz-i-1;
- } else {
- suffix[sz-i-1] = nextSuffix;
- }
- }
- int ans = -1;
- int l=0, m=0, r=0;
- for(int mid = k ; mid + k < sz ; mid ++ ) {
- int left = mid - k ;
- int right = mid + k;
- int sum = arr[prefix[left]] + arr[mid] + arr[suffix[right]];
- if(sum > ans) {
- ans = sum;
- l = prefix[left] ;
- r = suffix[right];
- m = mid;
- }
- }
- // System.out.println(ans);
- return new int[]{l, m, r};
- }
- }

20
采用了动态规划的思想。在遍历数组的过程中,通过不断更新 count
变量,记录当前连续子数组的和,并将其与当前元素的值比较,选择较大的值。同时,使用 ans
变量记录遍历过程中的最大子数组和。最终返回 ans
即为数组的最大子数组和。
- class Solution {
- public int maxSubArray(int[] nums) {
- int ans = Integer.MIN_VALUE;
- int count = 0;
-
- // 遍历数组
- for (int i = 0; i < nums.length; i++) {
- // 更新当前连续子数组的和,如果之前的和为负数,则从当前元素开始重新计算
- count = Math.max(count + nums[i], nums[i]);
-
- // 更新最大和
- ans = Math.max(ans, count);
- }
-
- return ans;
- }
- }

21
2216. 美化数组的最少删除数 - 力扣(LeetCode)
通过维护 pos
变量记录当前操作的位置,curPos
指针遍历数组。当 pos
为偶数时,尝试删除相邻的相同元素,如果删除后的数组没有相邻的相同元素,pos
加 1;当 pos
为奇数时,直接移动到下一个位置。最终,根据剩余元素的奇偶性返回删除的元素个数。
- class Solution {
- public int minDeletion(int[] nums) {
- int len = nums.length;
- int pos = 0; // 记录当前操作的位置
- int curPos = 0; // 遍历数组的指针
-
- // 遍历数组
- while (curPos < len) {
- if ((pos & 1) == 0) {
- // 当 pos 为偶数时,尝试删除相邻的相同元素
- if (curPos + 1 == len || nums[curPos] != nums[curPos + 1]) {
- pos++;
- }
- } else {
- // 当 pos 为奇数时,直接移动到下一个位置
- pos++;
- }
- curPos++;
- }
-
- int delCount = curPos - pos; // 计算删除的元素个数
- return ((len - delCount) & 1) == 0 ? delCount : delCount + 1; // 判断最终剩余元素的奇偶性
- }
- }

22
2304. 网格中的最小路径代价 - 力扣(LeetCode)
动态规划算法,用于计算从二维网格底部到顶部的最小路径代价。通过迭代从倒数第二行到第一行,计算每个位置的最小代价,最终得到从顶部到底部的最小路径代价。
- class Solution {
- public int minPathCost(int[][] grid, int[][] moveCost) {
- int m = grid.length, n = grid[0].length;
-
- // 从倒数第二行迭代到第一行
- for (int i = m - 2; i >= 0; i--) {
- for (int j = 0; j < n; j++) {
- int[] cost = moveCost[grid[i][j]]; // 获取当前单元格的移动成本
- int res = Integer.MAX_VALUE;
-
- // 找到下一行的最小成本
- for (int k = 0; k < n; k++) {
- res = Math.min(res, grid[i + 1][k] + cost[k]);
- }
-
- // 使用最小成本更新当前单元格
- grid[i][j] += res;
- }
- }
-
- // 从第一行找到最小成本
- int ans = Integer.MAX_VALUE;
- for (int res : grid[0]) {
- ans = Math.min(ans, res);
- }
-
- return ans;
- }
- }

23
1410. HTML 实体解析器 - 力扣(LeetCode)
- class Solution {
-
- private static char[][] key = {{'q','u','o','t'}, {'a', 'p', 'o', 's'} , {'a', 'm', 'p'}, {'g', 't'}, {'l', 't'}, {'f', 'r', 'a', 's', 'l'}};
- private static int[] len = {5, 5, 4, 3, 3, 6};
- private static char[] value = {'\"', '\'', '&', '>', '<', '/'};
-
- static char[] result;
- public String entityParser(String text) {
-
- char[] cs = text.toCharArray();
- int n = cs.length;
- result = new char[n];
- int index = 0;
-
- for (int i = 0; i < n; i++) {
-
- if (cs[i] == '&' && i + 3 < n) {
- boolean special = false;
- for (int j = 0; j < 6; j++) {
- if(i + len[j] + 1 > n || cs[i + len[j] ] != ';') continue;
- if (isSpecial(cs, i + 1, key[j])) {
- result[index++]= value[j];
- i += len[j];
- special = true;
- break;
- }
- }
- if (!special)
- result[index++]= cs[i];
- }else
-
- result[index++]= cs[i];
-
- }
- return new String(result, 0, index);
- }
-
- private boolean isSpecial(char[] cs, int i, char[] target) {
- for (char c : target)
- if (cs[i++] != c) return false;
-
- return true;
- }
- }

24
2824. 统计和小于目标的下标对数目 - 力扣(LeetCode)
双重循环n方
- class Solution {
- public int countPairs(List<Integer> nums, int target) {
- int n = nums.size(), ans = 0;
- for (int i = 0; i < n - 1; i++)
- for (int j = i + 1; j < n; j++)
- if (nums.get(i) + nums.get(j) < target)
- ans++;
- return ans;
- }
- }
先排好序
- class Solution {
- public int countPairs(List<Integer> nums, int target) {
- int[] numsArray = new int[nums.size()];
- for (int i = 0; i < nums.size(); i++) {
- numsArray[i] = nums.get(i);
- }
-
- Arrays.sort(numsArray);
-
- int cnt = 0;
-
- int start = 0;
- while (start < numsArray.length) {
- int end = start + 1;
- while (end < numsArray.length && numsArray[start] + numsArray[end] < target) {
- cnt++;
- end++;
- }
- start++;
- }
-
- return cnt;
- }
- }

25
1457. 二叉树中的伪回文路径 - 力扣(LeetCode)
使用 status
变量来表示当前路径中每个数字的出现次数的奇偶性。通过异或运算 (^
) 来更新状态。如果路径的某个数字出现次数为奇数,那么对应位上的状态就是1,否则是0。
在叶子节点处,通过判断 status
是否是 status & (-status)
,如果是,则说明路径中最多只有一个数字出现次数为奇数,即可以构成伪回文路径。
递归过程中,将左右子树的状态相加,最终得到从根节点到叶子节点的伪回文路径数量。
- /**
- * Definition for a binary tree node.
- * public class TreeNode {
- * int val;
- * TreeNode left;
- * TreeNode right;
- * TreeNode() {}
- * TreeNode(int val) { this.val = val; }
- * TreeNode(int val, TreeNode left, TreeNode right) {
- * this.val = val;
- * this.left = left;
- * this.right = right;
- * }
- * }
- */
- class Solution {
- // 主函数,计算伪回文路径的数量
- public int pseudoPalindromicPaths(TreeNode root) {
- return dfs(root, 0);
- }
-
- // 递归函数,计算以当前节点为起点的伪回文路径的数量
- public int dfs(TreeNode cur, int status) {
- if (cur == null) {
- return 0;
- }
- // 使用异或运算更新当前节点的状态
- status ^= 1 << cur.val;
- // 如果当前节点是叶子节点,判断路径是否为伪回文路径
- if (cur.left == null && cur.right == null) {
- return status == (status & (-status)) ? 1 : 0;
- }
- // 递归遍历左右子树
- return dfs(cur.left, status) + dfs(cur.right, status);
- }
- }

- /**
- * Definition for a binary tree node.
- * public class TreeNode {
- * int val;
- * TreeNode left;
- * TreeNode right;
- * TreeNode() {}
- * TreeNode(int val) { this.val = val; }
- * TreeNode(int val, TreeNode left, TreeNode right) {
- * this.val = val;
- * this.left = left;
- * this.right = right;
- * }
- * }
- */
- class Solution {
- int ans = 0;
- public int pseudoPalindromicPaths (TreeNode root) {
- dfs(root,0);
- return ans;
- }
- public void dfs(TreeNode root, int status){
- if(root == null){
- return ;
- }
- status ^= 1 << root.val;
- if(root.left == null && root.right == null){
- if(status == 0 || (status&(status-1))==0){
- ans++;
- }
- }
- if(root.left!=null) dfs(root.left, status);
- if(root.right!=null) dfs(root.right, status);
- }
- }

26
828. 统计子串中的唯一字符 - 力扣(LeetCode)
- class Solution {
- public int uniqueLetterString(String s) {
- // 假设一个字母出现 2 次或者3 次,下标分别为 i,j,k,这里相当于记录 i 的下标
- // 出现 2 次的时候 i 相当于 -1。
- int[] prePreIndex = new int[128];
- int[] preIndex = new int[128];// 这里相当于记录 j 的下标
- Arrays.fill(prePreIndex, -1);
- Arrays.fill(preIndex, -1);
- char[] chars = s.toCharArray();
- int res = 0;
- for (int i = 0; i < chars.length; i++) {
- int cur = chars[i];
- // 这里只计算一个字母出现 2 次以及 2 次以上的(不包含最后一个字母构成的个数)。
- if (preIndex[cur] > -1)
- res += (preIndex[cur] - prePreIndex[cur]) * (i - preIndex[cur]);
- // 更新 prePreIndex 和 preIndex 。
- prePreIndex[cur] = preIndex[cur];
- preIndex[cur] = i;
- }
- // 这里再来计算字母只出现 1 次的情况以及重复的时候最后一个字母构成的个数。
- for (int i = 0; i < 128; i++) {
- if (preIndex[i] > -1)
- res += (preIndex[i] - prePreIndex[i]) * (s.length() - preIndex[i]);
- }
- return res;
- }
- }

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

28
- class FrontMiddleBackQueue {
- LinkedList<Integer> preq;
- LinkedList<Integer> proq;
- int pren,pron;
- public FrontMiddleBackQueue() {
- pren=0;
- pron=0;
- preq=new LinkedList<Integer>();
- proq=new LinkedList<Integer>();
- }
-
- public void pushFront(int val) {
- preq.offerFirst(val);
- pren++;
- if(pren>pron){
- proq.offerFirst(preq.pollLast());
- pren--;
- pron++;
- }
- }
-
- public void pushMiddle(int val) {
- preq.offerLast(val);
- pren++;
- if(pren>pron){
- proq.offerFirst(preq.pollLast());
- pren--;
- pron++;
- }
- }
-
- public void pushBack(int val) {
- proq.offerLast(val);
- pron++;
- if(pron>pren+1){
- preq.offerLast(proq.pollFirst());
- pren++;
- pron--;
- }
- }
-
- public int popFront() {
- int res=-1;
- if(preq.isEmpty()&&!proq.isEmpty()){
- res=proq.pollFirst();
- pron--;
- }else if(!preq.isEmpty()){
- res=preq.pollFirst();
- pren--;
- if(pron>pren+1){
- preq.offerLast(proq.pollFirst());
- pren++;
- pron--;
- }
- }
- // System.out.println(res);
- return res;
- }
-
- public int popMiddle() {
- int res=-1;
- if(pron>pren){
- res=proq.pollFirst();
- pron--;
- }else if(pron==pren&&pron!=0){
- res=preq.pollLast();
- pren--;
- if(pron>pren+1){
- preq.offerLast(proq.pollFirst());
- pren++;
- pron--;
- }
- }
- // System.out.println(res);
- return res;
- }
-
- public int popBack() {
- int res=-1;
- if(!proq.isEmpty()){
- res=proq.pollLast();
- pron--;
- if(pren>pron){
- proq.offerFirst(preq.pollLast());
- pren--;
- pron++;
- }
- }
- // System.out.println(res);
- return res;
- }
- }
-
- /**
- * Your FrontMiddleBackQueue object will be instantiated and called as such:
- * FrontMiddleBackQueue obj = new FrontMiddleBackQueue();
- * obj.pushFront(val);
- * obj.pushMiddle(val);
- * obj.pushBack(val);
- * int param_4 = obj.popFront();
- * int param_5 = obj.popMiddle();
- * int param_6 = obj.popBack();
- */

29
2336. 无限集中的最小数字 - 力扣(LeetCode)
- class SmallestInfiniteSet {
- boolean[] used;
- int idx;
- public SmallestInfiniteSet(){
- used = new boolean[1001];
- idx = 1;
- }
-
- public int popSmallest(){
- while(used[idx]) ++idx;
- used[idx] = true;
- return idx;
- }
-
- public void addBack(int num){
- if(used[num]) used[num] = false;
- if(idx > num) idx = num;
- }
- }
-
- /**
- * Your SmallestInfiniteSet object will be instantiated and called as such:
- * SmallestInfiniteSet obj = new SmallestInfiniteSet();
- * int param_1 = obj.popSmallest();
- * obj.addBack(num);
- */

30
1657. 确定两个字符串是否接近 - 力扣(LeetCode)
长度相等 字符集相等 频率数组相等
- class Solution {
- public boolean closeStrings(String word1, String word2) {
- int n = word1.length();
- if (n != word2.length()) return false;
- if (word1.equals(word2)) return true;
-
- int[] count1 = new int[128];
- int[] count2 = new int[128];
- byte[] bytes = new byte[n];
- word1.getBytes(0, n, bytes, 0); // Faster than String.toCharArray().
- for (byte b : bytes) count1[b]++;
-
- word2.getBytes(0, n, bytes, 0);
- for (byte b : bytes) count2[b]++;
-
- int maxFreq = 0;
- for (int i = 'a'; i <= 'z'; i++)
- maxFreq = Math.max(maxFreq, Math.max(count1[i], count2[i]));
-
- byte[] freq = new byte[maxFreq + 1];
- int count = 0;
- for (int i = 'a'; i <= 'z'; i++) {
- int c1 = count1[i];
- int c2 = count2[i];
- if (c1 == 0 ^ c2 == 0) return false;
- if (c1 == 0) continue;
- int f1 = ++freq[c1];
- int f2 = --freq[c2];
- if (f1 == 1) count++; else if (f1 == 0) count--;
- if (f2 == -1) count++; else if (f2 == 0) count--;
-
- }
-
- return count == 0;
-
- }
- }

写完这一个月,老实说我觉得收获不大,每天都是不一样的题型,还是得系统性刷才有效果。然后我觉得我刷题还不太行,稍微难一点就是cv侠了,还要加油啊,下个月继续吧。当然这个我之后还要再都看一遍的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。