字节跳动暑期后端实习面经(已offer)

本来是通过闪电内推投的南京的后端开发实习生,HR打电话说南京没有岗位了,帮我转投的上海产品研发和工程架构部。

一共三面技术面,没有HR面,直接offer。

一面

聊项目,项目里用到一个开源路由软件quagga,问我和传统路由软件(华为)的区别;

项目开发遇到的难点、项目开发过程等等;

写题目,给定字符串s1,s2,target,判断target是否是s1、s2的交错组成,比如s1 = "abcd", s2 = "efgh", target = "aebfghcd",target中字符由s1和s2中字符构成,并且s1/s2中字符在target中保持原来的顺序。

上来就想到了归并排序的merge思路,只要按顺序看target是否是s1/s2的merge就行了,比如target第一个字符a和s1第一个字符a相同,就比较s1剩余的部分/s2以及target剩余部分,否则比较s1/s2剩余的部分以及target剩余部分,case通过率40%

发现问题,s1 = "abc", s2 = "aef", target = "aefabc",如果第一个a和s1的a匹配,那么target剩余的部分无法和s1剩余部分以及s2匹配,然而如果第一个a和s2的a匹配,则能匹配成功,因此遇到相同字符时两种情况都要考虑。

但是已经来不及改了,还被面试官吐槽直接把逻辑写到main函数中,写算法的坏习惯。。。

面试完重新写了, AC,下面是编辑器重新写的,可能会有错误。

bool help(string s1, int i, string s2, int j, string target, int k, vector<vector<int>>& dp) {
    if (k == target.size())
        return true;
    if (dp[i][j] != -1)
        return dp[i][j];
    bool res = false;
    if (i < s1.size() && s1[i] == target[k]) {
        res |= help(s1, i + 1, s2, j, target, k + 1);
        if (res) 
            return dp[i][j] = res;
    }
    if (j < s2.size() && s2[j] == target[k])
        res |= help(s1, i, s2, j + 1, target, k + 1);
    return dp[i][j] = res;
}

bool isCross(string s1, string s2, string target) {
    int l1 = s1.size(), l2 = s2.size(), l = target.size();
    // s1和s2长度之和和target长度不相等
    if (l1 + l2 != l)
        return false;
    // 记录中间状态结果,减少重复计算    
    vector<vector<int>> dp(l1 + 1, vector<int>(l2 + 1, -1));
    dp[l1][l2] = 1;
    return help(s1, 0, s2, 0, target, 0, dp);
}

最后问了一下C++的多态就结束了,面试完面试小哥让我好好想想代码怎么写,以为大概率凉凉,结果第二天HR约了二面。

二面

二面的面试官非常友好,整个面试下来体验不错。

自我介绍,介绍简历上2个项目;

C++11 特性介绍;

右值引用和移动语义;

智能指针;

写参数是指针的模板函数;

template<class T>
void func(T* p) {}

linux网络指令,losf/netstat/tcpdump;

TCP/UDP,一揽子问题 + 应用场景;

进程间通信的几种方式,大概介绍;

算法题,给定一个数src,一个目标数target,每个数每次可以加上自己的因数(除1和本身)跳到下一个数,问src最少用几次跳到target,比如4->24,最短路径长度是5(4->6->8->12->18->24,最短路径可能不知一条),动态规划,面试官提示了思路完成。

提问环节,介绍部门所用技术栈,面试官讲的非常详细,游戏中台搭建相关内容,云原生技术等等。

vector<int> factors(int n) {
    vector<int> res;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            res.push_back(i);
            res.push_back(n / i);
        }
    }
    return res;
}

int minSteps(int src, int target) {
    vector<int> dp(target + 1, INT_MAX);
    dp[src] = 0;
    // 从src开始遍历直达target
    // 在当前数基础上加上每个因数,结果可以从当前位置再跳一步获得
    for (int i = src; i < target; ++i) {
        for (int factor : factors(i)) {
            dp[i + factor] = min(dp[i] + 1, dp[i + factor]);
        }
    }
    // 不可达
    if (dp[target] == INT_MAX)
        return -1;
    return dp[target];
}

 三面

问的问题十分全面,但相对而言比较基础。

手写算法题,2道,都是剑指offer原题

1.随机链表复制,每个链表有一个随机指向的指针rand。直接照书上思路写的话比较麻烦,需要先复制每个节点插到原来节点后面,而后确定新节点指针指向,最后将新节点分离出来做成新的链表。

于是我用的map来代替,原节点为key映射到复制后的节点,代码如下:

class Node{
public:
    int val;
    Node* next;
    Node* rand;
    Node(int v) : val(v), next(nullptr), rand(nullptr) {}
};

Node* copyRandomList(Node* head) {
    if (head == nullptr)
        return nullptr;
    map<Node*, Node*> m;
    Node* cur = head;
    while (cur != nullptr) {
        m[cur] = new Node(cur->val);
        cur = cur->next;
    }
    cur = head;
    while (cur != nullptr) {
        m[cur]->next = m[cur->next];
        m[cur]->rand = m[cur->rand];
        cur = cur->next;
    }
    return m[head];
}

2.顺时针打印矩阵,借鉴左程云《程序员面试指南》上的写法,清晰易懂

// 每次打印矩阵一圈
void printCirc(vector<vector<int>>& matrix, int tr, int tc, int dr, int dc) {
    int i = tr, j = tc;
    while (j < dc) {
        cout << matrix[tr][j] << " ";
        ++j;
    }
    while (i < dr) {
        cout << matrix[i][dc] << " ";
        ++i;
    }
    while (j > tc) {
        cout << matrix[dr][j] << " ";
        --j;
    }
    while (i > tr) {
        cout << matrix[i][tc] << " ";
        --i;
    }
}

void printMat(vector<vector<int>>& matrix) {
    int tr = 0, tc = 0;    // 左上角行列坐标
    int dr = matrix.size() - 1, dc = matrix[0].size() - 1;    // 右下角行列坐标
    while (tr <= dr && tc <= dc) {
        printCirc(matrix, tr++, tc++, dr--, dc--);
    }
}

select/epoll/poll区别以及使用注意点;

线程和进程;

100亿个访问某网站的用户id,统计出现次数最多的10个用户,老生常谈大数问题;

shell脚本写找出一个文件中出现次数最多的10个用户id,我把出现次数前10的次数显示了出来。。面试官看命令没啥问题就过了。

sort | uniq -c | awk 'print $1' | sort -rn | head -10

问了对docker底层原理的理解。答了底层的命名空间隔离技术,network namespace以及与传统虚拟化技术的区别。

非常基础的记不太清了,后续有补充会更新。

 

大概隔了一周接到offer call和正式邮件,由于已经接受了鹅厂的offer,没有接受这个offer,希望以后有机会能去字节学习工作。

目前已经写了腾讯和字节的面经,后续会继续更新百度、网易、网易雷火(均已offer),华为(流程中),阿里微软(挂)的面经,也算作对春招的一次认真总结,希望对大家有所帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值