字节-商业化-后端开发-日常实习-一面

八股

删除掉了部分项目相关

  1. 讲讲IO多路复用?select/poll/epoll的差别?
  2. 动态线程池技术?如果让你来实现一个线程池,会考虑哪几部分?
  3. Kafka的使用场景?如果出现了消息堆积如何优化?

算法

1. 八皇后

总结

问的问题很少。算法折腾了半小时。

八股答的很糊,说的很多但是可能术语不够精确。

简历上写了埋点监控,但是被问起来场景,为什么要用这个方法,数据落下来怎么用,如果重新设计怎么设计,被问懵了。

复盘

IO多路复用

什么是IO多路复用?

多路:指的是多个socket网络连接;

复用:指的是使用一个线程来检查多个网络连接(或者说文件描述符)的就绪状态。

好处是:解决了单线程处理多个IO操作时的阻塞问题。在需要处理大量并发IO的情况下,可以提升程序性能和响应速度。

常用的多路复用技术?

select -> poll -> epoll

select:

提供了一种单线程处理多个IO操作的方式。

流程如下:

  1. 应用程序调用select函数,参数中传递待监听的文件描述符集合,进入阻塞。
  2. 内核将这些文件描述符加入到内核维护的等待队列,监听其IO状态。
  3. 当有文件描述符准备好IO操作,内核将其标记为可读或可写。
  4. 内核将这些准备好的文件描述符信息返回给应用程序,select返回。
  5. 应用程序接受返回后,遍历文件描述符,检查哪些文件描述符准备好了IO操作。
  6. 应用程序根据文件描述符状态进行IO操作,完成后调用select等待下一次IO事件。

存在的问题:

问题一:首先是用户态和内核态之间拷贝的开销,两次copy,从用户态->内核态,因为需要告知内核态需要管理哪些文件描述符,这个无法省。但是内核态->用户态,明显只有部分fds(文件描述符)发生了事件,却做了一次整体拷贝。并且,用户态只知道发生了事件,但是不知道是哪些fds,所以还要重新遍历,时间复杂度是O(n)。

问题二:单个进程/线程能监听的最大连接数,在内核中写死了,32位:1024;64位:2048。

poll

解决了问题二,在调用时,可以指定数组大小,而非select中固定大小的bitmap,该数组的大小仅受限于系统资源。但这个并非关键问题。

epoll

有几点改进:

1.IO事件发生后放入就绪链表,避免了用户态需要遍历来确定事件的处理;

2.只有在用户态主动调用epoll_wait时才执行拷贝,而非一旦发生IO事件即拷贝,减少了用户态和内核态的切换次数,提高了效率。

另外, 红黑树的结构,便于查询、插入、删除,时间复杂度都是O(logn)。、

MQ中的消息堆积问题

出现消息堆积的原因

1.生产端数据激增

2.消费端消费能力不足或挂掉

3.kafka中数据倾斜

解决方法:

1.从生产端入手:如果是过时无效的数据直接清空,拉到latest;如果有效,但是不要求消费顺序,可以直接消费最新的数据,lag部分的数据用离线补偿。

2.从消费端入手:

  1. 增加消费者的数量。
  2. 增加分区的数量。(增加分组时,同步要增加一组内的消费者的数量)
  3. 加强消费者的消费能力:优化消费逻辑,批量消费,异步消费

3.数据倾斜问题解决:根据业务重新设置key,使其均匀。

八皇后

非优解,写的麻烦,仅为理思路

不考虑输出的形式,现在需要使用回溯去遍历所有的可能。

主干函数如下:

/**
tmp: 用于记录每行所选定的y轴坐标的index,x轴坐标隐藏在数组下标中了
x:当前在处理第几行
n:处理的是几皇后问题
**/
public void backtrack(List<Integer> tmp, int x, int n){
	if(tmp.size() == n){
	  	//当tmp中元素数量为n时,说明每行的皇后都找到了合适的位置。
	  	//res是一个全局变量,用于记录结果,先不考虑他。
		res.add(new ArrayList<>(tmp);
		return;
	}
	//对于当前第x行的皇后,要考虑0~n-1的所有可能的y轴坐标
	for (int y = 0;y < n;y++){
	  	//尝试坐标(x,y),如果是个合法位置,则考虑x+1的摆放
		if (isValid(tmp, y){
		  tmp.add(y);
		  backtrack(tmp, x+1, n); //进入下一行
		  tmp.remove(tmp.size()-1);
		}
	}
}

其中有些问题,比如如何判断当前皇后位置是否合法?

public boolean isValid(List<Integer> tmp, int y2){
	//当前的坐标实际上是(tmp.size(), y2),点还没放进tmp,所以不用-1
  	int x2 = tmp.size();
  	for (int x1 = 0; x1 < tmp.size(); x1++){
		int y1 = tmp.get(x1);
	  	//肯定不会同行的,同列或者斜线即为不合法,斜线可以看x,y轴增量是否一致
	 	if (y1 == y2 || Math.abs(x1 - x2) == Math.abs(y1 - y2)){
			return false;
		}
	  	return true;
	}
}

现在问题已经解决了,收个尾。

目前给出的结果实际上是 [[0,1,2,3,4],[1,0,4,2,3],...],即给出y轴的坐标,题目要求给出的形式是

[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

写个函数改改

public List<List<String>> visualize(int n){
	List<List<String>> finalRes = new ArrayList<>();
  	for(List<Integer> item : res){
		List<String> tmp = new ArrayList<>();
	  	for (int y : item){ //第y个为Q
			tmp.add(".".repeat(y) + "Q" + ".".repeat(n-y-1));
		}
	  	finalRes.append(tmp);
	}
}

整体流程如下:

class Solution {
    List<List<Integer>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        backtrack(new ArrayList<>(), 0 , n);
        
        return visualize(n);
    }

    public void backtrack(List<Integer> tmp, int x, int n){
       
    }

    public boolean isValid(List<Integer> tmp, int y2){
      
    }

    public List<List<String>> visualize(int n){
	}
}

#面经#
全部评论
m
点赞 回复 分享
发布于 02-27 23:37 贵州
M
点赞 回复 分享
发布于 02-28 13:11 安徽
m
点赞 回复 分享
发布于 02-29 00:04 上海
m
点赞 回复 分享
发布于 02-29 11:10 北京
m
点赞 回复 分享
发布于 02-29 12:06 江苏
M
点赞 回复 分享
发布于 02-29 16:12 吉林
分享的很详细,楼主真的好人啊。牛牛们,考虑pdd实习机会吗?核心部门:商业化(广告),录用比例极高。
点赞 回复 分享
发布于 03-08 13:57 上海
需要的友友可以看看我首页,直接扫内推码投递,米哈游有大量岗位,可以咨询
点赞 回复 分享
发布于 03-14 08:39 上海

相关推荐

10-11 17:30
湖南大学 C++
我已成为0offer的糕手:羡慕
点赞 评论 收藏
分享
我已成为0offer的糕手:别惯着,胆子都是练出来的,这里认怂了,那以后被裁应届被拖工资还敢抗争?
点赞 评论 收藏
分享
24 162 评论
分享
牛客网
牛客企业服务