0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

你解得出这道Google面试题吗?

电子工程师 来源:lq 2019-05-14 09:02 次阅读

为了更了解其他人对软件工程的看法,我开始疯狂在 YouTube 上追TechLead视频。在接下来的几天里,我为他在 Google 工作时提出的一道面试题想出了各种解决方案。

通过 TechLead 模拟 Google 面试(软件工程师职位)

TechLead 在 Google 的 100 多次面试中都提出了一个问题,这引起了我对 RxJS 的兴趣。本文会讨论解决该问题的所有传统方法。

他问这个问题的真正目的是从应聘者得到下列信息:在编码之前,他们会问正确的问题吗?提出的解决方案是否符合项目指南?他甚至指出,是否得到正确的答案一点都不重要,重要的是应聘者的思考方式,以及应聘者是否能够理解这个问题。

他谈到了一些解决方案,包括递归方法(受堆栈大小限制)和迭代方法(受内存大小限制)。本文将对这两个解决方案进行详细讨论。

TechLead 的问题

在 TechLead 的问题中,他要求应聘者在如下网格中,计算出所有颜色相同的最大连续块的数量。

当看到这个问题时,我的第一反应是,必须做一些 2D 图像建模才能解决这个问题。听起来这道题在面试中几乎不可能回答出来。

但在听完他的详细解释之后,我方知情况并非如此。在这个问题中,我们需要处理的是已经捕获的数据,而不是解析图像。

数据建模

在编写任何代码之前都需要定义数据模型。对于任何问题,首先要弄清楚我们在处理什么,并收集业务需求。

在我们案例中,TechLead 为我们定义了许多具体的需求,例如:

彩色方块或“节点”的概念

数据集中包含 1 万个节点

节点被组织成行和列,即二维数据

列数和行数可能不同

节点有颜色信息,并具有对“邻接”这一概念的表示方式

我们还可以从数据中获得更多信息:

节点不会重叠

节点不会和其自身邻接

节点不会有重复的邻接

位于边角的节点会比其他节点少一个或两个邻接

还有一些未知信息,例如:

行数与列数的比

可能的颜色数量

只有一种颜色的可能性

颜色的大致分布

开发人员的水平越高,其需要问的问题越多。虽然这有所帮助,但如果不能找出未知信息,问题的实际解决还是会存在阻碍。

大部分人并不会想到询问这些未知信息。在开始研究这个算法之前,我也不知道这些未知信息是什么。要找到所有的未知信息,需要与业务人员进行反复的讨论才行。

对于 TechLead 的这张照片来说,颜色的分布似乎是随机的。他只用了三种颜色,并且没有提到其他限制,因此我们暂时也做这种假设。另外我们还假设,这些颜色可能是相同的。

为了保证算法的有效性,因此我假设我们使用的是 100x100 的网格,以避免处理1行10000列这样的极端情况。

在一般情况下,我会在查看数据的最初几个小时内询问所有这些问题。这也是 TechLead 真正关心之处。应聘者需要思考,是要从编写一个随机解决方案开始,还是要首先找出问题所在。如果提前计划的话,这些问题将更容易处理。在解决这些问题之后,我们最终只需重写代码的一小部分即可。

创建数据模型

我们需要知道数据是如何输入的,以及我们希望以何种形式来处理这些数据。由于没有处理数据的系统,因此我们需要自己设计一个可视化的方法。

数据的基本结构如下:

Color

ID

X

Y

需要 ID 的原因在于,我们可能不止一次碰到同一个图片格。要想防止无限循环的话,就必须标记在这些情况下该图片格所处的位置。

此外,像这样的数据通常会分配某些 ID、哈希值或其他值。它是一个唯一的标识符,因此,我们可以通过某种方式来标识特定的节点。如果我们想知道最大的连续块,就需要知道该块中有哪些节点。

由于 TechLead 使用网格对数据进标识,我假设我们会得到 X 和 Y 的值。依靠这些属性,我就能够生成一些 HTML,并确保生成的内容与他给我们的内容相类似。

这是使用绝对定位来完成的,就像他的例子一样:

答案:3

这种方法也可以处理更大一些的数据集,如下图:

答案:18

下面是生成节点的代码:

1constgenerateNodes=({ 2numberOfColumns, 3numberOfRows, 4})=>( 5Array( 6numberOfColumns 7*numberOfRows 8) 9.fill()10.map((11item,12index,13)=>({14colorId:(15Math16.floor(17Math.random()*318)19),20id:index,21x:index%numberOfColumns,22y:Math.floor(index/numberOfColumns),23}))24)

我们使用行列信息创建一个一维数组,然后根据这些数据生成节点。

我用的是 colorId 而不是 color 。这样做有两个原因,一是随机化更为简洁,二是我们通常必须自己查找颜色值。

虽然 TechLead 没有明确说明,但该题目只用了 3 个颜色值,因此,我将数据集限制为 3 种颜色。我们只需知道它可能有数百种颜色,最终的算法就不需要改变了。

下面是一个更简单的例子,这是一个 2x2 的节点列表:

1[2{colorId:2,id:0,x:0,y:0},3{colorId:1,id:1,x:1,y:0},4{colorId:0,id:2,x:0,y:1},5{colorId:1,id:3,x:1,y:1},6]

数据处理

我们希望知道每个节点的邻接关系,但仅靠 X 和 Y 的值无法做到。所以,给定 X 和 Y,我们还需要找出如何找出相邻的 X 和 Y 值。其实很简单,我们只需在 X 和 Y 上找到 +1 和 -1 的节点即可。

我为此写了一个函数:

1constgetNodeAtLocation=({ 2nodes, 3x:requiredX, 4y:requiredY, 5})=>( 6( 7nodes 8.find(({ 9x,10y,11})=>(12x===requiredX13&&y===requiredY14))15||{}16)17.id18)

我们用来生成节点的方式,实际上是一种计算相邻节点 ID 的数学方法。而在这一步中,我将采取一个与之相反的思路,即假设节点将以随机顺序输入。

我通过再次遍历所有节点来添加邻接关系:

1constaddAdjacencies=( 2nodes, 3)=>( 4nodes 5.map(({ 6colorId, 7id, 8x, 9y,10})=>({11color:colors[colorId],12eastId:(13getNodeAtLocation({14nodes,15x:x+1,16y,17})18),19id,20northId:(21getNodeAtLocation({22nodes,23x,24y:y-1,25})26),27southId:(28getNodeAtLocation({29nodes,30x,31y:y+1,32})33),34westId:(35getNodeAtLocation({36nodes,37x:x-1,38y,39})40),41}))42.map(({43color,44id,45eastId,46northId,47southId,48westId,49})=>({50adjacentIds:(51[52eastId,53northId,54southId,55westId,56]57.filter((58adjacentId,59)=>(60adjacentId!==undefined61))62),63color,64id,65}))66)

这个预处理代码中,我尽量避免了任何不必要的优化。它不会影响算法的最终性能,只会有助于简化我们的算法。

接下来,我将 colorId 换成 color 。这对于我们的算法而言其实没有必要,这一步只是为了更好的可视化。

我们为每组相邻的 X 和 Y 值调用 getNodeAtLocation 函数,并找到我们的 northId 、 eastId 、 southId 和 westId 。在此步骤中,我们不会对 X 和 Y 的值进行参数传递。

获取基本 ID 之后,再将它们转换为一个 adjacentIds 数组,这个数组只包含那些具有值的邻接数组。如此一来,如果我们有边角的话,就不用担心检查这些 ID 是不是为空。它还允许我们对数组进行循环,而无需在算法中手工记录每个基本 ID。

下面是另一个 2x2 网格的示例,这里我们使用了一组新的节点,并通过 addAdjacencies 来运行:

1[2{adjacentIds:[1,2],color:'red',id:0},3{adjacentIds:[3,0],color:'grey',id:1},4{adjacentIds:[3,0],color:'blue',id:2},5{adjacentIds:[1,2],color:'blue',id:3},6]

优化预处理过程

为了简化本文的算法,我添加了另一个优化过程。该算法将删除与当前节点颜色不匹配的相邻 ID。

重写 addAdjacencies 函数,如下:

1constaddAdjacencies=( 2nodes, 3)=>( 4nodes 5.map(({ 6colorId, 7id, 8x, 9y,10})=>({11adjacentIds:(12nodes13.filter(({14x:adjacentX,15y:adjacentY,16})=>(17adjacentX===x+118&&adjacentY===y19||(20adjacentX===x-121&&adjacentY===y22)23||(24adjacentX===x25&&adjacentY===y+126)27||(28adjacentX===x29&&adjacentY===y-130)31))32.filter(({33colorId:adjacentColorId,34})=>(35adjacentColorId36===colorId37))38.map(({39id,40})=>(41id42))43),44color:colors[colorId],45id,46}))47.filter(({48adjacentIds,49})=>(50adjacentIds51.length>052))53)

我在添加更多功能的同时简化了 addAdjacencies 。

通过删除颜色不匹配的节点,我们的算法可以 100% 确定 adjacentIds 属性中的任何 ID 都是邻接的节点。

最后,我删除了所有不具有相同颜色邻接的节点,这进一步简化了我们的算法。这样,我们就将节点缩减为只有我们关心的那些节点。

错误的方式:递归

TechLead 指出,我们无法递归地执行这个算法,因为我们会遇到堆栈溢出的问题。

虽然在一定程度上,他这么说是对的,但有几种方法可以缓解这个问题。我们可以使用迭代或者尾递归(tail recursion),但 JavaScript 不再将尾递归作为自带功能。

尽管我们仍然可以用 JavaScript 来写一个尾递归函数,但为使得算法更加简单,我仍然选择了创建一个典型的递归函数。

在编写代码之前,我们需要先找到算法。对于递归,使用深度优先搜索是合理的。“不要担心别人不明白计算机科学术语。”在我向一位同事展示我想出的不同解决方案时,他如此说道。

算法

我们将从一个节点开始,尽可能向下搜索,直到到达一个端点。然后我们将返回并采取下一个分支路径,直到我们扫描完整个连续块为止。在此过程中,我们还必须记录我们搜索过的部分,以及最大的连续块的长度。

我将函数分成了两部分。其中一个函数将保存最大列表和先前扫描的 ID,同时至少循环每个节点一次。另一个函数则将从未扫描的根节点开始,进行深度优先遍历。

代码如下所示:

1constgetContiguousIds=({ 2contiguousIds=[], 3node, 4nodes, 5})=>( 6node 7.adjacentIds 8.reduce( 9(10contiguousIds,11adjacentId,12)=>(13contiguousIds14.includes(adjacentId)15?contiguousIds16:(17getContiguousIds({18contiguousIds,19node:(20nodes21.find(({22id,23})=>(24id25===adjacentId26))27),28nodes,29})30)31),32(33contiguousIds34.concat(35node36.id37)38),39)40)41constgetLargestContiguousNodes=(42nodes,43)=>(44nodes45.reduce(46(47prevState,48node,49)=>{50if(51prevState52.scannedIds53.includes(node.id)54){55returnprevState56}575859constcontiguousIds=(60getContiguousIds({61node,62nodes,63})64)656667const{68largestContiguousIds,69scannedIds,70}=prevState717273return{74largestContiguousIds:(75contiguousIds.length76>largestContiguousIds.length77?contiguousIds78:largestContiguousIds79),80scannedIds:(81scannedIds82.concat(contiguousIds)83),84}85},86{87largestContiguousIds:[],88scannedIds:[],89},90)91.largestContiguousIds

下面,我们将逐步进行分析。

递归函数

getContiguousIds 是递归函数,在每个节点调用一次。在该函数每次返回结果时,我们都会得到一个连续节点的更新列表。

这个函数只有一个判断条件:节点是否已在列表中?如果没有,则再次调用getContiguousIds 。当该函数返回结果时,我们会获得一个更新的连续节点列表,该列表会被返回到 reducer ,并用作下一个 adjacentId 的状态。

每当我们用 concat 将当前节点连接到 contiguousIds 时,都要向 contiguousIds 传入值。每次进一步递归时,我们都要确保在循环执行 adjacentIds 之前,当前节点已经被添加到 contiguousIds 列表中。这可以确保我们不会无限地递归。

循环

该函数的后半部分也会遍历每个节点一次。递归函数使用 reducer来检查代码是否已被扫描。若已被扫描,就继续循环,直到找到一个没有循环的节点,或者直到退出循环为止。

如果我们的节点尚未被扫描,则调用 getContiguousIds,并继续遍历,直到扫描完成。这是同步的,但可能需要一些时间。

每当函数返回一个 contignousIds 列表,都对照 largestContiguousIds 进行检查,如果该列表的返回值更大的话,就存储返回值。

同时,我们将把这些 contiguousIds 添加到我们的 scannedIds 列表中,以标记我们搜索的节点。

执行

就算我们有 10000 个项目,这个算法也不会遇到 3 种随机颜色的堆栈溢出问题。如果我把所有的都改成单一颜色,就可能会遇到堆栈溢出的问题,这是因为我们的递归函数经历了 10000 次的递归。

顺序迭代

由于内存比函数调用的堆栈要大,所以我的下一个想法是在一个循环中完成整个事情。我们将跟踪节点列表的列表。我们将不断添加它们,并将它们链接在一起,直到退出循环。

这个方法要求在完成循环之前,将所有可能的节点列表保存在内存中。在递归示例中,我们只将最大的列表保存在内存中。

1constgetLargestContiguousNodes=( 2nodes, 3)=>( 4nodes 5.reduce( 6( 7contiguousIdsList, 8{ 9adjacentIds,10id,11},12)=>{13constlinkedContiguousIds=(14contiguousIdsList15.reduce(16(17linkedContiguousIds,18contiguousIds,19)=>(20contiguousIds21.has(id)22?(23linkedContiguousIds24.add(contiguousIds)25)26:linkedContiguousIds27),28newSet(),29)30)313233return(34linkedContiguousIds35.size>036?(37contiguousIdsList38.filter((39contiguousIds,40)=>(41!(42linkedContiguousIds43.has(contiguousIds)44)45))46.concat(47Array48.from(linkedContiguousIds)49.reduce(50(51linkedContiguousIds,52contiguousIds,53)=>(54newSet([55...linkedContiguousIds,56...contiguousIds,57])58),59newSet(adjacentIds),60)61)62)63:(64contiguousIdsList65.concat(66newSet([67...adjacentIds,68id,69])70)71)72)73},74[newSet()],75)76.reduce((77largestContiguousIds=[],78contiguousIds,79)=>(80contiguousIds.size81>largestContiguousIds.size82?contiguousIds83:largestContiguousIds84))85)

另一个想法是,从顶部开始遍历,并将每个节点循环一次。到在此过程总,我们必须检查 ID 是否存在于节点列表的列表 contiguousIdsList 中。

如果它不存在于任何 contiguousIds 列表中,我们就将添加该列表和 adjacenIds 。这样,在循环时,就会有其他的内容链接到它。

如果我们的节点在其中一个列表之中,那么节点就可能也存在于其中相当多的列表中。我们想要把所有这些都链接在一起,并从 contiguousIdsList 中删除未链接的那些节点。在我们得到节点列表的列表之后,检查哪个列表是最大的,这个算法就完成了。

执行

与递归版本不同的是,当所有 10000 个项目都是相同的颜色时,这个算法能够完成任务。但该算法的一个缺陷是,它执行得相当慢。在上述代码的性能评估中,我没有考虑到循环列表的列表的情况,这显然对性能有很大的影响。

随机迭代

我想采用递归方法背后的思路,并以迭代方式进行应用。这一算法的目标是精确命中每个节点一次,并且只存储最大的连续块:

1constgetLargestContiguousNodes=( 2nodes, 3)=>{ 4letcontiguousIds=[] 5letlargestContiguousIds=[] 6letqueuedIds=[] 7letremainingNodesIndex=0 8 910letremainingNodes=(11nodes12.slice()13)141516while(remainingNodesIndex< remainingNodes.length) {17    const [node] = (18      remainingNodes19      .splice(20        remainingNodesIndex,21        1,22      )23    )242526    const {27      adjacentIds,28      id,29    } = node303132    contiguousIds33    .push(id)343536    if (37      adjacentIds38      .length >039){40queuedIds41.push(...adjacentIds)42}434445if(46queuedIds47.length>048){49do{50constqueuedId=(51queuedIds52.shift()53)545556remainingNodesIndex=(57remainingNodes58.findIndex(({59id,60})=>(61id===queuedId62))63)64}65while(66queuedIds.length>067&&remainingNodesIndex===-168)69}7071if(72queuedIds.length===073&&remainingNodesIndex===-174){75if(76contiguousIds.length77>largestContiguousIds.length78){79largestContiguousIds=contiguousIds80}8182contiguousIds=[]83remainingNodesIndex=08485if(86remainingNodes87.length===088){89break90}91}92}9394returnlargestContiguousIds95}9697module.exports=getLargestContiguousNode

这里,我们没有将节点添加到先前扫描的 ID 列表,而是从 remainingNodes 数组中拼接出值来,但是我不建议大家这样做。

分解

我把上述代码分成 3 个部分,用 if 语句分开。

让我们从中间部分开始。首先查看 queuedIds 。如果该对象有值,就对队列中的内容进行循环,看看它们是否存在于 remainingNodes 中。

第三部分的内容取决于第二部分的结果。如果 queuedIds 对象为空,并且 remainingNodesIndex 是 -1 的话,那么我们就已经完成了这个节点列表,并需要从一个新的根节点开始。新的根节点始终位于索引 0 处,因为我们正在对 remaininigNodes 进行拼接。

现在再来看循环的顶部。我可以使用 while (true) ,但是需要留一个跳出条件,以防止出错。这在调试时很有用,因为要弄清楚无限循环可能是件痛苦的事情。

之后,我们将拼接节点。我们将节点添加到 contiguousIds 列表中,并将 adjacentIds 添加到队列中。

执行

这一算法几乎和递归版本一样快。当所有节点都是相同颜色时,它是所有算法中速度最快的。

针对数据的优化

对相似的颜色进行分组

由于我们只知道有两种蓝色,所以我们可以将类似颜色的节点分组在一起,用于顺序迭代版本。

通过将节点拆分成 3 个更小的数组,我们可以减少内存占用,以及需要在列表的列表中执行的循环次数。尽管如此,这并不能解决所有颜色都相同的情况下会出现的问题,因此我们并不会使用此方法修改递归版本。这也意味着我们可以对操作进行多线程处理,将执行时间缩短近三分之一。

如果我们按顺序执行这些命令,只需先运行三个中最大的一个。如果最大值比另外两个值大,就无需检查它们。

可能存在的最大数据集的大小

我们可以检查每一次迭代,而不是在特定时间间隔检查是否有最大的列表。如果最大节点集合的规模大于或等于可用节点的一半(5000 或更高),那么,很显然我们已经有了最大的列表。

若使用随机迭代版本的话,我们可以找到迄今为止最大的列表大小,并查看剩余的节点数量,如果没有比最大的节点集合大小还小的数值,那么就可以说明,我们已经有最大的列表了。

使用递归

虽然递归有其局限性,但我们仍可以使用它。我们需要做的事情就是检查剩余节点的数量。如果它没有超出堆栈的限制,我们就可以使用更快的递归版本。这么做的风险是很大,但随着循环的深入,这一方法会缩短执行时间。

使用 for 循环

在知道节点最大数量的情况下,我们可以使用 for 循环编写 reduce 函数。无论何时,与 for 循环相比, Aray.prototype 方法都非常慢。

使用尾递归

我没有在本文中讨论相关算法,因为我认为尾递归需要一篇单独的文章来阐述。这是一个很大的主题,很多地方都需要解释。另外,虽然它使用了递归结构,但它可能并不会想你所期望的那样比while循环还快。

RxJS:可维护性与性能

有一些方法可以重写这些函数,这样你就可以更轻松地理解并维护它们。我想出的主要解决方案是使用 Redux-Observable 风格的 RxJS,但并不使用 Redux。

接下来,我想以常规的方式来编写代码,然后使用 RxJS 流式传输数据,看看能将算法性能提升多少。

我使用 RxJS 做了 3 个版本的算法,并做了一些修改来加快执行速度。与我之前的文章不同的是,即使增加了行和列,所有的三个版本都会变慢。

我本来可以做很多优化,但要以代码的可读性为代价,这不是我想要的。

最终,我终于找到了一个可行的解决方案,该方案目前是最快的,只需一半的执行时间。这已经是总体上最好的改进了。

只有当每个节点都是相同的颜色时,我才能用可观察到的数据击败内存占用较多的顺序迭代。从技术上来讲,这一算法也优于递归方法,因为在这种情况下,递归算法会出现堆栈溢出的问题。

在研究如何使用 RxJS 流数据之后,我意识到该方法对本文来说实在过于复杂了。希望以后会有文章详细介绍这些代码示例。

如果希望查看详细代码,可以查看如下 GitHub 项目地址:

https://github.com/Sawtaytoes/JavaScript-Performance-Interview-Question

最终统计数据

一般来说,最大的连续块平均有 30~80 个节点。

下面展示了相关算法的评估数据:

随机颜色

执行时间 方法
229.481ms 递归
272.303ms 迭代随机
323.011ms 迭代序列
391.582ms Redux-Observable 并发
686.198ms Redux-Observable 随机
807.839ms Redux-Observable 顺序

一种颜色

执行时间 方法
1061.028ms 迭代随机
1462.143ms Redux-Observable 随机
1840.668ms Redux-Observable 顺序
2541.227ms 迭代序列

无论我进行了多少次测试,每种方法的相对排名位置都保持不变。

当所有节点颜色都相同时,Redux-Observable 并发方法受到了影响,我试过很多方法尝试提高这个方法的运行速度,但是没有成功。

游戏制作

在我的职业程序员生涯中,我曾两次遇到过这段代码。其中一次是我在开发独立游戏《Pulsen》时使用 Lua 编写的代码,代码长度要小得多。

还有一次是在我绘制一张世界地图的时候,该地区有一个预定义的节点列表,我对其进行了实时处理。这使得使用者可以通过键盘上的方向键来移动世界地图。

我还为具有 X 和 Y 值的未知项列表编写了一个节点生成器。听起来是不是很熟悉?我同样需要使网格位居屏幕中央。不过,要做到这点,在 HTML 中比在游戏引擎中要更容易实现。尽管如此,将一堆绝对定位的 div 放在中央位置也并不容易。

在这个案例中,实时执行时间并不怎么很重要,因为我在加载游戏时就进行了大量的预处理。

我想强调的是,TechLead 的问题可能是你会在职业生涯中遇到的问题,但在典型的 JavaScript 应用程序中,往往不太需要考虑程序的速度。

TechLead 在 Google 使用的是 Java ,我猜他面试的职位都很关心执行速度。他们有可能有一堆工作任务要处理大量的数据,因此像这样的解决方案可能是必要的。

但是,这个视频也有可能是关于 HTML 和 CSS 的职位的,谁知道呢!

结语

正如你在最终统计数据中所看到的那样,读起来最槽糕的代码几乎是最快的,并且还完成了我们所有的要求。

据我自己的经验,我花了更长的时间来开发非 RxJS 版本的代码。我认为,这是因为更快的版本需要全面的思考。Redux-Observable 能够让你以化整为零的方式进行思考。

这是一道非常有趣的问题。它起初看起来似乎很难,但是将它分解成几块之后,问题就迎刃而解了。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Google
    +关注

    关注

    5

    文章

    1754

    浏览量

    57365
  • 数据处理
    +关注

    关注

    0

    文章

    571

    浏览量

    28505
  • 数据模型
    +关注

    关注

    0

    文章

    48

    浏览量

    9995

原文标题:赌5毛钱,你解不出这道Google面试题

文章出处:【微信号:rgznai100,微信公众号:rgznai100】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Google公司的的面试题,你想试试吗?

    Google公司的的面试题,你想试试吗? 求高人解答:Google 的疯狂面试题能答出几道? 下面G
    发表于 07-22 12:13

    java基础练习、面试题

    java基础练习、面试题整理了java私塾教材的课后作业,基础部分,面试中也常常遇到的基础问题,赶紧下载了。下载: [hide][/hide]
    发表于 07-16 14:02

    这样的面试题敢回答吗?

    本帖最后由 海同iotek 于 2014-12-11 14:27 编辑 这样的面试题敢回答吗?海同网校面试时,有个面试题大致是这样的:
    发表于 11-27 17:16

    java经典面试题深度解析

    免费视频教程:java经典面试题深度解析对于很多初学者来说,学好java在后期面试的阶段都没什么经验,为了让大家更好的了解面试相关知识,今天在这里给大家分享了一个java经典面试题深度
    发表于 06-20 15:16

    c语言面试题,c++面试题下载

    c语言面试题,c++面试题1. static有什么用途?(请至少说明两种) 1) 限制变量的作用域 2) 设置变量的存储域 2. 引用与指针有什么区别?  1) 引用必须被初
    发表于 10-22 11:19 5次下载

    c语言面试题

    c语言面试题集(单片机)C language problem(20151125084232)
    发表于 12-18 14:05 9次下载

    c语言面试题

    c语言面试题
    发表于 11-05 16:48 0次下载

    C语言经典面试题

    面试题
    发表于 12-20 22:41 0次下载

    C语言经典面试题

    C语言 经典面试题
    发表于 01-05 11:27 0次下载

    经典硬件面试题精选及解答

    经典硬件面试题精选及解答
    发表于 11-29 18:02 0次下载

    Java的经典面试题和答案详细说明

    发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~ 博主已将以下这些面试题整理成了一个Java面试手册,题型非常全面附带答
    发表于 09-07 08:00 0次下载
    Java的经典<b class='flag-5'>面试题</b>和答案详细说明

    常见的MySQL高频面试题

    在各类技术岗位面试中,似乎 MySQL 相关问题经常被问到。无论面试开发岗位或运维岗位,总会问几道数据库问题。经常有小伙伴私信我,询问如何应对 MySQL 面试题。其实很多
    的头像 发表于 02-08 16:05 2347次阅读

    操作系统的四十多道题面试题

    说,是因为我系统查过,如果有不相信的大佬,欢迎狠狠的打我脸。 这份面试题有四十多道题,涉及操作系统简介篇、进程和线程篇、内存管理篇、文件系统篇、IO 篇、死锁篇。囊括了校招面试和社招面试,看完这一篇文章,保准
    的头像 发表于 03-10 10:17 3171次阅读
    操作系统的四十多道题<b class='flag-5'>面试题</b>

    关于数组常见的面试题

    数组是最基本的数据结构,关于数组的面试题也屡见不鲜,本文罗列了一些常见的面试题,仅供参考。目前有以下18道题目。
    的头像 发表于 08-17 09:25 1605次阅读

    【C语言经典面试题】sizeof与strlen有什么区别?

    这道经典的面试题,我来跟你一起聊一聊。
    的头像 发表于 10-05 16:30 2111次阅读
    【C语言经典<b class='flag-5'>面试题</b>】sizeof与strlen有什么区别?