跳到主要内容

去哪儿网AI面试

一、数组和链表的区别?

1. 存储方式

  • 数组:内存中连续存储,支持随机访问(通过索引直接访问元素),时间复杂度 O(1)
  • 链表:内存中非连续存储,通过指针连接节点,需要从头遍历才能找到指定位置,访问复杂度 O(n)

2. 插入/删除效率

  • 数组:插入或删除元素需要移动后续元素,平均时间复杂度 O(n)
  • 链表:已知节点后,可以在 O(1) 时间完成插入和删除,但查找目标位置是 O(n)

3. 适用场景

  • 需要频繁随机访问数组更合适
  • 需要频繁插入/删除链表更合适,尤其是在中间插入删除元素时。

二、Java的垃圾回收机制GC以及常见的垃圾回收器有哪些?

1. GC机制简述 Java通过垃圾回收器(GC)自动管理堆内存,主要任务:

  • 标记(Mark):找出所有存活对象(从GC Roots开始可达)。
  • 清除(Sweep):回收不可达对象的内存。
  • 压缩(Compact):整理碎片,让内存连续,避免频繁分配失败。

2. 常见垃圾回收器

回收器特点适用场景
Serial单线程,Stop-The-World,简单高效单核或Client模式
Parallel (吞吐量优先)多线程,追求吞吐量CPU核心多、批处理应用
CMS (低延迟)并发标记清除,减少STW时间响应时间敏感应用
G1分区回收,支持可预测的停顿时间大堆、低延迟服务
ZGC / Shenandoah超低延迟,停顿时间毫秒级大内存、实时系统

三、对MySQL索引的理解,为什么它可以加快查询速度?

1. 索引原理 MySQL常用索引结构是 B+树索引,它能保持数据有序,并支持范围查找。通过B+树多层节点的指针,可以快速缩小查询范围,而不是全表扫描。

2. 性能提升原因

  • B+树查询的时间复杂度是 O(log n),而全表扫描是 O(n)
  • 叶子节点存储指向数据行的指针(或主键),可直接定位数据行。
  • 索引有利于排序和分组,减少额外的排序操作。

3. 注意事项

  • 合理设计索引(区分单列索引、联合索引、覆盖索引)。
  • 过多索引会影响写性能(更新时需要维护索引)。

四、如何设计一个简单的日历系统,支持事件的创建、更新及提醒功能?

功能需求:

  • 事件创建:用户可添加日程事件(标题、开始时间、结束时间、提醒时间)。
  • 事件更新:支持修改和删除。
  • 提醒功能:在指定时间发送通知。

设计思路:

  1. 数据库表设计(MySQL)
CREATE TABLE event (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
title VARCHAR(100) NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME,
remind_time DATETIME,
status TINYINT DEFAULT 1, -- 1正常 0删除
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
  1. 业务流程
  • 创建/更新:写入数据库,并写入消息队列(用于提醒)。
  • 提醒功能:定时任务扫描即将到达提醒时间的事件,推送到MQ,由消费者异步发送通知(如邮件/短信/APP推送)。

消息队列具体会选择什么工具实现?为什么选择它?

推荐使用 RabbitMQ / Kafka

  • RabbitMQ:可靠性好,支持延迟队列,适合实时提醒。
  • Kafka:吞吐量大,适合高并发日志类场景。 对于提醒系统,RabbitMQ 更合适,可直接使用延迟插件或死信队列实现定时提醒。

如果在这个系统中事件提醒的时间点非常密集,比如每秒都有大量提醒需要发送,你会如何优化消息队列的处理效率?

批量消费:消费者批量拉取消息,批量推送提醒,减少IO开销。

水平扩展:增加消费者实例,通过队列分区提高吞吐量。

异步推送:使用线程池、异步HTTP请求,提高并发处理能力。

降级与限流:当提醒量超出系统承载能力,可对低优先级提醒降级(如延迟几秒)。

缓存+预取:提前把未来几分钟的提醒加载到Redis,减少数据库查询压力。