字母异位词分组
一个字符串数组,将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
输入: strs = "eat", "tea", "tan", "ate", "nat", "bat"
输出: \["bat","nat","tan","ate","eat","tea"]
思路:遍历每一个字符串,利用HashMap,先按字字典序生成key,value为当前字符串,最后返回所有values值
语法:s.toCharArray->char\[\] arr Arrays.sort(arr)->有序的char\[\] arr
new String(arr)->得到key
map.computeIfAbsent(key,k->new ArrayList<>()).add(s)->构建Hash
new ArrayList<>(map.values())->提取value值
三数之和
给出一个数组,找出i!=j!=k,i,j,k处和为0的三元组,并返回三元组的集合
思路:排序+固定一个点+L,R双指针
排序之后,相同的数字会挤在一起,"去重"就从"全局判重"变成了"相邻跳过"
核心思路:固定一个 + 两数之和的双指针
1. 整体框架
文本
编辑
数组排好序: -4 -1 -1 0 1 2 2 3
i L--------------->R
- 枚举
i(三元组里最小的那个数),范围[0, n-3]; - 在
i右边的区间[i+1, n)里,用 L、R 两个指针 找两数之和等于-nums[i]; - 因为数组有序:
sum < target→ 太小,L++(只能往右挪才变大)sum > target→ 太大,R--(只能往左挪才变小)sum == target→ 命中,记录答案,然后 L++、R-- 继续找
这就是经典的「三数之和 = 排序 + 固定一个 + 两数之和双指针」,把 O(n³) 压到 O(n²)。
2. 去重的三个位置(本题唯一容易写错的地方)
表格
| 位置 | 写法 | 原因 |
|---|---|---|
| i 去重 | if (i > 0 && nums[i] == nums[i-1]) continue; |
同一个值作为第一个数,产生的三元组必然重复 |
| L 去重 | 命中后 while (L < R && nums[L] == nums[L+1]) L++; |
避免第二个数重复 |
| R 去重 | 命中后 while (L < R && nums[R] == nums[R-1]) R--; |
避免第三个数重复 |
⚠️ 关键细节 :去重是「和前一个比」,不是「和后一个比完就停」。 L/R 去重必须在命中之后做 ,做完还要再各挪一步(L++; R--;),否则死循环。
接雨水
思路:每一轮先更新 两边的最高墙,再比较 谁矮------矮的那一侧,它的水位已经被锁死,可以当场结算,一格一格的算,然后累加
1.java的hashmap底层实现?是不是线程安全的?
┌─────────────────────────────────────────────────────────────┐
│ HashMap 底层结构 │
│ │
│ 数组 + 链表 + 红黑树 │
│ │
│ put 流程: │
│ 1. hash 扰动 │
│ 2. 计算索引 (n-1) & hash │
│ 3. 空位直接放,非空遍历链表/红黑树 │
│ 4. 链表长度 ≥ 8 且容量 ≥ 64 → 转红黑树 │
│ 5. size > 阈值 → 扩容(容量翻倍) │
│ │
│ 关键参数: │
│ 容量 16,负载因子 0.75,树化阈值 8,链化阈值 6 │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 线程安全性 │
│ │
│ HashMap:❌ 不安全 │
│ ├─ JDK 7:头插法扩容 → 死循环 │
│ └─ JDK 8:尾插法 → 不死循环,但仍丢数据 │
│ │
│ 替代方案: │
│ ├─ Hashtable:全表锁,性能差 │
│ ├─ synchronizedMap:包装锁,一般 │
│ └─ ConcurrentHashMap:CAS + synchronized,高并发首选 │
│ │
└─────────────────────────────────────────────────────────────┘
为什么容量必须的2的幂次方?
在计算数组索引时加快运算,& 比 % 快得多
使分布均匀 ,n - 1 低位全是 1,hash 的每一位都能影响索引
index = hash % capacity == hash & (capacity - 1)
ConcurrentHashMap 的核心
JDK 7:分段锁(Segment)
-
把整个 map 分成多个 Segment
-
每个 Segment 独立加锁
-
不同 Segment 可以并发访问
JDK 8:CAS + synchronized
-
取消 Segment,直接用 Node 数组
-
空节点用 CAS 插入
-
非空节点用 synchronized 锁头节点
-
锁粒度更细,并发度更高
2.string stringbuilder stringbuffer的区别
┌─────────────────────────────────────────────────────────────┐
│ String(不可变) │
│ │
│ final char\[\] value │
│ 每次修改 → 创建新对象 │
│ 线程安全(因为不可变) │
│ 适合:少量、不常变的字符串 │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ StringBuilder(可变,不安全) │
│ │
│ char\[\] value(可修改) │
│ 直接改数组,不创建新对象 │
│ 线程不安全(无锁) │
│ 适合:单线程频繁拼接 │
│ 性能:最高 │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ StringBuffer(可变,安全) │
│ │
│ char\[\] value(可修改) │
│ 所有方法加 synchronized │
│ 线程安全 │
│ 适合:多线程频繁拼接 │
│ 性能:较高(有锁开销) │
│ │
└─────────────────────────────────────────────────────────────┘
String:不可变,线程安全,频繁修改性能差。
StringBuilder:可变,线程不安全,单线程性能最高。
StringBuffer:可变,线程安全,多线程用,有锁开销
3.优化sql的几个角度
开启慢查询日志定位耗时超过阈值(比如1秒)的SQL
首先利用EXPLAIN。 重点看 type 是不是 ALL(全表扫描,索引失效)、key 是不是 NULL(没用索引)
第二步针对性优化:
最高优先是索引优化。 一是设计合理的联合索引;二是尽量用覆盖索引,让查询列都在索引里,避免回表;三是注意刚才说的索引失效场景,别写了索引却用不上。
其次是写法优化。 永远不要 SELECT *,只查需要的列;
再次是利用join替代子查询,同时 确保小表驱动大表。
最后是架构层面。 如果单表优化到极限还不够,才考虑分库分表、读写分离或者加缓存层。
B+ 树的核心优势:数据按索引列有序排列,支持二分查找。
4.分库分表的概念,以及怎么实现
两个维度
| 维度 | 含义 |
|---|---|
| 分库 | 把数据分散到多个数据库实例 |
| 分表 | 把一张表拆成多张表 |
┌─────────────────────────────────────────────────────────────┐
│ 分库分表的四种方式 │
│ │
│ 1. 垂直分库 │
│ 按业务拆分:用户库、订单库、商品库 │
│ │
│ 2. 垂直分表 │
│ 按字段拆分:常用字段一张表,不常用字段另一张表 │
│ │
│ 3. 水平分库 │
│ 同业务数据分散到多个库:order_db1、order_db2 │
│ │
│ 4. 水平分表 │
│ 同业务数据分散到多张表:order_0、order_1、order_2 │
│ │
└─────────────────────────────────────────────────────────────┘
5.jvm的垃圾回收机制是怎么工作的
┌─────────────────────────────────────────────────────────────┐
│ JVM 垃圾回收 │
│ │
│ 1. 回收什么:堆中的垃圾对象 │
│ │
│ 2. 怎么判断:可达性分析(GC Roots) │
│ │
│ 3. 怎么回收: │
│ ├─ 标记-清除(有碎片) │
│ ├─ 复制(无碎片,浪费一半) │
│ └─ 标记-整理(无碎片,移动开销) │
│ │
│ 4. 分代模型: │
│ ├─ 新生代(Eden + S0 + S1)→ 复制算法 → Minor GC │
│ └─ 老年代 → 标记-整理 → Full GC │
│ │
│ 5. 回收器: │
│ ├─ Serial / ParNew / Parallel │
│ ├─ CMS(低延迟) │
│ ├─ G1(可预测停顿) │
│ └─ ZGC(超低延迟) │
│ │
└─────────────────────────────────────────────────────────────┘
6.高并发中如何保证线程安全
使用互斥锁、volatile可见性、线程安全的集合类、原子类、semaphore控制同时访问共享资源的线程数、ThreadLocal线程局部变量等。