109、单例模式优点
单例模式的优点包括:
-
全局访问:单例模式确保一个类只有一个实例,并提供一个全局访问点,方便在整个应用中访问该实例。
-
延迟初始化:实例在第一次使用时才被创建,这样可以节省资源,尤其是当实例的创建开销较大时。
-
控制实例数量:通过限制类的实例化,避免了对资源的过度消耗,适合需要共享资源的场景。
-
简化代码:使用单例模式可以减少全局变量的使用,从而降低代码复杂度,提高可维护性。
-
线程安全:在多线程环境中,单例模式可以通过合适的锁机制实现线程安全,确保只有一个实例被创建。
-
易于测试:单例模式可以通过依赖注入等方式进行替换和测试,提高了代码的可测试性。
在实际应用中,虽然单例模式有其优点,但也需要谨慎使用,以免造成代码的耦合和测试的复杂性。
110、 set和map有什么区别?
set
和 map
是 C++ STL(标准模板库)中提供的两种关联容器,它们有以下区别:
1. 存储的内容:
- set:存储唯一的元素,没有重复的值。它主要用于快速查找和插入。
- map:存储键值对(key-value pairs)。每个键都是唯一的,每个键对应一个值,可以通过键快速访问值。
2. 访问方式:
- set:可以通过迭代器遍历所有元素,但不能通过索引访问。查找元素时使用值。
- map:可以通过键访问对应的值,使用
map[key]
语法可以直接获取值。
3. 内部结构:
- set:通常实现为红黑树或平衡树,保持元素的有序性。
- map:也通常实现为红黑树,保证键的有序性,并允许通过键快速访问值。
4. 常用操作:
- set:常用操作包括插入、删除和查找元素。
- map:常用操作包括插入键值对、删除键值对和通过键查找值。
5. 性能:
- 对于查找、插入和删除操作,
set
和map
的平均时间复杂度都是 O(log n),但由于存储的内容不同,其具体性能表现会有所差异。
在选择使用 set
还是 map
时,可以根据需求来决定:如果只需要存储唯一值并进行查找,使用 set
;如果需要关联键值对,使用 map
。
111、红黑树怎么实现的?
红黑树是一种自平衡的二叉搜索树,具有以下性质:
每个节点是红色或黑色。
根节点是黑色。
每个叶子节点(NIL)是黑色。
红色节点的两个子节点都是黑色(没有两个红色节点相连)。
从任何节点到其每个叶子节点的所有路径都包含相同数量的黑色节点(黑高)。
红黑树的基本实现步骤:
节点结构: 每个节点通常包含:
key:存储的值。
color:节点的颜色(红色或黑色)。
left:左子节点的指针。
right:右子节点的指针。
parent:父节点的指针(用于修复树的平衡时使用)。
插入操作:
标准二叉搜索树插入:首先按二叉搜索树的规则插入节点。
修复:插入后,需要根据红黑树的性质进行调整,可能包括以下步骤:
如果新插入的节点是红色且其父节点也是红色,触发冲突。
根据叔父节点的颜色进行不同的修复:
如果叔父节点是红色,改变父节点和叔父节点的颜色为黑色,改变祖父节点为红色,然后继续向上检查。
如果叔父节点是黑色,需要进行旋转(左旋或右旋)和颜色调整,确保树的性质得到维护。
删除操作:
标准二叉搜索树删除:首先按二叉搜索树的规则删除节点。
修复:删除后,需要根据红黑树的性质进行调整,主要包括:
如果删除的节点是黑色,可能会导致黑色高度不平衡。
使用旋转和重新着色的方式修复树,使红黑树的性质得到维护。
旋转操作:
左旋:将节点的右子节点上升,原节点降为其左子节点。
右旋:将节点的左子节点上升,原节点降为其右子节点。
旋转操作在调整树的结构时非常关键。
112、数组和链表的优缺点对比?
113、线程池中怎么判断线程是否空闲(微信收藏)
设置条件变量加锁
判断该线程维护的任务队列是否为空
114、http请求怎么解析的 平时常用的正则表示式(微信收藏)
115、日志模块为什么用单例 你对单例模式的理解 用到哪些场景 日志有没有划分等级 具体怎么实现的
单例模式的理解
单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。其主要特点包括:
唯一性:确保在应用的整个生命周期内,某个类只有一个实例。
全局访问:可以方便地在整个应用中访问该实例。
延迟初始化:实例可以在第一次使用时创建,以节省资源。
单例模式的使用场景
日志模块:确保日志记录的唯一性,避免多个实例同时写入日志文件导致内容混乱。
配置管理:配置类通常需要在整个应用中共享,单例模式可以确保读取和修改配置的唯一性。
数据库连接池:在数据库操作中,可以使用单例模式来管理连接池,确保资源的合理使用。
线程池:统一管理线程资源,避免创建多个线程池实例。
日志模块的等级划分
日志通常会划分不同的级别,以便于记录不同重要性的信息。常见的日志等级包括:
DEBUG:用于开发调试,记录详细信息。
INFO:用于记录应用正常运行时的相关信息。
WARNING:表示潜在的问题或警告。
ERROR:记录发生错误的事件。
CRITICAL:表示严重错误,可能导致程序崩溃。
116、数据库连接池:登录注册怎么实现的 有没有实现拦截 用户的登录状态怎么保存的
数据库连接池是用于管理数据库连接的工具,可以提高数据库操作的效率。下面是关于登录注册实现、拦截器、用户登录状态保存的详细说明。
登录注册的实现
数据库连接池:
使用连接池来管理数据库连接,避免频繁建立和关闭连接。
在应用启动时初始化连接池,提供获取和释放连接的方法。
用户注册:
提供一个注册接口,接收用户输入的注册信息(如用户名、密码等)。
在注册时,对密码进行加密(如使用 bcrypt、SHA-256 等)。
检查用户名是否已存在,若不存在则将用户信息插入数据库。
用户登录:
提供一个登录接口,接收用户输入的用户名和密码。
从数据库中查询用户名对应的记录,比较输入密码和存储的密码(注意使用相同的加密算法)。
登录成功后,生成用户会话(session)或令牌(token)。
实现拦截器
拦截器用于处理请求的预处理和后处理,可以用于验证用户的登录状态。通常可以通过中间件的方式实现:
身份验证:
在请求到达具体的处理逻辑之前,检查请求中的认证信息(如 token 或 session ID)。
如果认证信息无效,返回未授权的错误响应。
用户登录状态的保存
用户的登录状态可以通过以下方式保存:
Session:
服务器生成一个会话 ID,并将其存储在服务器端。
将会话 ID 发送给客户端(如以 cookie 的形式)。
每次请求时,客户端会带上会话 ID,服务器根据 ID 查询用户状态。
Token:
生成一个包含用户信息的 JWT(JSON Web Token),并将其返回给客户端。
客户端在后续请求中携带这个 token,服务器解码验证 token 的有效性。
这种方式无须在服务器端存储状态,适合分布式系统。
117、C11新特性 智能指针 右值引用
118、场景题:场景设计 大量数据,内存加载不了所有,找最大的10个数, 多种方案
在处理大量数据时,如果内存无法加载所有数据,可以采用多种方案来找出最大的10个数。以下是几种常见的方案:
1. 外部排序(External Sorting)
将数据分成多个部分,在内存中对每个部分进行排序,然后将排序后的部分合并。
-
步骤:
- 将数据分割成多个可管理的块(例如,大小适合内存)。
- 对每个块在内存中进行排序(使用快速排序、归并排序等)。
- 将排序后的块写回磁盘。
- 使用归并算法合并所有排序的块,同时跟踪最大的10个数。
-
优点:适合处理非常大的数据集,保证结果准确。
-
缺点:性能较慢,受限于磁盘 I/O。
2. 最小堆(Min-Heap)
利用最小堆来维护当前最大的10个数。
-
步骤:
- 初始化一个大小为10的最小堆。
- 逐步读取数据:
- 如果堆中的元素少于10个,将新元素加入堆。
- 如果新元素大于堆顶元素(最小的元素),则替换堆顶元素,并进行堆调整。
- 读取完所有数据后,堆中的元素即为最大的10个数。
-
优点:时间复杂度为O(n log k),k为10,适合大数据流。
-
缺点:需要维护堆的状态。
3. 分布式计算(如 Hadoop)
使用分布式计算框架处理数据。
-
步骤:
- 使用 MapReduce,将数据分散到多个节点。
- 在每个节点上并行计算出各自的最大10个数。
- 将各节点的结果汇总,找出最终的最大10个数。
-
优点:适合非常大的数据集,利用多节点并行处理。
-
缺点:搭建和维护分布式系统的复杂性。
4. 采样(Sampling)
对数据进行采样,寻找最大的10个数。
-
步骤:
- 从数据集中随机选择一部分数据(如10%)。
- 在采样数据中找到最大的10个数。
- 根据业务需求,可以多次进行采样,并合并结果以提高准确性。
-
优点:处理速度快,内存占用小。
-
缺点:可能导致结果不准确,尤其是在数据分布不均匀时。
5. 数据库操作
如果数据存储在数据库中,可以使用 SQL 查询来获取最大的10个数。
-
步骤:
- 使用 SQL 查询(如
SELECT * FROM table ORDER BY value DESC LIMIT 10
)直接获取最大10个数。
- 使用 SQL 查询(如
-
优点:简洁,利用数据库优化的查询性能。
-
缺点:受限于数据库的处理能力,可能存在性能瓶颈。
关键点总结
选择哪种方案取决于具体的应用场景、数据量和可用资源。外部排序适合非常大的数据集,最小堆方法简单高效,分布式计算适合大规模并行处理,而采样则适用于快速处理和初步分析。
119、场景设计 两个大文件,找出重复的行,合并到一起
要找出两个大文件中重复的行,并将它们合并在一起,可以采用多种方案。以下是一些常见的方法:
1. 使用哈希表
通过哈希表来存储和查找重复的行。
步骤:
-
读取第一个文件:
- 创建一个哈希表(或集合)来存储第一文件的每一行。
- 遍历第一个文件,将每一行添加到哈希表中。
-
读取第二个文件:
- 遍历第二个文件,对于每一行,检查它是否在哈希表中存在。
- 如果存在,则将该行添加到结果列表中。
-
输出结果:
- 将重复的行写入输出文件或其他目标位置。
优点:
- 时间复杂度为O(n + m),n和m分别为两个文件的行数。
- 适合较大的文件,内存使用合理。
缺点:
- 内存消耗较大,尤其是在文件非常大的情况下。
2. 排序和比较
先对两个文件进行排序,然后进行比较。
步骤:
-
读取并排序:
- 将两个文件的内容读取到内存中(如果不能全读,可以分块读取)。
- 对每个文件的内容进行排序(使用快速排序、归并排序等)。
-
合并过程:
- 使用两个指针分别指向两个文件的开始,比较当前行:
- 如果相等,将行加入结果,并移动两个指针。
- 如果不相等,移动较小的那个指针。
- 使用两个指针分别指向两个文件的开始,比较当前行:
-
输出结果:
- 将找到的重复行写入输出文件。
优点:
- 适合内存较小,但能够对文件进行排序的情况。
- 不需要额外的哈希表,节省内存。
缺点:
- 排序操作的时间复杂度为O(n log n + m log m),可能会比较慢。
3. 使用数据库
如果数据量非常大,可以考虑将文件导入数据库进行操作。
步骤:
-
导入数据:
- 将两个文件的内容分别导入到数据库表中。
-
SQL 查询:
- 使用 SQL 查询找出重复的行,例如:
SELECT column_name FROM table1 INTERSECT SELECT column_name FROM table2;
- 使用 SQL 查询找出重复的行,例如:
-
导出结果:
- 将查询结果导出到新的文件。
优点:
- 数据库可以高效处理大量数据,利用索引加快查询。
- 可扩展性强,适合更复杂的查询和操作。
缺点:
- 需要搭建和维护数据库环境。
4. 使用外部工具
使用专门的文本处理工具(如 awk
, sort
, uniq
)来查找重复行。
步骤:
-
合并文件:
- 使用
cat
将两个文件合并。 - 例如:
cat file1 file2 > combined.txt
- 使用
-
排序和去重:
- 使用
sort
和uniq
进行排序和去重。 - 例如:
sort combined.txt | uniq -d > duplicates.txt
- 使用
优点:
- 简单易用,适合处理文本文件。
- 利用命令行工具处理大文件时速度快。
缺点:
- 不适合需要复杂逻辑处理的情况。
关键点总结
选择合适的方案取决于数据规模、可用资源和具体需求。哈希表适合较大文件且内存足够的情况,排序适合较小文件,数据库处理则适合非常大的数据集,而外部工具提供了一种简单快速的解决方案。