红黑树

红黑树

基本规则:

隐藏的空节点是黑色

满足Avl树

根节点和叶子节点都是黑色

不能有连续两个红色节点

从根节点到叶子节点的每个路径上黑色节点数量都一致

插入:

插入节点是根节点;直接变黑

插入节点的叔叔是红色;叔父爷爷变色,爷爷变插入节点继续调整

插入节点的叔叔是黑 ;(LL,RL,LR,RL)旋转,然后变色

红黑树 - 定义, 插入, 构建_哔哩哔哩_bilibili

金山C++研发实习生面试准备

金山C++研发实习生面试准备

1.自我介绍
2.拷打项目

3.拷打八股:

4.多态的实现

5.静态多态和动态多态

静态多态是指在编译时多态,动态多态是指运行时多态

静态多态有函数重载(函数名相同,参数不同,返回值相同或不同),函数模板

动态多态(动态绑定):即运行时的多态,在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。

6.动态多态怎么实现的

动态多态

通过每个类的虚函数和动态绑定

派生类实现动态绑定

  • 先拷贝基类的虚函数表
  • 如果派生类重写了基类的某个虚函数,就用派生类的虚函数替换虚表同位置的基类虚函数
  • 跟上派生类自己的虚函数

指向基类的指针或引用

动态多态需要通过指向基类的 指针或引用 操作派生类对象。如果直接操作派生对象,则是静态绑定,而非动态绑定。

7.介绍一下虚函数表和虚指针

虚函数表和虚指针是由编译器生成并使用的,它们是实现 动态绑定 和运行时多态的核心。

虚函数表是一种由编译器生成的 隐藏数据结构,它是一个函数指针数组,用来存储类中所有虚函数的地址。简单来说,虚函数表就是一个指针数组,每个元素指向一个虚函数的具体实现。

虚指针是一个 隐藏成员变量,存在于每个包含虚函数的类的对象中。虚指针用于指向当前对象所属类的虚函数表。

8.说一下构造函数和析构函数在父类和子类的执行顺序

  • 构造函数顺序
    父类构造函数成员对象的构造函数子类构造函数
  • 析构函数顺序
    子类析构函数成员对象的析构函数父类析构函数

9.析构函数的作用

析构函数用于释放对象占用的资源(如内存、文件句柄等),在对象生命周期结束时自动调用,确保资源不会泄漏。

10.为什么析构函数通常定义为虚函数;不调用析构函数会怎样

  • 原因
    若父类指针指向子类对象,且父类析构函数非虚,则通过父类指针删除对象时,只会调用父类析构函数,导致子类资源泄漏。
  • 后果
    不调用析构函数会导致资源泄漏(如内存、文件句柄未释放)。

11.结构体为何通常要进行内存对齐;一般是几字节对齐

为了提高访问效率,为了时内存命中访问效率更高

对齐原则

结构体的内存对齐通常遵循以下原则:

  1. 每个成员变量的起始地址必须是其自身大小的整数倍
  2. 结构体本身的大小必须是其最大对齐系数(对齐要求)的整数倍

12.new/delete和malloc/free的区别

特性 new/delete malloc/free
语言特性支持 C++ 专有 C 风格(C 和 C++ 均支持)
类型安全 否(需要手动类型转换)
初始化支持 自动调用构造函数和析构函数 不调用,需要手动处理
异常处理 内存分配失败抛出 std::bad_alloc 内存分配失败返回 NULL
灵活性 适合对象管理和复杂场景 适合简单的POD或跨语言
效率 稍有额外开销(构造/析构函数调用) 相对高效

什么时候选择哪一个?

使用 new/delete

  • 在C++程序中,需要创建具有构造函数和析构函数的复杂对象。
  • 希望利用类型安全和异常处理机制,提高代码的健壮性。
  • 更偏向于面向对象的编程风格。

使用 malloc/free

  • 在C语言程序中,或者需要与 C 的库/代码接口。
  • 需要跨语言兼容时(如 C 和 C++ 混合开发)。
  • 只操作简单的内存(非对象)时,比如使用裸内存块或数组。

总结

  1. newdelete 是C++中的语法糖,功能更强大,更贴合现代编程需求。
  2. mallocfree 是底层内存管理工具,适合裸内存分配。
  3. 在C++开发中,通常推荐优先使用 newdelete,除非有特殊需要才选择 mallocfree

13.什么情况用mmap()

mmap() 是一个 POSIX 标准的内存映射函数,通常用于将文件或设备映射到进程虚拟内存空间中,使得我们可以通过内存访问文件内容,大大提高了 I/O 的效率。它的使用场景主要集中在对文件的高效访问、大文件处理、共享内存以及特殊的内存管理需求等。以下是 mmap() 的典型使用情况、优缺点以及示例解释。

1.高效文件访问,减少内核态和用户态之间的拷贝开销

2.处理大文件,将文件内容按需映射到虚拟内存中

3.共享内存(进程间通信)

  • mmap() 支持使用 MAP_SHARED 标志创建共享内存,多个进程可以操作同一内存区域进行通信。
  • 使用共享内存比使用消息队列、管道等传统 IPC 机制有更高的效率。

4.文件修改或写入(内存作为缓冲区)适用于需要高效更新大文件的场景

5.实现内存映射I/O

6.异步I/O和并行处理

使用 mmap() 的场景主要是权衡性能与开销,以下是具体选择标准:

  1. 适合用 mmap() 的场景
    • 需要频繁随机访问大文件。
    • 文件的内容按需加载,不需要一次性加载到内存。
    • 多进程需要共享文件或内存。
    • 操作设备文件或硬件寄存器。
    • 文件访问频繁,且希望减少用户态与内核态之间频繁的数据拷贝。
  2. 不太适合用 mmap() 的场景
    • 操作小文件,或者一次性读取整个文件,不需要复杂的随机访问。
    • 文件处理逻辑非常简单,而 mmap() 的复用性和管理开销反而适得其反。
    • 跨平台兼容需求非常高的场景。

14.动态链接库用的brk()还是mmap()

特性 brk() mmap()
内存范围 紧跟数据段末尾 任意空闲的虚拟地址空间
按需加载 不支持 支持
分页分配 不支持 支持,以页为单位分配和管理
碎片问题 容易引发堆碎片 避免堆碎片,内存管理灵活
共享内存 不支持 支持共享内存(MAP_SHARED 标志)
动态链接库加载 不适用 是动态链接库的默认选择

因此,动态链接库的加载和运行使用 mmap() 是最佳选择,它为动态链接库的按需加载、多进程共享、独立管理以及性能优化提供了完美支持。同时,brk() 由于其限制,已不适用于动态库加载。

15.动态链接和静态链接的区别

特性 静态链接 动态链接
生成的可执行文件体积 较大,库代码直接嵌入 较小,仅包含对动态库的引用
运行时依赖 不依赖外部库,所有代码都在一个文件中 依赖外部库,运行时需要动态加载
运行时性能 加载速度快,无需加载外部库 加载速度稍慢,需要动态加载符号解析
内存使用 每个程序独立占用一份库的内存 多个程序共享同一个库,节省内存
更新方式 更新程序需要重新编译并链接静态库 可以通过更新库文件,无需重新编译程序
跨平台兼容性 静态库只与编译平台相关,适合封装依赖 动态库可能与运行时环境、平台设置相关
调试难度 相对简单,因为库已集成进可执行文件 调试更复杂,可能需要检查多个动态库
符号冲突问题 不会出现符号冲突,代码已固定 有可能发生符号冲突,需要小心处理
磁盘存储 每个可执行文件都包含完整的库代码 动态库存储独立,节省磁盘空间
维护与分发 更新程序和库需要重新分发完整的可执行文件 库文件可以单独更新,减少分发工作量

16.红黑树特性;有哪些应用

  • 特性
    1. 每个节点是红或黑。
    2. 根节点和叶子节点(NIL)是黑。
    3. 红节点的子节点必为黑。
    4. 从任一节点到其叶子节点的路径包含相同数量的黑节点。
  • 应用
    C++ STL map/set、Java TreeMap、Linux 内核调度器等。

17.死锁的必要条件有哪些

  1. 互斥:资源只能被一个进程占用。
  2. 持有并等待:进程持有资源并等待其他资源。
  3. 不可抢占:资源只能由持有者主动释放。
  4. 循环等待:存在进程资源的环形等待链。

18.线程安全实现方式

  1. 互斥锁(Mutex):保证临界区代码原子性。
  2. 原子操作(Atomic):通过硬件支持的原子指令(如 CAS)。
  3. 无锁编程:如使用无锁数据结构。
  4. 线程局部存储(TLS):避免共享数据。
  5. 读写锁(Read-Write Lock):区分读/写操作。
  6. 条件变量(Condition Variable):线程间同步。

docker遇到的问题

拉取镜像失败是网络问题,先看网络

docker命令报错error during connect: Get http://2F2F.2Fpipe2Fdocker_engine/v1.36/containers/json: open//.

在清理docker时遇到

1
docker system prune

原因是docker无法挂载到wsl上

因为刚刚执行压缩命令占用了vhdx文件,不能把docker文件关联上

关机重启就好

1
2
3
4
5
6
7
8
9
 docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- unused build cache

Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B

docker上传文件至容器

docker上传文件至容器

拿到容器ID

1
docker ps -a

将本地文件上传到容器的指定目录中

1
docker cp 本地文件路径 ID全称:容器路径
1
docker cp /home/stydent/EdgeGPT /usr/local/lib/python3.11/site-packages
1
sudo docker cp fbf56805d23a:/usr/local/lib/python3.11/site-packages ./site-packages

ngnix实现共享文件

ngnix实现共享文件

乱码解决

1
2
server和loaction层都加上
charset utf-8

配置 Nginx

1
sudo vi /etc/nginx/sites-enabled/default

验证配置

1
2
3
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

这样测试说明 nginx 配置是正确的。

重启启动 nginx 服务

1
sudo service nginx restart

权限问题

要求权限是755才能正常访问

1
chmod -R 755 xmal/

openvpn

openvpn

下载

1
wget -O openvpn.sh https://get.vpnsetup.net/ovpn

安装

1
sudo bash openvpn.sh --auto

启动

1
sudo bash openvpn.sh

system初始化问题

System has not been booted with systemd as init system (PID 1). Can’t operate.
Failed to connect to bus: Host is down

翻译过来就是:“系统尚未以systemd作为初始系统启动”。

问题分析:

1)当你尝试使用 systemd 命令来管理 Linux 系统上的服务的时候,之所以会报错,可能因为系统中根本就没有使用 systemd,而是使用的 SysV init (sysvinit)。

2)如果你是在 windows 中通过 WSL 使用的 Ubuntu 或者 Dibian 系统,默认情况下系统使用的是 SysV 而不是 systemd。

windows安装python2环境

windows安装python2环境

image-20240918205426825

安装在D盘后,不用管最后这个,之后配置环境变量

image-20240918205806490

image-20240918210154534

把这两个目录添加到环境变量,之后再修改文件夹中python.exe为python2.exe