持续更新ing 11.14
想着每天都要刷每日一题的,但每次刷过了也没啥留下的,之后也容易忘,不如记录下来,一些知识,解题技巧,有趣的,碎碎念的。。。工作日就每日都更新,周末的题可能会留到周一更新。
1
第一天就难度升级
这题还是很好看懂的,这个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();
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.
中等题,但还是很简单的
这题其实就是层序遍历,填充他的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.
这个题最容易想到的是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 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 findRepeatedDnaSequences(String s) {
int n = s.length();
List ansL = new ArrayList<>();
if (n <= 10) return ansL;
Map 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 findRepeatedDnaSequences(String s) {
int n = s.length();
Map cMap = new HashMap<>() {{
put('A', 0);
put('C', 1);
put('G', 2);
put('T', 3);
}};
return new AbstractList() {
private final List list = new ArrayList<>();
private final Map 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
优化:
首先对每个字符串进行预处理,将其转换为一个包含字符出现情况的位向量(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.
简单题,我哭死
判断首尾是不是元音字符串就好了。
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;
}
}
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 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 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 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.
// // 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=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
715. Range 模块 - 力扣(LeetCode)
讲实话,我看不懂一点,这个之后还要仔细看看
// class RangeModule {
// TreeMap intervals;
// public RangeModule() {
// intervals = new TreeMap();
// }
// public void addRange(int left, int right) {
// Map.Entry entry = intervals.higherEntry(left);
// if (entry != intervals.firstEntry()) {
// Map.Entry 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 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 entry = intervals.higherEntry(left);
// if (entry != intervals.firstEntry()) {
// Map.Entry 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
用上树状数组(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];
}
}
这里写一点刷题感受吧,总结一点有规律性的,不同模块的东西吧。