和为 K 的子数组
java
package hot100;
import java.util.*;
public class lc560 {
/*和为 K 的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
*/
//前缀和
public int subarraySum(int[] nums, int target){
Map<Integer,Integer> map = new HashMap<>();
int sum = 0;
int ans = 0;
map.put(0,1);
for(int i = 0; i < nums.length; i++){
sum+=nums[i];
if(map.containsKey(sum - target)){
ans+=map.get(sum - target);
}
map.put(sum, map.getOrDefault(sum,0)+1);
}
return ans;
}
public static void main(String[] args) {
lc560 solution = new lc560();
int[] nums = new int[]{1,1,1};
System.out.println("答案:" + solution.subarraySum(nums,2));
}
}
滑动窗口最大值
java
package hot100;
import java.util.ArrayList;
import java.util.*;
public class lc239 {
/*
239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。
你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
*
* */
public int[] maxSlidingWindow(int[] nums, int target){
//第一次做还是不会
//堆?[不能限定位置]
int[] ans = new int[nums.length - target +1];
//滑动窗口
//我只能想到暴力?
//单调队列,
Deque<Integer> queue = new ArrayDeque<>();
//存的是下标,所以需要nums【下标】才能得到具体的数值去比大小
for(int i = 0; i < nums.length; i++){
//大于,从Last开始的
//入栈就是比Last这段
while(queue.size() >0 && nums[i]>=nums[queue.getLast()]){
queue.removeLast();
}
queue.addLast(i);
//出栈才是first这头,这个队列从大到小
if((i - target +1) >0 && queue.getFirst() < i - target + 1){
queue.removeFirst();
}
if(i >= target -1){
ans[i-target+1] = nums[queue.getFirst()];
}
}
return ans;
}
public static void main(String[] args) {
int[] nums = new int[]{1,3,-1,-3,5,3,6,7};
int k = 3;
lc239 solution = new lc239();
int[] sout = solution.maxSlidingWindow(nums, k);
for(int num : sout){
System.out.println(num+ " ");
}
}
}
最小覆盖子串
java
package hot100;
import java.util.Arrays;
public class lc76 {
//写了一半,没写完整,只能说有思路, 针对后面缩小窗口要用while
/*最小覆盖子串
给定两个字符串 s 和 t,长度分别是 m 和 n,返回 s 中的 最短窗口 子串,
使得该子串包含 t 中的每一个字符(包括重复字符)。如果没有这样的子串,返回空字符串 ""。
测试用例保证答案唯一。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
* */
public String minWindow(String s, String p){
//先找到然后开始 int[字母]是满足的,然后看看能不能缩减
//后续判断
String ans = "";
int[] pLen = new int[128];
int[] sLen = new int[128];
for(int i = 0; i < p.length(); i++){
pLen[p.charAt(i) ]++;
}
int psum = 0;
for(int i = 0; i < 128; i++){
if(pLen[i]>0){
psum++;
}
}
int left = 0;
int right = 0;
int ssum = 0;
while(right < s.length()){
sLen[s.charAt(right)]++;
if(pLen[s.charAt(right)] >0 && sLen[s.charAt(right)] == pLen[s.charAt(right)]){
ssum++;
}
while(ssum == psum && left <= right) {
//里面的数量肯定达标了,字母缩减?
if (sLen[s.charAt(left)] > pLen[s.charAt(left)]) {
sLen[s.charAt(left)]--;
left++;
} else {
break;
}
}
//已经是最小窗口
if (ssum == psum) {
String temp = s.substring(left, right + 1);
if (ans.isEmpty() || temp.length() < ans.length()) {
ans = temp;
}
// 为了继续寻找更小的窗口,左指针右移一位,并更新计数
char leftChar = s.charAt(left);
sLen[leftChar]--;
//相减也要看ssum
if (pLen[leftChar] > 0 && sLen[leftChar] < pLen[leftChar]) {
ssum--;
}
left++;
}
right++;
}
return ans;
}
public static void main(String[] args) {
String s = "ADOBECODEBANC";
String t = "ABC";
lc76 solution = new lc76();
System.out.println(solution.minWindow(s, t));
}
}
最大子数组和
java
package hot100;
import java.util.*;
public class lc53 {
/*
最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
* */
//贪心?
public int maxSubArray(int[] nums){
int[] dp = new int[nums.length];
int max = nums[0];
dp[0] = nums[0];
for(int i = 1; i < nums.length; i++){
dp[i] = Math.max(dp[i-1], 0) + nums[i];
max = Math.max(max,dp[i]);
}
return max;
}
//优化的版本,只需要pre就行
public int maxSubArray1(int[] nums){
int pre = nums[0];
int max = nums[0];
for(int i = 1; i < nums.length; i++){
pre= Math.max(pre, 0) + nums[i];
max = Math.max(max,pre);
}
return max;
}
public static void main(String[] args) {
lc53 solution = new lc53();
int[] nums = new int[]{-2,1,-3,4,-1,2,1,-5,4};
System.out.println("答案:" + solution.maxSubArray1(nums));
}
}
合并区间
java
package hot100;
import java.util.*;
public class lc56 {
//第一次没写出来
/*
* 56. 合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。
*请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].*/
public int[][] merge(int[][] intervals){
//排序不会写,get[i],api不会写
Arrays.sort(intervals,(p,q)->p[0]-q[0]);
//int[][]用list
List<int[]> ans = new ArrayList<>();
for(int[] interval : intervals){
if(ans.size() >0 && ans.get(ans.size() -1)[1]>= interval[0]){
//按顺序存的,按顺序取
ans.get(ans.size() -1)[1] = Math.max(ans.get(ans.size()-1)[1],interval[1]);
}else{
ans.add(interval);
}
}
//list转数组
return ans.toArray(new int[ans.size()][]);
}
public static void main(String[] args) {
lc56 soulution = new lc56();
int[][] nums = new int[][] {{1, 3}, {2, 6}, {8, 10}, {15, 18}};
int[][] result = soulution.merge(nums);
for(int[] s : result){
System.out.println(Arrays.toString(s));
}
}
}
随机知识
1. AQS是什么?ReentrantLock的实现原理?
-
AQS (AbstractQueuedSynchronizer)是Java并发包的核心框架,基于state状态变量 + CLH队列实现。
-
加锁流程:线程尝试CAS将state从0改为1,成功则获取锁并设置独占线程;失败则进入CLH队列尾部自旋或挂起等待。
-
可重入:state记录重入次数,获取一次+1,释放一次-1,为0时锁完全释放。
-
公平锁 :先判断队列是否有前驱节点,有则排队;非公平锁:上来就CAS抢锁,抢不到再排队。
-
与Synchronized区别:ReentrantLock可手动控制加锁释放、支持公平锁、可中断获取锁、可tryLock超时,但需手动释放。
2. volatile关键字的作用和原理?
两大作用:
-
可见性:一个线程修改volatile变量后,立即刷新到主内存,其他线程读时强制从主内存重新加载。
-
禁止指令重排序:通过内存屏障实现,保证volatile写之前的操作不会被重排到写之后,读之后的操作不会被重排到读之前。
-
内存屏障:写操作前插入StoreStore屏障,写后插入StoreLoad屏障;读操作后插入LoadLoad和LoadStore屏障。
-
不保证原子性:i++这种复合操作,volatile无能为力,仍需synchronized或Atomic类。
-
应用场景:单例模式的双重检查锁定(DCL),用volatile修饰instance防止指令重排导致半初始化对象逃逸。
3. Spring事务的传播行为有哪些?常用的两个解释一下?
七种传播行为,最常用:
-
REQUIRED(默认):有事务就加入,没有就新建。调用方有事务,被调用方就共享一个事务,一方回滚全部回滚。
-
REQUIRES_NEW:始终新建独立事务,如果调用方有事务就挂起它。二者互不影响,各自提交或回滚。
-
其他:SUPPORTS(有就加入没有也无所谓)、NOT_SUPPORTED(非事务执行)、MANDATORY(必须有事务否则抛异常)、NEVER(必须没事务)、NESTED(嵌套事务,通过保存点实现部分回滚)。
-
坑点 :同类方法调用不走代理,事务失效;非public方法默认不走事务;异常被catch住不抛出,事务不回滚(除非手动设置
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly())。
4. Redis的缓存穿透、击穿、雪崩是什么?如何解决?
-
缓存穿透:查询不存在的数据,缓存没命中,请求直达数据库。
- 解决:布隆过滤器过滤不存在的key;或把null值也缓存,设置较短过期时间。
-
缓存击穿:热点key过期瞬间,大量并发请求同时打到数据库。
- 解决:互斥锁(只有一个线程去加载DB并回写缓存,其他等待);或逻辑过期(永不过期,后台异步刷新)。
-
缓存雪崩:大量key同时过期,或Redis宕机,所有请求打到数据库。
- 解决:过期时间加随机值打散;Redis集群高可用(主从+哨兵);限流降级保护数据库;多级缓存。
5. MySQL的事务隔离级别有哪些?InnoDB如何解决幻读?
四种隔离级别:
-
读未提交:可读到未提交数据,有脏读问题。
-
读已提交:只能读已提交数据,解决脏读,但有不可重复读和幻读。
-
可重复读(InnoDB默认):同一事务多次读取结果一致,解决不可重复读,部分解决幻读。
-
串行化:事务完全串行执行,解决所有问题,性能最差。
InnoDB如何解决幻读?
-
MVCC快照读:普通SELECT读的是快照版本,不受其他事务插入影响,解决快照幻读。
-
间隙锁+临键锁 :
SELECT ... FOR UPDATE这种当前读,会在查询范围加间隙锁(锁住索引间隙)和临键锁(行锁+间隙锁),阻止其他事务在范围内插入数据,解决当前读的幻读。
碎碎念:后续会更新每天学习的八股和算法 题,开始准备秋招的第65天。努力连续更新100天!以后每天就按,秋招项目【java +agent】,科研,必做项目,算法,八股,锻炼身体来总结。
总结:继续总结项目
1.hot100 【acm】 14/100 ,有点手感了,2到3h,快速把hot100过一遍【20天看看行不行】
2.秋招项目,【java 项目】,
【agent 项目 】,看来一遍打算面向面试拷打一下
3.科研。确定方向就搞就可以了
4.实习;上班干杂活2h
6.背八股,无
7.锻炼身体,无
要点:坚持,今天还是松懈了点,一天摸鱼加学习能学6-7h大概