LeetCode100个人题解+java面经总结

螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

思路:模拟移动,定义4个边界,top,bottom,left,right

  1. top:上边界,初始为 0。

  2. bottom:下边界,初始为 m - 1。

  3. left:左边界,初始为 0。

  4. right:右边界,初始为 n - 1。

整个遍历过程分为四个动作,循环执行,直到边界相撞:

  • 向右走 :从 left 走到 right,走完后,上边界 top 向下缩一格(top++)。

  • 向下走 :从 top 走到 bottom,走完后,右边界 right 向左缩一格(right--)。

  • 向左走 :从 right 走到 left,走完后,下边界 bottom 向上缩一格(bottom--)。

  • 向上走 :从 bottom 走到 top,走完后,左边界 left 向右缩一格(left++)。

核心易错点(防重复)

在向下走和向上走之前,必须 加一个条件判断(if (top <= bottom) 和 if (left <= right))。

为什么?因为如果矩阵不是正方形(比如只有一行,或者只有一列),走到最后可能会原路返回,导致元素被重复加入结果集。加了这个判断,就可以保证只有在空间还没被榨干时才继续。

class Solution {

public List spiralOrder(int\[\]\[\] matrix) {

List res=new ArrayList<>();

if(matrix==null)return res;

int n=matrix.length;

int m=matrix0.length;

int top=0;

int bottom=n-1;

int left=0;

int right=m-1;

while(top<=bottom&&left<=right){

for(int i=left;i<=right;i++){

res.add(matrixtopi);

}

top++;

for(int i=top;i<=bottom;i++){

res.add(matrixiright);

}

right--;

if(top<=bottom){

for(int i=right;i>=left;i--){

res.add(matrixbottomi);

}

bottom--;

}

if(left<=right){

for(int i=bottom;i>=top;i--){

res.add(matrixileft);

}

left++;

}

}

return res;

}

}

环形链表

判断有无环形

思路:快慢指针是否会重合

/**

* Definition for singly-linked list.

* class ListNode {

* int val;

* ListNode next;

* ListNode(int x) {

* val = x;

* next = null;

* }

* }

*/

public class Solution {

public boolean hasCycle(ListNode head) {

if(head==null)return false;

ListNode slow=head;

ListNode fast=head;

while(fast!=null&&fast.next!=null){

slow=slow.next;

fast=fast.next.next;

if(slow==fast){

return true;

}

}

return false;

}

}

相交链表

两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

思路:a,b指针走到null后分别再重头走对方的路直到有相交,如果有则返回相交的点

最优雅的解法是双指针法。我们可以把它想象成两个人在走迷宫:

  • 指针 pA 在链表 A 上走,走到底后,去走链表 B。

  • 指针 pB 在链表 B 上走,走到底后,去走链表 A。

为什么这样能相遇?

假设链表 A 的独立部分长度为 a,链表 B 的独立部分长度为 b,相交部分的长度为 c。

  • pA 走过的总路程是:a + c + b(先走完 A,再走 B 的独立部分)。

  • pB 走过的总路程是:b + c + a(先走完 B,再走 A 的独立部分)。

你会发现,两者的总路程完全相等 (都是 a + b + c)。

所以,如果两个链表有交点,它们一定会同时到达交点 。

如果两个链表没有交点(即 c = 0),它们会同时走到对方的终点(null),此时返回 null 即可。

/**

* Definition for singly-linked list.

* public class ListNode {

* int val;

* ListNode next;

* ListNode(int x) {

* val = x;

* next = null;

* }

* }

*/

public class Solution {

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

if(headAnull||headBnull)return null;

ListNode pa=headA;

ListNode pb=headB;

while(pa!=pb){

pa=(pa==null)?headB:pa.next;

pb=(pb==null)?headA:pb.next;

}

return pa;

}

}

回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

思路:利用快慢指针先找到中点->反转后半段链表->依此比较前半段链表和反转后的后半段链表值

设计:slow和fast,反转链表

/**

* Definition for singly-linked list.

* public class ListNode {

* int val;

* ListNode next;

* ListNode() {}

* ListNode(int val) { this.val = val; }

* ListNode(int val, ListNode next) { this.val = val; this.next = next; }

* }

*/

class Solution {

public ListNode reverse(ListNode head){

ListNode pre=null;

ListNode cur=head;

while(cur!=null){

ListNode temp=cur.next;

cur.next=pre;

pre=cur;

cur=temp;

}

return pre;

}

public boolean isPalindrome(ListNode head) {

if(headnull||head.nextnull)return true;

ListNode slow=head;

ListNode fast=head;

while(fast.next!=null&&fast.next.next!=null){

slow=slow.next;

fast=fast.next.next;

}

ListNode reverp=reverse(slow.next);

ListNode p1=head;

ListNode p2=reverp;

boolean flag=true;

while(p2!=null){

if(p1.val!=p2.val){

flag=false;

break;

}

p1=p1.next;

p2=p2.next;

}

return flag;

}

}

细节:fast.next!=null&&fast.next.next!=null 只有这样slow才能停到中点位置

用友

3.HashMap的底层原理,介绍一些put流程

底层原理:

JDK 8 以后,HashMap 底层是数组 + 链表 + 红黑树。数组(哈希桶)是主干,通过哈希算法定位。当发生哈希冲突时,以链表形式存储;当链表长度大于 8 且数组长度大于等于 64 时,链表会转化为红黑树,将查询时间复杂度从 O(n) 降为 O(log n)。

put 流程(核心要点):

  1. 计算哈希 :调用 hash(key)。

  2. 计算下标:通过 (n - 1) & hash 计算数组下标(等价于取模,但位运算更快)。

  3. 处理冲突:

    • 若桶为空,直接新建 Node 节点放入。

    • 若桶不为空:比较 hash 和 key(== 或 equals)。如果完全相等,则覆盖旧值。

    • 如果不相等,判断是链表还是红黑树。是红黑树则插入树中;是链表则尾插法插入。插入后如果链表长度达到 8,触发树化逻辑。

  4. 扩容判断 :插入成功后,如果 size > threshold(容量 * 0.75 负载因子),则触发扩容(翻倍)。

4.SpringBean的生命周期,SpringBean有几种实例化方式

生命周期(四大阶段):

  1. 实例化(Instantiation):通过反射调用构造器创建 Bean 实例。

  2. 属性赋值(Population):依赖注入(DI),填充属性。

  3. 初始化(Initialization):

    • 调用 BeanNameAware、BeanFactoryAware 等 Aware 接口。

    • 执行 BeanPostProcessor 的 postProcessBeforeInitialization。

    • 执行 @PostConstruct 注解的方法,或实现 InitializingBean 的 afterPropertiesSet,或自定义的 init-method。

    • 执行 BeanPostProcessor 的 postProcessAfterInitialization(AOP 动态代理通常在这里生成)。

  4. 销毁(Destruction) :容器关闭时,执行 @PreDestroy、DisposableBean 的 destroy 或自定义 destroy-method。

  5. 构造器实例化(最常见):Spring 底层通过反射调用类的无参或带参构造函数。

  6. 静态工厂实例化 :调用某个类的 static 静态方法返回对象。

  7. 实例工厂实例化:先实例化工厂类本身,再调用该工厂实例的普通方法返回对象。

  8. FactoryBean 实例化 :调用 FactoryBean 的 getObject() 方法返回对象。

5.方法重载和方法重写的区别

  • 重载(Overload) :发生在同一个类 中,方法名相同,但参数列表不同 (类型、个数、顺序)。它属于编译期多态(静态绑定)。

  • 重写(Override) :发生在子父类之间 (或接口实现),方法名、参数列表、返回类型必须相同。它属于运行期多态(动态绑定)。

资深视角理解: 重载是为了提供同名但不同输入的处理逻辑,提升 API 易用性;重写是为了多态扩展,让框架能面向接口编程。

6.==和equals的区别

  • == :如果是基本类型,比较数值;如果是引用类型,比较内存地址(是否指向同一个对象)。

  • equals :是 Object 类的方法,默认实现就是 ==。但像 String、Integer 等类重写了该方法,用来比较内容。

  • 规范 :重写 equals 必须同时重写 hashCode,保证 equals 相等的两个对象 hashCode 也相等,否则会导致 HashMap 等集合出错。

7.Java常用集合类和他们的特点

  • List(有序、可重复):

    • ArrayList:基于动态数组,查快改慢(涉及数组复制),线程不安全。

    • LinkedList:基于双向链表,增删快,查慢,也可作队列。

  • Set(无序、不可重复):

    • HashSet:基于 HashMap 实现,无序。

    • LinkedHashSet:基于 LinkedHashMap,维护插入顺序

  • Map(键值对):

    • HashMap:数组+链表+红黑树,无序,线程不安全,性能高。

    • ConcurrentHashMap:线程安全,分段/CAS 优化,高并发首选。

      复制代码
                       Collection (接口)
                          │

      ┌─────────────────────┼─────────────────────┐

      │ │ │

      List (接口) Set (接口) Queue (接口)

      │ │ │

      ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐

      ArrayList LinkedList HashSet TreeSet ArrayDeque LinkedList

      (Vector) (LinkedHashSet) (PriorityQueue)

      复制代码
                          Map (接口)  <-- 独立于 Collection 的另一大顶层接口
                            │

      ┌───────────────────────┼───────────────────────┐

      │ │ │

      HashMap TreeMap Hashtable

      │

      LinkedHashMap

      (ConcurrentHashMap 等并发包下的类)

8.List和Set的区别是什么

  1. 重复性 :List 允许重复元素,Set 不允许(通过 equals 和 hashCode 判重)。

  2. 有序性 :List 是有序的(元素存入和取出的顺序一致,有索引),Set 是无序的(HashSet 无序,TreeSet 按大小排序,LinkedHashSet 按插入顺序)。

  3. 索引访问 :List 支持通过下标 get(i) 访问,Set 不支持。

9.Map怎么用,hashMap是有序的吗,有序的Map是哪个

  • 用法 :存储键值映射。遍历通常用 entrySet();线程安全场景用 ConcurrentHashMap。

  • HashMap 无序。

  • 有序的 Map:

    • 需要按插入顺序 存取:用 LinkedHashMap(底层加了双向链表)。

    • 需要按 Key 大小排序 :用 TreeMap(底层红黑树)。

10.抽象类和接口的区别

  • 抽象类:单继承,可以有构造函数、普通变量、普通方法,是对事物的抽象。

  • 接口:多实现,是对行为的规范。

  • 资深视角:优先使用接口,因为组合优于继承。只有在需要共享代码实现或维护一个稳定骨架时,才用抽象类(模板方法模式)。。

11.你是怎么理解IOC的

本质上是一种设计思想 ,把创建和管理对象的控制权,从业务代码移交给了 Spring 容器。以前是自己 new 对象(主动权在自己),现在告诉容器"我需要什么",容器在运行时把它"注入"进来(DI,依赖注入)。核心价值 :解耦。如果实现类换了(比如从 MySQL 换到 Oracle),只需要改配置,不需要改业务代码。

13.说一下你对SQL优化的理解

  1. 索引优化(最见效):

    • 使用 explain 看执行计划,确保命中索引(type 不为 ALL,key 不为 null)。

    • 遵守最左前缀原则,尽量使用覆盖索引(避免回表)。

    • 避免索引失效(如对索引列做函数运算、隐式类型转换、前导模糊查询 like '%x')。

  2. SQL 语句重写:

    • 避免 select *,只取需要的列。
相关推荐
chenbingjie_c1 小时前
C++ 继承全解析(从三种继承方式到菱形继承与内存布局)
开发语言·c++
文丑颜不良啊1 小时前
LeetCode1201~1220题目解析
java·leetcode
香蕉大香蕉(06敬畏市场)1 小时前
高频八股 · JVM(7):方法区、元空间与字符串常量池
java·jvm·ai·博客·八股·大厂实习
LazyHunter91 小时前
Spring Boot 3 微服务实践指南
java·spring boot
zzu123zsw1 小时前
从 SPI 契约到独立 ClassLoader 隔离:Spring Boot 插件热插拔架构设计与内存泄露防御
java·spring boot·后端·架构设计
文丑颜不良啊1 小时前
LeetCode1161~1180题目解析
java·leetcode
古法老登瞎折腾9 小时前
大数据智能运维实战(十一):运维 MCP 的工程化——权限、限流、审计与错误处理
java·大数据·运维·spring ai·mcp
飞Link9 小时前
什么是 AirSim?一文读懂无人机仿真平台
python·算法·无人机
学逆向的9 小时前
C 堆区内存管理
c语言·开发语言·网络安全