记录2025的面着玩
1.8 虫下
1.java里面如何判断比如一个hashmap它的键是一样的,比如键是一个对象的时候
是否相同依赖于该对象的equals() 方法和hashCode() 方法的实现
2.ssl是在tcp之前还是之后?哪里用到对称加密和非对称加密,哪个更安全?如何判断公钥是安全的
三次握手会在 SSL/TLS 握手之前完成,SSL/TLS 加密处理后,生成的加密数据通过 TCP/IP 传输。
3.synchronized底层是通过什么实现的?synchronized可以作用于哪些地方
它的底层实现依赖于 JVM(Java 虚拟机)和对象监视器(Monitor)。具体实现如下:
(1) 基于 Monitor 对象
synchronized实现依赖于 JVM 中的对象监视器(Monitor)。在字节码层面,synchronized 被编译成两条主要的字节码指令:
monitorenter:线程进入同步块时调用,尝试获取监视器锁。
monitorexit:线程退出同步块时调用,释放监视器锁。
(2) 内置锁
每个对象在 Java 中都关联一个对象头,其中包括一个锁标志位和指向 Monitor 的引用。它支持以下几种锁状态:
无锁(Unlocked):默认状态,没有线程持有锁。
偏向锁(Biased Locking):
为了减少锁的开销,如果一个线程独占访问一个同步块,JVM 会将锁偏向这个线程。
轻量级锁(Lightweight Locking):
适用于多个线程竞争,但没有真正发生阻塞的情况。
通过 CAS操作来尝试获取锁。
重量级锁(Heavyweight Locking):
如果多个线程同时竞争,发生阻塞时,锁会升级为重量级锁。
线程会挂起并进入操作系统的阻塞队列。
(3) 操作系统级别的实现
在重量级锁状态下,底层通过 操作系统的互斥量(Mutex)或线程调度机制 来实现线程的同步和阻塞。
4.java hashmap底层是红黑树还是散列表?扩容的时候是需要重新计算所有键值吗
底层数据结构:HashMap 的核心是基于 散列表,链表和红黑树用于优化哈希冲突。链表在链过长时会转换为红黑树。
扩容行为:扩容时会 重新计算键值对的位置,这是因为哈希的目标桶数量发生了变化。经过 Java 8 的优化,位置重新计算时不需要完全重新哈希,而是基于原有的哈希值对索引简单调整。
5.反转字符串中的单词
tx图形学课题
1.uras原理
2.asan是如何检测内存泄露的
3.urp和hdrp 区别
1.14百度
不同数据库怎么迁移,怎么保证数据库高可用?
大模型应用怎么搭建
2.14 二面挂
神人部门,前面人鸽了又捞
2.27 aliyun
1.什么时候用map什么时候用unordered_map?map的复杂度n指什么?
有序数据、范围查询 → 用 std::map
大数据量、频繁查找 → 用 std::unordered_map
n 指的是 map 中存储的元素数量
2.手写B树B+树
struct BTreeNode { int leaf; // 是否是叶子节点 int n; // 当前关键字数量 int keys[ORDER - 1]; // 关键字数组 struct BTreeNode* child[ORDER]; // 子节点数组 };
3.索引下推和谓词下推
索引下推针对单表模糊查询
4.分片有哪些策略范围分片,哈希分片,列表分片,复合分片
5.多线程轮流打印数字
https://blog.csdn.net/qq_51274317/article/details/132621731
https://blog.csdn.net/chuxinchangcun/article/details/130378838
6.多个线程获取某线程的shared_ptr的引用,计数会增加吗?peterson多线程算法是啥
多个线程获取某个线程的 shared_ptr 引用时,引用计数会增加。这是因为 shared_ptr 的引用计数机制是线程安全的,旨在支持多线程环境下的共享所有权管理。
7.力扣,最大数
3.6 wx客户端
1.inline的优点和缺点
2.c++里malloc再delete,new再free分别会有什么问题
3.如何让进程一个函数只执行一次
static,锁
4.有问题吗
int i=1; class A{ A(){ i++; } ~A() { i--; } int main() { mutex.lock(); class A; mutex.unlock(); }
5.napi
6.两个升序单向链表合并降序返回
这里有两种方法可以实现将两个升序的单向链表合并成降序的单向链表:
- 直接合并后反转(先合并成升序,然后反转链表)
- 头插法构造降序链表(遍历两个链表,较大的节点优先插入新链表头部)
下面是头插法的实现,避免了额外的反转操作,时间复杂度为 (O(n + m)):
代码实现:
#include <iostream> struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(nullptr) {} }; // 合并两个升序单链表并返回降序链表 ListNode* mergeAndReverse(ListNode* l1, ListNode* l2) { ListNode* newHead = nullptr; // 结果链表的头节点 while (l1 && l2) { ListNode* temp; if (l1->val < l2->val) { temp = l1; l1 = l1->next; } else { temp = l2; l2 = l2->next; } temp->next = newHead; // 头插法 newHead = temp; } // 处理剩余的节点 while (l1) { ListNode* temp = l1; l1 = l1->next; temp->next = newHead; newHead = temp; } while (l2) { ListNode* temp = l2; l2 = l2->next; temp->next = newHead; newHead = temp; } return newHead; }
复杂度分析:
- 时间复杂度:(O(n + m)),遍历两个链表合并。
- 空间复杂度:(O(1)),原地修改链表,无额外空间开销。
这种方法比合并后反转更高效,因为避免了额外的链表反转操作。