设为首页 收藏本站
查看: 102|回复: 0

[经验分享] 【大厂C++面试突击手册】Day11:动态规划/SQL优化/二叉树层...

[复制链接]
累计签到:50 天
连续签到:1 天
发表于 2025-3-18 15:25:02 | 显示全部楼层 |阅读模式
美团到家后端
c++的多态是怎么实现的
C++中的多态主要通过虚函数实现。当一个类中的函数被声明为虚函数时,它可以在该类的派生类中被重写,实现运行时多态。在调用时,会根据对象的实际类型来确定调用哪个版本的函数,这个决定是在运行时做出的。这种机制是通过虚函数表来实现的,每个有虚函数的类都有一个对应的虚函数表,表中存储了指向虚函数的指针。通过对象指针或引用调用虚函数时,会根据这个表来动态确定调用哪个函数。

什么是虚函数
虚函数是在基类中使用关键字 virtual 声明的函数,它允许在派生类中被重写,以实现多态性。这意味着当你通过基类指针或引用调用一个虚函数时,实际调用的是对象的动态类型(派生类类型)中对应的函数实现,而非指针或引用的静态类型(基类类型)中的实现。这个机制允许在运行时根据对象的实际类型调用正确的函数版本,从而实现运行时的多态。

什么是进程?什么是线程?两者的区别?
进程是操作系统分配资源和调度的基本单位,它包含了运行程序所需的代码、数据以及其他系统资源。线程是进程中的执行流,它是CPU调度和执行的最小单位。
两者的主要区别在于:
  • 进程拥有独立的地址空间,而同一进程下的线程共享地址空间。
  • 进程间切换开销较大,线程间切换开销较小。
  • 进程间通信(IPC)方式多样但相对复杂,线程间可以直接通过读写共享内存来通信,更简单高效。


什么是死锁?怎么避免死锁?
死锁是指多个进程或线程在运行过程中,因争夺资源而造成的一种僵持状态,每个进程都在等待其他进程释放资源,但没有任何进程先行释放资源,导致所有进程都无法向前推进。
避免死锁的策略包括:
  • 资源分配顺序:确保所有进程以相同的顺序请求资源。
  • 资源一次性分配:要求进程启动时一次性申请所需的所有资源。
  • 资源使用限制:限制同一时刻请求资源的进程数量,或通过预先分析确定资源的最大需求量。
  • 使用锁超时:进程尝试锁定资源时,加入超时机制,超时未锁定则释放已占有的资源并重试。


操作系统的段和页?
  • :段是一种逻辑地址空间的划分方式,以便将程序分成多个相对独立的部分,例如代码段、数据段等。每个段在逻辑内存中是连续的,但在物理内存中可能不连续。段的大小不固定,由程序所需要的内存量决定。
  • :页是一种物理地址空间的划分方式,将物理内存和逻辑内存都分割成固定大小的块。物理内存的页被称为页框,逻辑内存的页被称为页。通过页表,操作系统可以将逻辑内存的页映射到物理内存的页框,实现内存的虚拟化。

段和页的主要区别在于,段是以逻辑的方式进行内存划分

MySQL中什么是索引?
索引是一种数据结构,用于提高数据检索效率,类似于书的目录,可以快速定位到数据的位置。

建立索引的原则是什么?
  • 选择性高的字段:字段的唯一值越多,索引的效果越好。高选择性意味着查询可以从大量数据中快速过滤出少量结果。
  • 常用作查询条件的字段:经常出现在WHERE子句中的字段是建立索引的好候选。
  • 排序、分组字段:经常用于ORDER BY、GROUP BY子句的字段,建立索引可以提高排序和分组的效率。
  • 连接字段:在多表JOIN操作中作为连接条件的字段,索引可以显著加快连接查询的速度。
  • 避免过宽字段:过宽的字段意味着索引空间更大,维护成本更高,应优先考虑短索引。
  • 避免频繁修改的字段:字段数据频繁变动会导致索引频繁重建,影响性能。
  • 考虑创建组合索引:多个字段经常一起作为查询条件时,考虑创建组合索引,但需注意字段顺序。
  • 权衡索引成本:虽然索引可以提高查询效率,但也会增加写操作(INSERT、UPDATE、DELETE)的成本和占用额外的存储空间。合理构建和维护索引以平衡这种权衡。


索引是怎么实现的?
索引通常通过数据结构如B树(尤其是B+树)和哈希表来实现。

讲讲为什么使用B+树?
  • B+树是一种平衡多叉树,保证了所有叶子节点都在同一层,查询路径长度一致,确保了查询性能的稳定性。
  • B+树的叶子节点按键值有序链接,便于实现范围查询。
  • 相对于二叉树等其他平衡树,B+树有更高的分支因子,减少了磁盘I/O操作次数。
  • B+树的设计充分利用了磁盘预读原理,减少了页的分裂概率,提高了空间和时间局部性。
  • B+树只在叶子节点保存数据的指针或实际数据,在内部节点仅存储键值,提高了节点存储效率。


什么是聚簇索引和非聚簇索引?
聚簇索引:直接按照一定顺序存储数据记录的物理形式。一个表只能有一个聚簇索引,因为数据只能按照一种顺序排列。
非聚簇索引:不改变数据实际存储顺序,而是创建一个单独的索引结构来指向数据记录的位置。一个表可以有多个非聚簇索引。

B+树是什么?
B+树是一种自平衡的树数据结构,它维护排序数据以允许搜索、顺序访问、插入和删除操作在对数时间内进行。B+树特别适合用于读写较大数据块的存储系统。主要特点包括:
  • 所有的叶子节点都位于同一层,并且包含数据及其对应的键值。
  • 所有的非叶子节点都可以作为索引部分,只包含其子节点中的最大(或最小)键值。
  • 叶子节点通过指针相连,这为遍历提供了高效的顺序访问路径。


什么叫慢查询
慢查询是指在数据库中执行时间过长,响应速度慢的查询操作。具体的时间阈值可以根据系统的具体需求进行定义,例如,如果一条查询语句执行时间超过了设定的阈值(比如1秒),那么就可以将这条查询语句标记为慢查询。慢查询的原因可能包括数据量过大、数据库设计不合理、索引问题等。了解和优化慢查询对于提升数据库和整个系统的性能有重要作用。

什么是事务?
事务是数据库管理系统执行过程中的一个操作序列单元,它是一个不可分割的工作单位。事务具备以下四个标准属性,通常被称为ACID属性:
  • 原子性:事务内的所有操作要么全部成功,要么全部失败回滚。
  • 一致性:事务必须保证数据库从一个一致性状态转移到另一个一致性状态。
  • 隔离性:并发执行的事务之间要相互隔离,防止互相影响。
  • 持久性:一旦事务提交,其结果就永久地保存在数据库中。


讲讲什么是动态规划以及他的主要思想。
动态规划(DP)是一种算法设计技术,主要用来解决特定类型的优化问题。它能够将复杂问题分解为更简单的子问题,并且存储这些子问题的解,避免了重复计算,极大地提高了计算效率。
动态规划的主要思想基于两个核心要素:
  • 最优子结构:关键在于识别出给定问题的解可以通过其子问题的最优解有效构造出来。换言之,原问题的最优解包含了其子问题的最优解,这样就可以逐步构建出整个问题的最优解。
  • 重叠子问题:动态规划适用于子问题重叠的情况,即不同的问题部分包含了相同的子问题。为了提高效率,动态规划会保存这些子问题的解,这样当再次遇到相同子问题时,就可以直接使用已经计算过的结果。

以下是实施动态规划的基本步骤:
  • 定义状态:分析问题性质,定义合适的状态表示解的各个方面。
  • 状态转移方程:根据问题的规则,确定状态之间的转移方程,这是动态规划的核心部分。
  • 初始化条件:确定初始状态值,以正确开始状态转移的过程。
  • 计算顺序:确定计算状态值的顺序,通常是一种基于之前计算结果的迭代过程。
  • 构造最终解:利用计算出的状态值构造问题的最终解。


动态规划一般可以解决什么样子的实际问题?
  • 计算最优解:比如路径寻找(最短路径问题)、资源分配(背包问题)。
  • 决策制定:如工程项目的调度、库存管理。
  • 序列:比如字符串比对(编辑距离问题)、最长递增子序列。
  • 组合:比如子集划分(划分问题)、不同路径的计算问题。

最长公共子序列,只用说出思路,然后问最长公共子序列的现实价值
在C++中实现最长公共子序列(LCS)通常使用动态规划。创建一个二维数组dp,其中dp[j]
代表第一个序列前i个元素和第二个序列前j个元素的LCS长度。遍历两个序列,当遇到相同元素时,dp[j] = dp[i-1][j-1] + 1;否则,dp[j] = max(dp[i-1][j], dp[j-1])。最终dp[m][n](m和n分别是两个序列的长度)即为所求的LCS长度。
最长公共子序列在实际中的应用包括:
在版本控制和代码审查中用来识别文件之间的差异。
在数据比对和数据同步中确定相似度高的记录。
在撤销功能的实现中记录和比较文本的历史更改。

一道sql题目:给了一个表,查询每个用户最近一天登录的日子(提示了使用Max函数)
思路:
  • 定义数据结构:使用std::unordered_map<std::string, std::string>存储用户ID和其最后登录日期的映射。这里假定用户ID和日期都是字符串格式。
  • 读入数据:遍历给定的表中的每一行记录,对于每条记录,提取用户ID和日期。
  • 更新记录:对于每条记录,首先检查是否已经在std::unordered_map中记录了该用户。如果记录了,比较现有日期和表中的日期,只保留较新的那一个;如果还未记录,直接插入该用户ID和日期的键值对。

[C++] 纯文本查看 复制代码
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm> // 用于std::max

// 假设LoginRecord是从某处得到的数据结构,存储了用户ID和登录日期
struct LoginRecord {
    std::string userId;
    std::string loginDate;
};

std::unordered_map<std::string, std::string> findLatestLogin(const std::vector<LoginRecord>& records) {
    std::unordered_map<std::string, std::string> userLatestLogin;
    for (const auto& record: records) {
        auto& recordedDate = userLatestLogin[record.userId];
        recordedDate = std::max(recordedDate, record.loginDate);
    }
    return userLatestLogin;
}

int main() {
    // 示例:假设records是从某处读入的登录记录
    std::vector<LoginRecord> records = {
        {"user1", "2024-04-01"},
        {"user2", "2024-04-02"},
        {"user1", "2024-04-03"},
        // 添加更多记录...
    };

    auto latestLogins = findLatestLogin(records);

    // 打印结果
    for (const auto& pair : latestLogins) {
        std::cout << "User: " << pair.first << ", Latest Login: " << pair.second << std::endl;
    }

    return 0;
}

lc二叉树层序遍历
思路:
  • 如果根节点为空,则返回空列表。
  • 创建一个队列,将根节点加入队列。

  • 当队列不为空的时候,进行以下操作:


    • 计算当前队列的长度,这将是这一层的节点数。
    • 为当前层创建一个空列表。
    • 根据这一层的节点数,依次从队列中取出节点,并将其子节点(非空的左和右子节点)加入队列。
    • 将取出节点的值加入当前层的列表中。

  • 将每一层的节点值列表加入到最终结果中。
  • 当队列为空时,返回最终结果。


[C++] 纯文本查看 复制代码
#include <iostream>
#include <queue>
#include <vector>

// Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

std::vector<std::vector<int>> levelOrder(TreeNode* root) {
    std::vector<std::vector<int>> levels;
    if (!root) return levels;
    
    std::queue<TreeNode*> queue;
    queue.push(root);
    
    while (!queue.empty()) {
        int levelLength = queue.size();
        std::vector<int> currentLevel;
        
        for (int i = 0; i < levelLength; ++i) {
            TreeNode* node = queue.front();
            queue.pop();
            
            // Add the current node's value to the current level
            currentLevel.push_back(node->val);
            
            // Insert the children of current node in the queue
            if (node->left) queue.push(node->left);
            if (node->right) queue.push(node->right);
        }
        
        // Add the current level's values to the output list
        levels.push_back(currentLevel);
    }
    
    return levels;
}

int main() {
    // Example usage:
    // Construct binary tree here and call levelOrder with root node
    
    return 0;
}




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-1005699-1-1.html 上篇帖子: 【大厂C++面试突击手册】Day10:ECS架构×内存管理×C++底层高... 下篇帖子: 【大厂C++面试突击手册】Day12:类成员存储机制/虚函数表...
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表