心中思考了良久,昨天的面试经历,让我对4399充满了好感,之前是想进4399,但听师兄介绍,网易会更好,但如今对大厂了解的越多,就知道自己真正的需要是什么,我想在工作之余,晚上能有自己的时间去做自己的事情,而4399更适合我,我现在只有一个目标,进入4399!!!
算法题(牛客网)
1. 输出二叉树的右视图
昨天没写出来,今天再试试,难就难在创建二叉树,之前写过,现在又忘了哈哈哈。我靠着推导跟记忆写出来了!!!但是应该有更简单的方法。
代码详情:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 求二叉树的右视图
* @param xianxu int整型vector 先序遍历
* @param zhongxu int整型vector 中序遍历
* @return int整型vector
*/
vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {
// write code here
TreeNode* root = create_tree(xianxu, zhongxu); //恢复二叉树
queue<TreeNode*> que; //创建队列
que.push(root);
vector<int> result; //存储最终答案
while(!que.empty()){
int size = que.size(); //记录当前队列长度
while(size--){
TreeNode* node = que.front(); //记录队首节点
que.pop(); //弹出队列
if (size == 0){ //最后一个节点代表右视图观察节点
result.push_back(node->val);
}
if (node->left){ //压入左节点
que.push(node->left);
}
if (node->right){ //压入右节点
que.push(node->right);
}
}
}
return result;
}
//辅函数-恢复二叉树
TreeNode* create_tree(vector<int> xianxu, vector<int> zhongxu){
if (xianxu.size() == 0) return nullptr;
int val = xianxu[0]; //当前根节点
TreeNode* root = new TreeNode(val); //创建根节点
auto pos = find(zhongxu.begin(),zhongxu.end(),val); //找到根节点在中序遍历的位置
int length = pos - zhongxu.begin(); //计算目标节点到第一个节点的距离
//链接左右节点
root->left = create_tree(vector<int>(xianxu.begin()+1,xianxu.begin()+1+length),
vector<int>(zhongxu.begin(),zhongxu.begin()+length));
root->right = create_tree(vector<int>(xianxu.begin()+1+length,xianxu.end()),
vector<int>(zhongxu.begin()+1+length,zhongxu.end()));
return root; //返回根节点
}
};
2.判断是否为回文字符串
这道题简单,要是寻找最长回文子串就难了。
代码详情:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param str string字符串 待判断的字符串
* @return bool布尔型
*/
bool judge(string str) {
// write code here
int l = 0,r = str.size()-1; //创建左右指针
while(l < r){
if(str[l++] != str[r--]){ //判断前后是否一样
return false;
}
}
return true;
}
};
3. 进制转换
笔试每次都考进制转换问题,这次好好刷一刷。这题让我理解了十进制转其他进制的方法。
代码详情:
class Solution {
public:
/**
* 进制转换
* @param M int整型 给定整数
* @param N int整型 转换到的进制
* @return string字符串
*/
string solve(int M, int N) {
// write code here
string t = "0123456789ABCDEF"; //转换字符串
string ans; //存储最终答案
bool minus = false; //存储M是否为正数
if (M < 0){ //如果M为负数,记录minus,取正。
minus = true;
M = -M;
}
while(M){
ans.push_back(t[M%N]); //取余
M /= N;
}
if (minus) ans.push_back('-');
reverse(ans.begin(), ans.end()); //反转字符串
return ans;
}
};
4. 二进制中1的个数
我居然用二进制的正数负数原理,硬用数组写出来了。
代码详情:
class Solution {
public:
int NumberOf1(int n) {
vector<int> ans; //存储二进制
bool minus = false; //标志符,是否为负数
if (n < 0){
minus = true; //n为负数
n = -n;
ans = vector<int>(32,1); //由于负数要取反,因此这里直接取反
}
else{
ans = vector<int>(32,0);
}
int i = 0; //数组下标
while(n){
ans[i] = (ans[i] + (n % 2)) % 2; //取余
i++;
n /= 2;
}
reverse(ans.begin(), ans.end()); //翻转数组
if(minus){
int exp = 0; //记录进位
ans[31] = (ans[31]+1) % 2;//负数加一
if (ans[31] == 0) exp = 1;
for(i = 30;i >= 0;--i){
if(exp == 1){ //向前进一
ans[i] = (ans[i] + exp) % 2;
exp = 0;
if (ans[i] == 0) exp = 1;
}
else{
break;
}
}
}
int Num_1 = 0; //记录1的个数
for(i = 0;i < 32;++i){
if(ans[i] == 1) Num_1++;
}
return Num_1;
}
};
但是有更加简单的版本,代码详情:
class Solution {
public:
int NumberOf1(int n) {
int ans = 0; //记录1的个数
int mark = 1;
while(mark != 0){ //遍历二进制的1
if (n & mark) ++ans;
mark <<= 1;
}
return ans;
}
};
5. 懂二进制
有了上一题的经验,这一题利用上一题一位一位的看就行了。
代码详情:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param m int整型
* @param n int整型
* @return int整型
*/
int countBitDiff(int m, int n) {
// write code here
int ans = 0; //记录有多少位不同
int mark = 1; //用来获取单个位
while(mark != 0){
if ((m & mark) != (n & mark)){ //位数不同
++ans;
}
mark <<= 1;
}
return ans;
}
};
面试题
1. 什么是死锁?死锁产生的条件?
死锁:在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其他进程释放它们保持的资源,从而导致进程无法继续进行,称为死锁。简单的来说,就是两个及以上进程无限期阻塞、相互等待的一种状态。
死锁产生的四个必要条件:
1. 互斥:至少有一个资源是非共享的。
2. 占有并等待:进程保持某种资源又同时申请其他资源(该资源被其他进程占有)。
3. 非抢占:进程不能被抢占,资源只能被进程主动释放。
4. 循环等待:若干个进程之间形成一种头尾相接的环形等待资源关系。
如何预防死锁:
1. 死锁预防(将四个必要条件至少一个条件打破,就能预防死锁):
a. 打破互斥条件:使资源共享。
b. 打破占有并等待:实行资源预先分配策略
c. 打破非抢占式条件:允许进程抢夺其他进程资源。
d. 打破循环等待条件:实现资源有序分配策略。
2. 死锁解除
1. 进程终止。
2. 资源抢占。
2. 进程有哪几种状态?
1. 就绪状态:等待分配处理
2. 运行状态:占用处理机资源运行。
3. 阻塞状态:等待某种资源或条件导致进程无法继续运行。
3. 虚函数的作用以及实现原理
口头回答:虚函数是实现多态的基础,每个拥有虚函数的类都有一个虚函数表,该表保存每一个派生类的虚函数指针。
虚函数作用:定义一个基类指针,其指向一个继承类,可以通过基类指针在运行时决定调用基类函数还是继承类函数,从而实现多态。每个有虚函数的类都有虚函数表(占四字节),该类的任何对象都有虚函数的指针。
4. overload以及overwrite的区别
口头回答:重载是将函数重新定义,必须拥有不同的参数,相同的函数名,返回值可同可不同,覆写是将父类函数重新写,相同函数,相同参数,相同返回值。
重载(overload):子类改写父类方法,函数名必须相同,参数列表必须不同,返回值类型可以不同。
重写(overwrite):派生类重写基类函数,同一个函数的不同版本,相同的函数名,相同的参数列表,相同的返回值类型。
5. 一个空的class类里有什么
口头回答:空的类里面有五个默认函数,默认构造函数,拷贝构造函数,析构函数,赋值运算符重载。
空的类含有(六个默认函数):默认构造函数,拷贝构造函数,析构函数,赋值运算符重载,取地址操作符重载,const修饰的取地址操作符重载。
6. 一个机构体中有一个int,一个char,一个static int,问这个结构体占多少内存?
口头回答:int占4个字节,char占2个字节,static int占4个字节,根据字节对齐机制,该结构体占3*4 = 12字节。
内存对齐的原因:
1. 性能原因:未对齐地内存,处理器可能要做两次访问,而对齐的内存只需要一次访问。
2. 空间原因:防止空间浪费。