网站首页 > 文章精选 正文
你猜他通过了吗
一面2020/12/20/14:00-14:50
1、自我介绍(2分钟)
2、你说你读过 redis 源码,那说一下常用的数据结构。
字符串、跳表、字典、压缩列表、链表
3、介绍一下跳表:
它在初始化的时候会分配一个头,它也是和链表一样链起来一个表嘛,它会给每个节点随机分配一个层,然后它从它的头部给每个节点进行每一层的连接,它的层数越高,它就会直接连到后面去,中间低的层就不连了,包括它的遍历也是一样,从高到低来找哪个数,这样它就可以跳着遍历了。
4、查找的时间复杂度是多少:log(n)
5、空间占用呢,相对于链表多了什么:主要是多出了那个层嘛,它每个节点的层数是不一样的,如果层都是1,也就退化成链表了。
6、vector push_back 扩容:2倍扩容,实际上可能更复杂一点
7、扩容以后,它的内存地址会变化嘛:会变化。以前的话,它是会把原来的元素重新复制过去,开辟一个新空间,复制过去,再把它之前的空间 free 掉。但是现在的话,是通过移动语义 move 来的
8、说一说 move 语义:相对于以前的复制来说,是把元素复制一遍,再把之前的销毁掉。现在的 move 相当于是把它直接搬走了。也就是,我再想一种说法,也就是延长了它的生命周期,然后把新的指针直到原先的那个位置去,然后把原先的指针赋为空,差不多这样。
9、move 底层的实现:emm,我看到了有 static_cast ,但是具体的,没有完全了解过
10、右值引用:把那种临时的变量,或者说马上就要消亡,那个叫将亡值,把它的生命周期给延长,变成左值。
11、什么时候被释放:我的理解是它相当于一个左值了(意思和左值一样)
12、怎么把 vector 里面存数据的所有内存空间都释放掉:先说的 resize ,后面改说是另一个函数,不记得名字了
13、说一下虚函数:子类对父类的函数进行一个重写。
14、举个例子详细说一下吧:举例说了动物(父类)吃东西,其他子类吃的动作不一样。这个时候对吃这个函数使用虚函数。
15、虚函数和函数重写有什么区别呢:(乱七八糟猜了一同)我这样猜的
16、再具体说说:但是我不知道这样猜的对不对
17、那换个问题吧,说说引用和指针:1. 内存占用;2. 初始化;3. 可修改指向
18、还有嘛:没了
19、引用可以销毁嘛:不知道了
20、智能指针了解过嘛,说说怎么实现的:增加了一个引用计数
21、具体说说:不同的智能指针指向同一个对象,有一个指针指向,它的引用计数就+1,最后变为0的时候,它就自动的销毁了。
22、多线程中可以使用智能指针嘛:不同线程里面的智能指针都是指向同一个对象嘛?是的:我觉得应该是适用的吧
23、你不知道是吧,确实是适用的,你是怎么想的呢:它不同的线程是共享资源的吧,所以它们是可以指到同一个对象上的吧
24、会有读写冲突嘛:会有的
25、怎么解决:应该通过加锁来解决
手撕
1. 中序遍历(迭代)
给定二叉树的,构造一个中序遍历的迭代器,对应的功能是返回二叉树上的下一个元素。
class Iterator {
public:
TreeNode* next();
}
1.1 思路讨论(6分钟左右)
我:在实现的过程中,需要把中序遍历给记录下来嘛
面:你具体说说
我:比如说,我在初始化的时候直接把它的中序遍历存在 vector 中,直接用 next 这样来访问
面:说说有什么优点和缺点
我:优点就是在调用 next 的时候复杂度是 O(1),缺点就是在初始化的时候要把它完全遍历一遍,空间复杂度是O(n)
面:你可以试试其他方法
我:是不用额外的空间嘛
面:也不是不用,但是比 O(n) 小点,比如 O(logn) 就行了。
我:哦哦,那就是用一个栈,记录它balabala....
面:那你开始写吧
面:你先定义一个TreeNode
我:需要定义Tree嘛
面:这个不用
我:这个需要运行嘛
面:先不需要
1.2中途代码编写(八分钟左右)
面:根节点从构造函数传入
#include <iostream>
#include <stack>
using namespace std;
struct TreeNode {
int val;
TreeNode* left, *right;
};
class Iterator {
public:
stack<TreeNode*> s;
Iterator(TreeNode* root) {
// s.push(root);
TreeNode* t = root;
while (t) {
s.push(t);
t = root->left;
}
}
TreeNode* next() {
if (s.empty()) return nullptr; // 面试官后面提醒
TreeNode* ret = s.top();
TreeNode* t = ret;
if (t&&t->right) {
t = t->right;
while (t) {
s.push(t);
t = t->left;
}
} else {
TreeNode* tRight = t;
s.pop();
if (s.empty()) return ret; // 面试官后面提醒
t = s.top();
while (t->right == tRight) {
s.pop();
if (s.empty()) return ret; // 面试官后面提醒
tRight = t;
t = s.top();
}
}
return ret;
}
}
1.3 代码解释(4分钟)
写完之后,让我解释代码,并指出几个没有加特殊情况的位置。
2. 全排列
给定一个数组,打印它的全排列
[1,2]
-> [1,2] [2,1]
2.1 说思路(2分钟)
- 面:说不太清,你觉得没问题就直接写吧
2.2 代码(7分钟)
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int>> ans;
void sol(vector<int>& v, vector<int> ret, vector<bool> flag) {
if (ret.size() == v.size()) ans.push_back(ret);
for (int i = 0; i < v.size(); ++ i) {
if (flag[i]) {
ret.push_back(v[i]);
flag[i] = false;
sol(v, ret, flag);
flag[i] = true;
ret.pop_back();
}
}
}
int main() {
vector<int> v;
for (int i = 1; i <= 3; ++ i)
v.push_back(i);
vector<int> ret;
vector<bool> flag(v.size(), true);
sol(v, ret, flag);
for (int i = 0; i < ans.size(); ++ i) {
for (int j = 0; j < ans[i].size(); ++ j)
cout << ans[i][j] << ' ';
cout << endl;
}
}
反问
- 我:你觉得我还有什么需要加强的地方嘛
- 面:你的广度还行,但是深度不够,比如虚函数和智能指针
二面2020/12/21/14:05-15:05
自我介绍
简历有写c++,接受其他语言嘛:具体什么语言
golang,python:可以接受,只是偏向c++
实习时间:没有其他问题,可以实习到转正
说明转正时间(提醒我时间很长):没特殊情况,希望实习到转正
base上海?:是的
阐述实习的工作(3分钟)
说说进程、线程、协程:说了进程是资源调度单位,线程是cpu调度单位,协程有个通道可以传输数据。粒度上:进程 > 线程 > 协程
可以试着说说它们的优缺点:进程切换的缺点,时间开销;其他的我可能不太了解,就说这么多吧
说说进程、线程的通信:进程:通道(口误把管道说成了通道。。)、信号,socket也算吧;线程用信号比较多吧。
算法题
说一说lru,缓存大小为K,怎么设计数据结构:用链表保存缓存数据,说了访问的过程;继续说链表查找上的缺点,时间复杂度为O(n),所以用哈希来查找,时间复杂度可以降为O(1)。(2分钟)
如果加上过期时间呢:在链表节点中加一个更新时间,在访问数据的时候,判断一下时间有没有过期,就行了吧。(3分钟)
写一下吧:
(刚开始的时候自己实现的 list 来写的,挺麻烦的,我突然反应过来之前是用stl的。这时候已经过了15分钟)我:我可以改成用stl么。。。。
面:可以的,你改
(15分钟后,写完了)
#include <iostream>
#include <unordered_map>
#include <list>
#include <pair>
using namespace std;
struct ListNode {
int val;
ListNode* next, *pri;
double update;
};
class lru {
// ListNode* head, *tail;
list<pair<int, double>> l;
unordered_map<int, list<pair<int, double>>::iterator > m;
int K;
int nums;
double T;
lru(int k, double t) {
K = k;
nums = 0;
T = t;
}
void set(int x) {
if (m.find(x) == m.end()) {
auto p = make_pair(x, time());
l.push_back(p);
m[x] = l.rbegin();
nums ++;
} else {
auto p = m[x];
p->first = x;
p->second = time();
l.erase(p);
l.push_back(*p);
m[x] = l.rbegin();
}
if (nums > K) {
l.pop_front();
nums --;
}
}
int get(int x) {
if (m.find(x) == m.end()) {
return 0;
} else {
auto p = m[x];
if (time() - p->second >= T) {
m.erase(x);
l.erase(p);
return 0;
} else {
p->second = time();
l.erase(p);
l.push_back(*p);
m[x] = l.rbegin();
return x;
}
}
}
}
如果是多线程里面会出错嘛:会出错
那怎么解决呢:在对数据结构内部进行修改的时候加锁
具体点:
// 这里加写锁
l.erase(p);
l.push_back(*p);
m[x] = l.rbegin();
mysql 的索引数据结构:B+树
说说为什么用 B+ 树:说了范围查找的优点
说说 git merge 和 git rebase 哪个好:rebase
说说有什么优点:不太清楚
反问
看源码和做项目,从面试和自我提升的角度哪个更好:都很重要。后者重要一点
面的哪个组:广告创意
面试官介绍了4分钟:感觉还挺有意思的
三面2020/12/24/14:00-14:55
自我介绍(2分钟)
实习介绍(12分钟)
redis常用数据结构:字符串、字典、链表、跳表、压缩列表、集合、有序集合
有序集合的实现方式:跳表和压缩列表
具体说说:压缩列表用于数据量小的情况,存储方式是线性的;数据量大的时候会自动转换为跳表,跳表和链表一样,由一个一个节点链起来,但是它的节点和链表不一样,它的节点会被随机分配一个层数,相同的层数链接起来。
跳表查询的时间复杂度:log(n)
有序集合中有查询时间复杂度为O(1)的实现吗:有序集合应该是没有的。
redis 的过期策略:有好几种吧,常见的lru
不是说这种,redis里面不是有好多不同的键、值,它们的过期策略:哦,在时间事件里面,会对过期的东西直接删掉。另外,访问到的时候,如果它过期了,也会进行删除
之前用的数据库是什么:mysql
引擎是什么:innodb
隔离级别是:可重复读
怎么实现的可重复读:MVCC
使用的乐观锁还是悲观锁:乐观锁
MVCC的实现:增加了版本号,有个版本链,每次事务开始的时候有一个快照,其他事务读取的时候,读取那个快照。事务内部修改的时候有一个日志,日志会记录事务内部对这一行的修改,会用于事务的回滚。
binlog、redolog(也可能是undo log)听说过吗:听说过,但是没深入了解过
innodb的索引数据结构是什么:B+树
主键和非主键的索引呢:它一开始只会对主键建索引吧
非主键呢:不清楚
B+树的叶子节点存放的什么:整体的数据,包括索引,都在里面
浏览器输入一个url,返回了一个error,怎么找出错的地方:通过状态码
没有状态码,只有一个error:啥都没有吗。。。
dns的返回标志:那可能是dns上的错误,可能不存在这个域名
但是我输入的toutiao.com,我知道是存在的:是的。那可能是dns查询的时候出现了错误,那可以一步一步的查,它刚出去的时候访问的那个dns服务器(应该都是有的,小声bb)。可能最近的那个路由器或者电脑里面没有配dns。有没有可能(强颜欢笑)
你怎么知道最近的呢:查询dns的路径,好像是有一个命令的
什么命令呢:这命令名字我不记得了。。。
linux怎么查看系统的状态呢:top
top出来显示什么:cpu占用率,内存占用率、一些进程的信息
实习中,python后端,是怎么实现的http连接,是django吗:emm,是通过webpy的api直接调用的。。。。
你的request,post,具体实现是什么:emmm,我可以从c++方面来说吗
嗯:它要创建一个套接字,给套接字分配一个端口,等待连接的到来,连接来了之后,像redis的话,它收到连接,会复制一个socket,分配一个新端口,把这个连接分配给新socket,原先的socket继续listen。连接之后,读取socket里面缓冲区的内容,再内部进行处理,再通过socket传输出去。
socket监听是哪个层:tcp
之前问的是http,这方面是不是不太了解:嗯
还有什么领域没问你的吗:操作系统,计算机网络,你们应该用golang,python比较多吧
也有用c++的:那还有c++这些,了解的多一些
段页式存储执行的时候会访问几次内存:访问内存会通过页的级数也有关系,如果它是一级页表的话,它取页号一次,取物理地址一次,如果没有在内存中的话,还会访问磁盘
我问的段页式:段页式先取它的段内偏移量,然后再访问物理地址,是两次
进程的存储分布:栈、堆、数据区、代码区、共享库
代码放在哪:代码区
变量放在哪:静态变量放在静态的变量区,局部变量会放在栈中
指针呢:指针也是有静态的吧(小声(不确定)),直接声明的会放在栈中
指针指向的地址分配的空间呢,malloc的:放在堆中
算法
做题吧,我先看看你之前做的什么题
求s1中包含s2中字符(不用考虑顺序)的最小子串(长度最小)
s1 = "abcedeabd"
s2 = "abe"
思路
滑动窗口:
可以先找到第一个包含s2字符的字串,比如例子中的abce,然后往右移,把a提出去,找到右边第一个提出去的a,这就是下一个子串。把所有子串找出来,再找最短的。
面:你这个会有问题,你先写代码吧。
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <string>
using namespace std;
int main() {
unordered_map<char, int> um1, um2;
unordered_set<char> us;
string s1 = "abcedeabd";
string s2 = "abe";
vector<string> ans;
int left, right;
left = right = 0;
int sum = 0;
for (int i = 0; i < s2.length(); ++ i) {
us.insert(s2[i]);
um1[s2[i]] ++;
sum ++;
}
string s;
um2 = um1;
int i = 0;
while (i < s1.length() && um2[s1[i]] == 0)
i ++;
while (sum > 0) {
if (um2[s1[i]]) {
um2[s1[i]] --;
sum --;
}
s += s1[i];
i ++;
}
ans.push_back(s);
for (; i < s1.length(); ++ i) {
char ch = s[0];
int cnt = 1;
// 找到截取的地方
if (um1[s[0]] == 1) {
while (us.find(s[cnt]) == us.end()) {
// TODO
}
}
while (s[cnt] )
s = s.strsub(1, s.length() -1);
while ()
if (sum == 0){
ans.push_back(s);
um2 = um1;
}
}
cout << "Hello World!" << endl;
}
(17分钟后)
面:没什么时间了,说说代码吧
我:(对着代码)先找到第一个符合条件的子串,然后移除掉第一个字符s[0],这里需要把s中,非s2字符的其他字符也移除掉,这里就有问题了,如果后面的s[cnt](已经是s[cnt] in s2了),正好是移除掉的s[0],这里有两种情况,如果s2中只有一个s[0],那么这个时候right不用右移,如果s2有多个s[0],则与其他字符做同样的处理。感觉后面还有挺多要写的。。。
面:还有更好的解法,你可以之后去想一想。
反问
问:推荐一本书
面:这种主观的推荐我们是规定不能说的哈
问:上下班时间
面:10105.5,你们应该都知道
我:不同组也有差异吧哈哈
面:那你想要多呢还是少呢(笑)
以上都是面试过程中的回答,没有加其他东西,也没有修改错误,有错误的地方欢迎大家纠正!
三面没过,换了个组继续加面两轮
文章来源:https://www.nowcoder.com/discuss/583059
- 上一篇: 死锁的 4 种排查工具 死锁的处理方法
- 下一篇: C++ 学习笔记 c++全套教程
猜你喜欢
- 2024-12-29 Java 知识点整理:Spring、MySQL java中的spring
- 2024-12-29 互联网大厂是如何使用牛客网提高面试效率的
- 2024-12-29 LightGBM最强解析,从算法原理到代码实现
- 2024-12-29 百度2023届暑期实习生面经-产品运营岗
- 2024-12-29 来,做一道字节跳动面试的简单算法题
- 2024-12-29 我的百度面经(共8次面试) 百度面试好过吗
- 2024-12-29 C++ 学习笔记 c++全套教程
- 2024-12-29 死锁的 4 种排查工具 死锁的处理方法
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)