快手C++二面-面经总结

1、new malloc的区别 至少说出4点以上,在申请内存的时候都做了哪些工作 申请内存的过程是否需要初始化?

new 是 C++ 中的关键字,用于动态分配内存,并调用构造函数初始化对象。
malloc 是 C 语言中的函数,用于动态分配内存。

new 在内存分配失败时会抛出 std::bad_alloc 异常。
malloc 在内存分配失败时会返回 NULL,需要检查返回值来判断是否分配成功。

new 分配的内存可以使用 delete 关键字来释放,同时会调用对象的析构函数。
malloc 分配的内存需要使用 free 函数来释放,不会调用对象的析构函数。

new 可以根据对象类型自动计算所需的内存大小。
malloc 需要手动计算所需的内存大小,并传递给函数。

申请内存:
1.new malloc
2.内存管理器找到内存块
3.分配给 标记为已分配 返回指针
4.new还会初始化
5.程序就可以通过指针来访问了

new会初始化啊 调用构造函数
malloc不会初始化 未定义

2、delete 和 delete [] 区别 如何对调使用会发生什么事情?

释放方式:
delete 用于释放使用 new 分配的单个对象的内存。
delete[] 用于释放使用 new[] 分配的数组对象的内存。
释放过程:
delete 会调用被释放对象的析构函数,然后释放对象占用的内存。
delete[] 会先调用数组中每个元素的析构函数(如果有),然后释放整个数组占用的内存。

对调用的影响:
如果使用 delete[] 释放使用 new 分配的单个对象的内存,行为是未定义的,可能会导致程序崩溃或其他意外情况。
如果使用 delete 释放使用 new[] 分配的数组对象的内存,行为也是未定义的,可能会导致程序崩溃或其他意外情况。

3、动态多态的虚函数内部原理, 子类继承父类在动态多态中会调用谁的虚方法?

虚函数通过虚函数表实现

每个类有一个虚函数表,其中存储着该类的虚函数的地址。

当一个类中包含虚函数时,编译器会在该类的对象中添加一个指向虚函数表的指针,称为虚函数指针(vptr)。

在调用虚函数时,实际上是通过对象的虚函数指针找到对应的虚函数表,然后根据函数在虚函数表中的偏移量找到具体的函数地址进行调用。

子类会覆盖父类的虚函数

4、多线程在C++中保证线程安全的方式有哪些?

互斥锁(Mutex)
原子操作(Atomic Operation)
读写锁(Read-Write Lock)
使用锁的 RAII 封装

5、多线程只读操作的时候需要加锁吗?

不修改通常不加锁

但是,在某些情况下,即使是只读操作也需要考虑加锁的情况:
确保数据一致性
避免数据竞争
提高性能

6、多个线程读 一个线程写需要加锁吗?

有写的 得加锁
不然脏读 数据竞争 不可重复读取 幻读
为了避免这些问题,可以使用读写锁(Read-Write Lock)来实现。读写锁允许多个线程同时读取数据,但只允许一个线程写入数据。当有写入操作时,读取操作会被阻塞,确保写入操作的原子性和一致性。

另一种方法是使用互斥锁(Mutex)来保护数据的读写操作。在读取数据时获取共享(读)锁,在写入数据时获取独占(写)锁,确保在同一时刻只有一个线程可以写入数据,从而避免数据竞争和不一致性。

7、读写锁如何实现口述?

好多可以读,但是允许一个写
读写锁分为读锁和写锁两种类型

基于互斥锁和条件变量来完成。
// 读写锁结构体
struct ReadWriteLock {
    pthread_mutex_t mutex;         // 用于保护读写锁的互斥锁
    pthread_cond_t readCondition;  // 读取条件变量
    pthread_cond_t writeCondition; // 写入条件变量
    int readers;                   // 当前正在读取的线程数
    int writers;                   // 当前正在写入的线程数
    int pendingWriters;            // 等待写入的线程数
};

// 初始化读写锁
void init_rwlock(ReadWriteLock* rwlock) {
    pthread_mutex_init(&rwlock->mutex, NULL);
    pthread_cond_init(&rwlock->readCondition, NULL);
    pthread_cond_init(&rwlock->writeCondition, NULL);
    rwlock->readers = 0;
    rwlock->writers = 0;
    rwlock->pendingWriters = 0;
}

// 加读锁
void read_lock(ReadWriteLock* rwlock) {
    pthread_mutex_lock(&rwlock->mutex);
    while (rwlock->writers > 0 || rwlock->pendingWriters > 0) {
        pthread_cond_wait(&rwlock->readCondition, &rwlock->mutex);
    }
    rwlock->readers++;
    pthread_mutex_unlock(&rwlock->mutex);
}

// 释放读锁
void read_unlock(ReadWriteLock* rwlock) {
    pthread_mutex_lock(&rwlock->mutex);
    rwlock->readers--;
    if (rwlock->readers == 0 && rwlock->pendingWriters > 0) {
        pthread_cond_signal(&rwlock->writeCondition);
    }
    pthread_mutex_unlock(&rwlock->mutex);
}

// 加写锁
void write_lock(ReadWriteLock* rwlock) {
    pthread_mutex_lock(&rwlock->mutex);
    rwlock->pendingWriters++;
    while (rwlock->readers > 0 || rwlock->writers > 0) {
        pthread_cond_wait(&rwlock->writeCondition, &rwlock->mutex);
    }
    rwlock->pendingWriters--;
    rwlock->writers++;
    pthread_mutex_unlock(&rwlock->mutex);
}

// 释放写锁
void write_unlock(ReadWriteLock* rwlock) {
    pthread_mutex_lock(&rwlock->mutex);
    rwlock->writers--;
    if (rwlock->pendingWriters > 0) {
        pthread_cond_signal(&rwlock->writeCondition)
    } else {
        pthread_cond_broadcast(&rwlock->readCondition);
    }
    pthread_mutex_unlock(&rwlock->mutex);
}

8、8大排序方法的时间复杂度?口述归并排序和快排

插冒龟统计鸡
选帽插n^2
块归队 nlogn
统计鸡 ++x

归并排序:切切切 切到单个元素 组装合并 合成大西瓜
快排:分治 基准元素魔力牌 一堆比这个小 一堆比这个大 然后两堆各自找魔力牌

9、map 和multimap unordered_map区别 为什么要有 unordered_map 使用场景是什么,这三者访问元素的时间复杂度 底层实现?

区别

底层数据结构不同:
    std::map 和 std::multimap 底层通常基于红黑树实现,保证了元素的有序性。
    std::unordered_map 底层基于哈希表实现,没有元素的顺序保证,但能够提供更快的查找速度(平均 O(1))。
元素的唯一性:
    std::map 中的键值对是唯一的,如果插入已存在的键,则会替换原有的值。
    std::multimap 中的键可以重复,允许多个键对应不同的值。
    std::unordered_map 也要求键的唯一性,但哈希冲突时会使用链表或其他方式解决,因此允许在常数时间内插入重复的键。
查找效率:
    std::map 和 std::multimap 的查找效率为 O(log n),因为基于红黑树实现。
    std::unordered_map 的查找效率为平均 O(1),最坏情况下为 O(n)(发生哈希冲突时)。
内存占用:
    std::map 和 std::multimap 需要额外的内存来存储红黑树结构,因此可能占用更多的内存。
    std::unordered_map 通常占用较少的内存,但在哈希冲突较多时可能会占用较多内存来维护链表等结构。
遍历顺序:
    std::map 和 std::multimap 遍历时按照键的顺序(升序)进行。
    std::unordered_map 没有固定的遍历顺序,取决于哈希表的实现和当前的负载因子。

使用场景

快速查找:unordered_map 的底层实现是哈希表,因此查找元素的平均时间复杂度为 O(1),对于大量数据的快速查找非常高效。
存储键值对:unordered_map 可以存储键值对,并且保证键的唯一性。这在需要建立键与值之间的映射关系时非常有用,比如字典、符号表等。
去重:由于 unordered_map 的键是唯一的,可以用来去重,即只保留不重复的元素。
缓存:unordered_map 可以用作缓存,存储计算结果以避免重复计算。
替代数组:在一些情况下,unordered_map 可以替代数组来存储数据,尤其是当键的范围比较大或者不连续时。

访问元素的时间复杂度和底层实现:

std::map 和 std::multimap 的查找、插入和删除操作的平均时间复杂度为 O(log n),因为它们是基于红黑树实现的,红黑树保证了元素的有序性。
std::unordered_map 的查找、插入和删除操作的平均时间复杂度为 O(1),最坏情况下为 O(n),这是因为 unordered_map 使用哈希表来实现,哈希表可以在平均情况下提供常数时间的性能,但在发生哈希冲突时,会退化到链表或其他方式来解决冲突,导致最坏情况下的时间复杂度为 O(n)。

1、IPV4地址字符串转化为 32整型数字?

思路

将 IPv4 地址字符串按照点号 "." 分割成四个部分。
将每个部分转换为整数,并将它们按照从左到右的顺序拼接起来,得到一个 32 位的整数。

举个例子,如果有一个 IPv4 地址字符串 “192.168.1.1”,它可以按照上述步骤转换为整数:

分割成四个部分:192, 168, 1, 1。
转换为整数并拼接:192 << 24 | 168 << 16 | 1 << 8 | 1。
#include <iostream>
#include <sstream>
#include <vector>

// 将IPv4地址字符串转换为32位整数
uint32_t ipToInteger(const std::string& ip) {
    // 创建一个向量来存储IP地址的各个部分
    std::vector<int> parts;
    // 使用字符串流来处理IP地址字符串
    std::stringstream ss(ip);
    std::string part;
    // 使用点号分割字符串,并将各个部分转换为整数并存储到向量中
    while (std::getline(ss, part, '.')) {
        parts.push_back(std::stoi(part));
    }
    // 将各个部分合并为一个32位整数并返回
    return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
}

// 这里将stoi也实现一下
int stoi(const std::string& str) {
    int result = 0;
    for (char c : str) {
        if (c < '0' || c > '9') {
            // 非数字字符,返回错误值 0
            return 0;
        }
        // 将字符转换为数字,并累加到结果中
        result = result * 10 + (c - '0');
    }
    // 返回转换后的整数值
    return result;
}

int main() {
    std::string ip = "192.168.1.1";
    // 调用函数将IP地址字符串转换为整数
    uint32_t result = ipToInteger(ip);
    // 打印转换结果
    std::cout << "IP: " << ip << " => Integer: " << result << std::endl;
    return 0;
}

2、词频统计 保证次数相同基础上优先字母排序打印 ACM模式?

问题描述

写一个 bash 脚本以统计一个文本文件 words.txt 中每个单词出现的频率。

为了简单起见,你可以假设:

words.txt只包括小写字母和 ' ' 。
每个单词只由小写字母组成。
单词间由一个或多个空格字符分隔。

示例:

假设 words.txt 内容如下:

the day is sunny the the
the sunny is is

你的脚本应当输出(以词频降序排列):

the 4
is 3
sunny 2
day 1

说明:

不要担心词频相同的单词的排序问题,每个单词出现的频率都是唯一的。
你可以使用一行Unix pipes实现吗?

我在LC上看到这里实际上考察的是一个对Linux命令的使用,如果是别的方式大家可以去学一下。

我就使用Linux命令给大家解答一下

使用 tr 命令将空格替换为换行符,然后使用 sort 命令排序,再使用 uniq -c 统计每个单词的频率,最后再次排序

cat words.txt | tr -s ' ' '\n' | sort | uniq -c | sort -nr | awk '{print $2, $1}'

这个命令的具体执行流程如下:

cat words.txt:读取文件 words.txt 的内容。
tr -s ' ' '\n':将空格替换为换行符,即将每个单词分隔到一行。
sort:对单词进行排序。
uniq -c:统计每个单词出现的次数,并在前面加上频率。
sort -nr:按照频率降序排序。
awk '{print $2, $1}':使用 awk 输出单词和频率,其中 2是单词,1 是频率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MatsumotoChrikk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值