架构设计第一讲:架构设计相关面试题汇总
Posted 程序员 jet_qi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了架构设计第一讲:架构设计相关面试题汇总相关的知识,希望对你有一定的参考价值。
架构设计第一讲:架构相关面试题
本文是架构设计第一讲:主要讲解架构相关面试题
文章目录
- 架构设计第一讲:架构相关面试题
- 前言:如何学习架构
- 场景题
- 1、秒杀系统(12306秒杀火车票,淘宝)
- Action:Redis集群能抗住的QPS是多少呢?
- 2、短链接生成
- 3、高并发的红包系统
- 4、分布式ID生成
- 5、分布式限流
- 6、分布式定时任务
- 7、消息推送平台
- 8、大文件有限内存排序
- 9、火车票系统的设计
- 10、设计一个 DNS 的 Cache 结构,要求能够满足每秒 5000 次以上的查询,满足 IP 数据的快速插入,查询的速度要快(题目还给出了一系列的数据,比如站点数总共为 5000 万、IP 地址有 1000 万等)。
- 11、有 N 台机器, M 个文件,文件可以以任意方式存放到任意机器上,文件可任意分割成若干块。假设这 N 台机器的宕机率小于 1/3,想在宕机时可以从其他未宕机的机器中完整导出这 M 个文件,求最好的存放与分割策略
- 12、假设有三十台服务器,每台服务器上面都存有上百亿条数据(有可能重复),如何找出这三十台机器中,根据某关键字,重复出现次数最多的前 100 条?要求使用 Hadoop 来实现。
- 13、设计一个高并发系统,说明架构和关键技术要点。
- Action1:百万并发抢券业务你如何设计?
- Action2:**情景题:如果一个外卖配送单子要发布,现在有200个骑手都想要接这一单,如何保证只有一个骑手接到单子?**
- Action3、**场景题:美团首页每天会从10000个商家里面推荐50个商家置顶,每个商家有一个权值,你如何来推荐?第二天怎么更新推荐的商家?**
- Action4、场景题:微信抢红包问题
- Action5、场景题:1000个任务,分给10个人做,你怎么分配,先在纸上写个最简单的版本,然后优化。
- Action6、场景题:保证发送消息的有序性,消息处理的有序性。
- Action7、有十万个单词,找出重复次数最高十个?
- Action8:架构设计题目远不止这些,主要从以下几个方面准备 ☆
- Action9:一致性hash算法的使用场景?
前言:如何学习架构
- 包含如何学习架构?
基础到方法论
包括架构的概述,特点,目标,本质以及方法论等
-
架构 - 架构基础: 特点,本质
- 总结下架构相关的基础知识:概述,特点,目标,本质…
-
如何理解架构
- 理解架构,包括架构的视角,架构的演进,服务化演进,架构的核心要素
- 架构 - 理解构架的视角
- 在学习架构时,我认为首先要理清楚架构的视角,因为你所认知的架构和别人所说的架构可能是两码事。对于不同职位的视角是不一样的,比如开发而言他更多的看到的是开发架构;对售前人员,他可能更多的看到的是业务架构;对于运维人员,他看到的可能是运维架构;而对于技术支持和部署人员,他更多的看到的网络和物理架构。
- 架构 - 理解构架的分层
- 技术框架(technological Framework)是整个或部分技术系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法。于开发者而言,实际工作从通常采用的是分层模型,由于其重要性,这里独立一个章节,总结经典的七层逻辑架构。
- 架构 - 理解架构的演进
- 在学习架构时,第一步不要去学习框架,而是要学习架构的演进。强烈推荐李智慧老师的《大型网站技术架构》,这本书翻起来很快,对构筑你自己的体系很有帮助,本文的内容来源于它,在此基础上拓展了下。
- 架构 - 理解架构的服务演化
- Kubernetes、Service Mesh 和 Serverless应该是最近比较火的了,而上文主要从逻辑架构角度分析了架构演进,本文将从服务演化和容器编排化的角度帮你增强对架构演进的认识。
- 架构 - 理解架构的模式1
- 架构演进中有很多知识点,总体上可以归结为以下模式,这里说的模式本质是架构中技术点的抽象。强烈推荐李智慧老师的《大型网站技术架构》,本文的内容也是来源于它,在此基础上拓展了下。
- 架构 - 理解架构的模式2
- 本文整理自朱晔的互联网架构实践心得, 他是结合了 微软给出的云架构的一些模式的基础上加入他自己的理解来总结互联网架构中具体的一些模式。我在此基础上进行了些微小的调整。
- 架构 - 理解架构的核心要素
- 一般来说软件架构需要关注性能、可用性、伸缩性、扩展性和安全性这5个架构要素。
-
架构高并发和高可用
- 架构之高并发:缓存
- 高并发实现的三板斧:缓存,限流和降级。缓存在高并发系统中有者极其广阔的应用,需要重点掌握,本文重点介绍下缓存及其实现。
- 架构之高并发:限流
- 每个系统都有服务的上线,所以当流量超过服务极限能力时,系统可能会出现卡死、崩溃的情况,所以就有了降级和限流。限流其实就是:当高并发或者瞬时高并发时,为了保证系统的稳定性、可用性,系统以牺牲部分请求为代价或者延迟处理请求为代价,保证系统整体服务可用。
- 架构之高并发:降级和熔断
- 在高并发环境下,服务之间的依赖关系导致调用失败,解决的方式通常是: 限流->熔断->隔离->降级, 其目的是防止雪崩效应。
- 架构之高可用:负载均衡
- 负载均衡(Load Balance),意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行。是解决高性能,单点故障(高可用),扩展性(水平伸缩)的终极解决方案。
- 架构之高可用:容灾备份,故障转移
- 容灾技术是系统的高可用性技术的一个组成部分,容灾系统更加强调处理外界环境对系统的影响,特别是灾难性事件对整个IT节点的影响,提供节点级别的系统恢复功能。故障转移(failover),即当活动的服务或应用意外终止时,快速启用冗余或备用的服务器、系统、硬件或者网络接替它们工作。故障恢复是在计划内或计划外中断解决后切换回主站点的过程。
- 架构之高并发:缓存
-
架构的安全
- 架构 - 保障架构安全
场景题
整理了一些常见的架构设计面试题,主要记录关键点
- 秒杀系统
- 短链接生成
- 高并发的红包系统
- 分布式ID生成
- 分布式限流
- 分布式定时任务
- 新浪微博怎么推送微博
- 大文件有限内存排序
1、秒杀系统(12306秒杀火车票,淘宝)
1.1、秒杀场景
- 电商系统重常见的一种业务模式,用于吸引用户,刺激留存及消费所做的一种活动。秒杀系统的例子:整点秒杀、商品秒杀。
1.2、秒杀系统的特点
-
1、瞬时流量极大,过了秒杀时间点流量结束。所以不能用机器堆QPS
- 可以在监控上看到明显的峰值
-
2、秒杀商品库存极少,例如1w个用户去抢10瓶茅台
-
3、秒杀时间点未到的时候,刷新量极大,静态资源被访问剧增
-
秒杀系统的流程:
- 1、运营策划活动,选品,在秒杀中台建立秒杀活动,需要距秒杀开始提前一段时间,同时将秒杀数据,库存等信息写入缓存。
- 2、业务方研发根据实际情况来看自身机器是否可以扛得住
- 3、用户进行秒杀
1.3、设计难点
-
1、并发量大,应用、数据库都承受不了
- 针对请求和用户的高并发,我们需要再上线前,首先要做好压测,知道服务qps瓶颈点。针对我们日常业务秒杀的预估,进行服务器的准备。测量tps,寻找薄弱点进行代码优化,服务支撑优化。
- 一般采用redis + 热数据 + mq。前端辅以静态化,cdn加速。带宽扩展。集群负载均衡
-
2、另外难控制超卖。不能因为大用户量高并发扣减库存,导致商品超卖,库存变负
-
为了速度快,我们要在秒杀活动建立之前就把库存同步到redis中。在真正秒杀时,我们一般分两步
- 第一步:判断库存名额是否充足
- 第二步:减少库存名额,扣减成功就是抢到
-
那么我们一般是借助redis的lua脚本特性来进行库存扣减,lua脚本可以保证我们的原子性
-
-
3、防止刷子:防止机器人,恶意刷子,去抢占订单,导致正常用户抢不到商品。
- 主要采取两种策略。一种是同一用户id限流,一种是同一ip进行限流
- 秒杀时一种非常快就结束的活动,我们无需担心进行限流是否会导致影响用户体验。例如一人1秒只能请求10次。一个ip最多只能请求10次。热点商品下10次都抢不到,那就是真的抢不到了,做了限流后给我们带来极大的好处,因为有一些流量可能是机器产生的,而不是人工手点。
- 限流逻辑可以参考这篇文章:
-
可以使用的技术
1.4、设计要点
- 将请求尽量拦截在系统上游,html尽量静态化,部署到cdn上面。按钮及时设置为不可用,禁止用户重复提交请求。
- url唯一化处理,我们在进行静态化时,要对页面资源进行提前的缓存,保证用户直接请求url时,无需去解析请求头,无需重组http协议,可直接找到静态资源进行返回,不与后端做数据交互。一般静态资源我们要放在cdn上。cdn的就近原则可以提高我们的响应速度和命中率。同时cdn开启资源压缩,减少传输数据量,提高速度。
- 秒杀按钮置灰。一个秒杀活动可能是10点开始,但是用户9:59就进来了,疯狂点击秒杀按钮,如果这里让用户疯狂点击秒杀按钮,无论对前端还是服务端都会产生交互压力。所以我们的秒杀一定是在未达到时间点时,不让点。时间点一点,通过前端的定时器来将按钮的状态作为变更,让活动正常进行。
- 秒杀真链接隐藏
- 如果说我们的秒杀在开始之前就被一些黄牛、刷子知道了真正的请求格式,那么他们其实可以提前做好准备,通过机器等方式来进行提前请求。那么又会造成压力真正达到了我们的服务器,也会造成正常人根本秒杀不到商品。所以我们要采取链接加密,每次秒杀无规律可言,时间到了,才能看到真实秒杀地址。
- 设置页面缓存,针对同一个页面和uid一段时间内返回缓存页面。
- 数据用缓存抗,不直接落到数据库。
- 读数据的时候不做强一致性校验,写数据的时候再做。
- 在每台物理机上也缓存商品信息等等变动不大的相关的数据
- 像商品中的标题和描述这些本身不变的会在秒杀开始之前全量推送到秒杀机器上并一直缓存直到秒杀结束。
- 像库存这种动态数据会采用被动失效的方式缓存一定时间(一般是数秒),失效后再去Tair缓存拉取最新的数据。
- 如果允许的话,用异步的模式,等缓存都落库之后再返回结果。
- 如果允许的话,增加答题校验等验证措施。
- 异步处理订单后续:秒杀成功后,订单处理交于异步服务。
- 交由其他服务进行后续的负责处理,快速将结果返回给用户,告诉他秒杀成功。随着用户点击查看订单的过程,后端其实已经处理完毕。
- 订单失败补偿:遇到订单后续处理失败,必须补偿
- 通常我们都是使用mq的方式进行驱动。mq只要存在,就一定要消费成功。如果消费失败、异常,抛错重试,也要有报警机制及时预警处理。
- 服务降级:遇到紧急情况,可以快速处理。
- 比如:秒杀价格上错了,变成了1块钱一个iphone。那么为了及时止损,我们肯定要做一个降级开关,返回提示文案
- 还有就是服务器撑不住了,同一时间点大量的秒杀活动瞬间开启,我们没有预料到,为了防止整体业务全崩,我们要根据业务线加降级。
1.5、其他业务和技术保障措施
- 业务隔离。把秒杀做成一种营销活动,卖家要参加秒杀这种营销活动需要单独报名,从技术上来说,卖家报名后对我们来说就是已知热点,当真正开始时我们可以提前做好预热。
- 系统隔离。系统隔离更多是运行时的隔离,可以通过分组部署的方式和另外 99% 分开。秒杀还申请了单独的域名,目的也是让请求落到不同的集群中。
- 数据隔离。秒杀所调用的数据大部分都是热数据,比如会启用单独 cache 集群或 mysql 数据库来放热点数据,目前也是不想0.01%的数据影响另外99.99%。
- 另外需要复习缓存穿透、雪崩等等问题,主要的流量都落在了缓存数据库上,需要针对缓存数据库的高可用作保障。
Action:Redis集群能抗住的QPS是多少呢?
- 可以看这张图
2、短链接生成
这个应该是比较公认的方案了:
- 分布式ID生成器产生ID
- ID转62进制字符串
- 记录数据库,根据业务要求确定过期时间,可以保留部分永久链接
主要难点在于分布式ID生成。鉴于短链一般没有严格递增的需求,可以使用预先分发一个号段,然后生成的方式。
看了下新浪微博的短链接,8位,理论上可以保存超过200万亿对关系,具体怎么存储的还有待研究。
3、高并发的红包系统
红包系统其实很像秒杀系统,只不过同一个秒杀的总量不大,但是全局的并发量非常大,比如春晚可能几百万人同时抢红包。
3.1、抢红包的过程
- 我们在发放一个红包时,一般要经历这样一个过程,用户发红包,内部其实是一个下单流程,红包为我们的虚拟商品,生成订单后,用户进行支付。返回红包标识码。用于进行分享。
- 在生成红包的过程中,我们已经将红包内的金额计算好了,用户抢红包的过程可以类比我们的秒杀过程。比如100块钱10个红包,其实就会生成10个令牌,先到的用户获取令牌,并获取对应的金额,后到的用户因为没有令牌了,所以自然就是红包已被领取完。
3.2、设定红包金额的两种算法
方法1:金额随机法(不公平)
- 假设100块钱发送10个红包,最低可以抢到0.01,那么生成第一个红包时,就是0到99.91。假设第一个人抢了50.那么生成第二个红包时,就是0到49.92。这样会存在一个大的问题,就是大家抢红包时,越后生成的金额越小,虽然我们最后会把生成的金额随机排序打乱,但是生成的过程其实已经是不公平的。那么注定不公平。
方法2:二倍均值法(公平)
- 抢红包的公式为(0.01,M/N*2)。其中M为我们的红包剩余金额。N代表剩余的红包数量。2就是我们固定的平均数区间。还是上面那个例子:100元的红包发给10个人。第一个红包在计算时,就是100/10 * 2。第一个红包的金额在0.01到20之间随机。平均数为10,符合我们的要求。假设第一个人最后计算为8块钱,那么第二个红包,就是92/9 * 2。发现我们平均值还是10块钱。以此类推,这个可以确保我们生成红包金额时是公平的。
3.3、主要技术难点
主要在数据库,减库存的时候会抢锁。另外由于业务需求不同,没办法异步,也不能超卖,事务更加严格。
不能采用的方式:
- 乐观锁:手慢会失败,DB 面临更大压力,所以不能采用。
- 直接用缓存顶,涉及到钱,一旦缓存挂掉就完了。
建议的方式:
- 接入层垂直切分,根据红包ID,发红包、抢红包、拆红包、查详情详情等等都在同一台机器上处理,互不影响,分而治之。
- 请求进行排队,到数据库的时候是串行的,就不涉及抢锁的问题了。
- 为了防止队列太长过载导致队列被降级,直接打到数据库上,所以数据库前面再加上一个缓存,用CAS自增控制并发,太高的并发直接返回失败。
- 红包冷热数据分离,按时间分表。
4、分布式ID生成
分布式ID生成大概也算老生常谈的问题了,主要关键在于是否需要严格递增,严格递增的话效率必然大降。
不需要递增的话比较简单:
-
一种方式是预先分片,比如十台机器,每台先分一千个ID,一号机从0开始,二号从1000开始等等。缺点是大致上可以被人看出来业务量。
-
另一种方式是类似雪花算法,每个机器有个id,然后基于时间算一个id,再加上一个递增id。比如如下美团的方案。缺点是机器的时间戳不能回拨,回拨的话会出现问题。
-
如果要求严格递增,我没找到现成的很好的方案,大概只能单机生成,不能分布式了,然后都去单机上取号。效率的话,类似Redis的数据库大概能到每秒十几二十几万的速度。
5、分布式限流
常见的限流方法:
- 固定窗口计数器:按照时间段划分窗口,有一次请求就+1,最为简单的算法,但这个算法有时会让通过请求量允许为限制的两倍。
- 滑动窗口计数器:通过将窗口再细分,并且按照时间“滑动”来解决突破限制的问题,但是时间区间的精度越高,算法所需的空间容量就越大。
- 漏桶:请求类似水滴,先放到桶里,服务的提供方则按照固定的速率从桶里面取出请求并执行。缺陷也很明显,当短时间内有大量的突发请求时,即便此时服务器没有任何负载,每个请求也都得在队列中等待一段时间才能被响应。
- 令牌桶:往桶里面发放令牌,每个请求过来之后拿走一个令牌,然后只处理有令牌的请求。令牌桶满了则多余的令牌会直接丢弃。令牌桶算法既能够将所有的请求平均分布到时间区间内,又能接受服务器能够承受范围内的突发请求,因此是目前使用较为广泛的一种限流算法。
- Google 的开源项目 guava 提供了 RateLimiter 类,实现了单点的令牌桶限流。
- 分布式环境下,可以考虑用 Redis+Lua 脚本实现令牌桶。
如果请求量太大了,Redis 也撑不住怎么办?我觉得可以类似于分布式 ID 的处理方式,Redis 前面在增加预处理,比如每台及其预先申请一部分令牌,只有令牌用完之后才去 Redis。如果还是太大,是否可以垂直切分?按照流量的来源,比如地理位置、IP 之类的再拆开。
6、分布式定时任务
任务轮询或任务轮询 + 抢占排队方案
- 每个服务器首次启动时加入队列;
- 每次任务运行首先判断自己是否是当前可运行任务,如果是便运行;
- 如果不是当前运行的任务,检查自己是否在队列中,如果在,便退出,如果不在队列中,进入队列。
7、消息推送平台
7.1、主要难点
关系复杂,数据量大。一个人可以关注非常多的用户,一个大 V 也有可能有几千万的粉丝。
7.2、先介绍最基本的方案
- 推模式:推模式就是,用户A关注了用户 B,用户 B 每发送一个动态,后台遍历用户B的粉丝,往他们粉丝的 feed 里面推送一条动态。
- 拉模式:推模式相反,拉模式则是,用户每次刷新 feed 第一页,都去遍历关注的人,把最新的动态拉取回来。
- 一般采用推拉结合的方式,用户发送状态之后,先推送给粉丝里面在线的用户,然后不在线的那部分等到上线的时候再来拉取。
另外冷热数据分离,用户关系在缓存里面可以设置一个过期时间,比如七天。七天没上线的可能就很少用这个 APP。
- 可以类比b站推送实时弹幕
8、大文件有限内存排序
对于远高于内存的文件排序。
外归并排序:
- 对文件分割,然后分别排序
- 排好序的文件依次读取一个缓冲区的大小,然后进行排序,输出到输出缓冲区,然后保存到结果文件。
如果是数字,可以用位图排序,但是要求比较苛刻:
- 数字不重复
- 知道最大值
- 相对密集,因为没出现的数字也会占用空间
比较适合电话号之类的。
9、火车票系统的设计
1、网络层
2、架构
3、数据库设计
-
可以类比什么数据结构呢?
-
类比图 可以有加权值,以整个链路的最小值作为新疆–》北京的票数,这个计算过程可以异步处理。空间换时间的思想
-
https://developer.aliyun.com/article/5570 可以参考这篇文章 浅谈12306核心模型设计思路和架构设计
10、设计一个 DNS 的 Cache 结构,要求能够满足每秒 5000 次以上的查询,满足 IP 数据的快速插入,查询的速度要快(题目还给出了一系列的数据,比如站点数总共为 5000 万、IP 地址有 1000 万等)。
todo
11、有 N 台机器, M 个文件,文件可以以任意方式存放到任意机器上,文件可任意分割成若干块。假设这 N 台机器的宕机率小于 1/3,想在宕机时可以从其他未宕机的机器中完整导出这 M 个文件,求最好的存放与分割策略
todo
12、假设有三十台服务器,每台服务器上面都存有上百亿条数据(有可能重复),如何找出这三十台机器中,根据某关键字,重复出现次数最多的前 100 条?要求使用 Hadoop 来实现。
todo
13、设计一个高并发系统,说明架构和关键技术要点。
todo
Action1:百万并发抢券业务你如何设计?
Action2:情景题:如果一个外卖配送单子要发布,现在有200个骑手都想要接这一单,如何保证只有一个骑手接到单子?
- 这个可以用redis的lpush rpop来实现。
- lpush:按照插入顺序排序,可以添加元素到列表的头部或尾部
Action3、场景题:美团首页每天会从10000个商家里面推荐50个商家置顶,每个商家有一个权值,你如何来推荐?第二天怎么更新推荐的商家?
- 这个可以用堆排,第二天更新推荐的可以在要更新之前的时候在redis做计算操作,然后放数据库做个同步就行了。
Action4、场景题:微信抢红包问题
悲观锁,乐观锁,存储过程放在mysql数据库中。
Action5、场景题:1000个任务,分给10个人做,你怎么分配,先在纸上写个最简单的版本,然后优化。
全局队列,把1000任务放在一个队列里面,然后每个人都是取,完成任务。
分为10个队列,每个人分别到自己对应的队列中去取任务。
Action6、场景题:保证发送消息的有序性,消息处理的有序性。
给消息加一个header,识别一下header的syn就行
Action7、有十万个单词,找出重复次数最高十个?
- map<String,Integer> 字符串,频次,然后堆排。
Action8:架构设计题目远不止这些,主要从以下几个方面准备 ☆
先了解常用算法,针对解决各种问题能用哪些算法,比如大文件排序用外排序,大量数据中的命中判断用位图/布隆过滤器等等。注意扩展性、多考虑极端情况,多问自己几个为什么。比如说起单机的限流算法想想分布式的怎么做。
实在不知道怎么弄的,叙述自己的思考过程,着重展示自己考虑周全、思维缜密。
比如之前一个同事面阿里被问整个机房网络挂了/断电了之类的极端问题,相信很多人不可能真的遇到这种情况,而且可用性一般也到不了这个级别。如果是我的话,我会着重考虑数据一致性、数据恢复、脏数据处理之类的问题,是否有其他机房提供服务,如果有的话涉及到网络分区,是不是可以引申谈谈 zab、raft 算法,另外原本两个或者三个机房提供服务,现在瞬间少了一个,其他机房的负载瞬间上升,怎么做削峰、降级,这就有回到我们会的问题上了。
另外等到网络恢复了,怎么恢复服务?
之前处理到一半的数据怎么处理 (借助MQ实现)? 这些引申开来都有太多可以聊。
Action9:一致性hash算法的使用场景?
todo
2021 年大厂面试高频架构题汇总(附答案详解)
金九银十到了,有很多即将面试的朋友咨询怎么做准备。这里必须要和大家再强调一下要准备的7大方面!总结起来包括:1至2门你最熟悉的编程语言+数据结构和算法题+计网+操作系统+设计模式+数据库+开发框架。
为了帮助每一个想顺利通过架构面试的朋友,我们研究了各个大厂常考的面试题型,涵盖各个大厂高频常考点,整理出这「 338 道最新大厂常考架构技术面试题 + 详细解答」,带你斩获大厂架构师 Offer!
通过这份资料你将获得:
架构师必考设计模式面试专题及答案解析
架构面试常见的数据结构预算法题
Spring、SpringBoot、SpringMVC 面试题全家桶
前阿里资深技术专家(P9)华仔提供的大厂案例与成长书单
资料其中包括:
1、架构师面试必考设计模式面试题(35 题)
简述什么是单例模式、工厂模式、值对象模式。
什么是开放 - 封闭法则 ( OCP )?
设计一个 ATM 机,请说出你的设计思路?
......
2、架构师面试常见数据结构与算法题合集(60 道)
字符串的长度是指?
在一个有 8 个 int 数据的数组中,随机给出数组的数据,找出最大和第二大元素一定需要进行几次比较?
对字符串“ mabnmnm ”的二进制进行哈夫曼编码有多少位?
......
3、Spring、SpringBoot、SpringMVC 面试题全家桶(105 道)
Spring Boot、Spring MVC 和 Spring 有什么区别?
Spring 如何处理线程并发问题?
SpringMVC 用什么对象从后台向前台传递数据的?
......
(篇幅有限,此处仅展示部分目录,扫码获取更多)
如何免费领取?
扫描????海报二维码即可免费领取
「 338 道最新大厂常考架构技术面试题」
以上是关于架构设计第一讲:架构设计相关面试题汇总的主要内容,如果未能解决你的问题,请参考以下文章
209期架构设计&分布式&数据结构与算法面试题(2020最新版)