尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

百度2016研发笔试题解析:数组指针、Linux进程与Java集合考点

百度2016研发笔试题解析:数组指针、Linux进程与Java集合考点 先说个现象每年校招季总有人把几年前的名企真题翻出来反复刷。我手里这份《百度2016研发工程师笔试题三》就是其中之一。别看它年头不短里面涉及的数组与指针、算法复杂度分析、Linux进程模型、Java集合原理到现在依然是笔试的高频考点。很多同学问我这么老的题还有必要做吗我的回答是不仅要刷还要按“面试官到底想听什么”的标准来刷。这份卷子在当时属于百度校招研发岗的第三场笔试整体风格以C/C为主穿插Java、操作系统和网络基础编程题占了不小的比重。它的价值在于2016年的题目比现在许多“八股文”式笔试题更贴近底层原理几乎每道题都能延伸到实际工程中的坑。比如指针题后面能扯出内存管理Linux题后面能扯出线上故障排查。所以这篇博文我不打算照搬原题罗列答案而是挑出几类最有代表性的题讲清楚考查意图、解题思路再补充答题时容易忽略的细节和复盘心得希望能帮到正在准备校招或社招的同学。1. 这套卷子到底在考什么2016年研发岗笔试的出题逻辑拿到一份笔试卷子别急着上手做题先花五分钟把题型分布和考察重心摸一遍。这样既能合理分配时间也能在心理上建立“这题在考什么”的预判。2016年百度的研发工程师笔试题三整体结构和现在很多大厂的在线笔试差别不大但少了一些偏门的选择题多了几道需要动手写代码的题目。从考察维度上看一份研发笔试通常不会只考一门语言而是围绕“计算机基础 编码能力 问题分析能力”三条线展开。这套卷子也是这么设计的语言基础以C/C为主重点考察指针、内存布局、字符串处理同时也加入了一些Java集合和并发的题目用于筛选不同技术栈的候选人。数据结构与算法数组、链表、二叉树、排序、动态规划都有涉及部分题要求手写完整代码并分析复杂度。操作系统与网络进程线程区别、僵尸进程、死锁条件、TCP握手等都属于常见送分题但拿满分的人其实不多。系统设计与场景题围绕具体业务场景展开考察边界条件处理和工程思维这类题没有标准答案但能给面试官一个评判逻辑能力的参考。为什么2016年的题到今天还值得做因为这套题里的很多考点恰恰是现在很多“刷题式”候选人最薄弱的地方。比如数组和指针的关系很多同学在LeetCode上用C写题没问题但一问他sizeof(arr)和sizeof(arr[0])为什么不相等就说不清楚。再比如Linux的僵尸进程平时开发可能很少直接碰到但一旦线上服务器出现大量defunct进程如果你连排查思路都没有就很容易被面试官判定为“只懂业务不懂系统”。所以做这份卷子的时候我建议大家不要只满足于“把题做对”而是顺着题目往下问自己“如果面试官在此基础上再追问三个问题我能不能接住”这套卷子本身的内容只是引子背后的知识网络才是你真正要梳理的东西。2. 高频考点逐个拆语言基础与算法题到底怎么答2.1 数组与指针永远的送分题与送命题C/C笔试里数组和指针几乎是必考题。这套卷子里有一道很经典的题目是这样的char str[] hello; char *p str; printf(%lu %lu\n, sizeof(str), sizeof(p));如果你答的是“6和6”那就要小心了。正确答案是6和8在64位系统上指针大小为8字节。str是数组名sizeof(str)计算的是整个数组占用的字节数字符串hello加上末尾的\0一共6个字节而p是指针变量sizeof(p)求的是指针本身的大小与它指向的内容无关64位平台上就是8字节。这道题背后隐藏着一个核心概念数组名在大多数表达式中会退化为指向首元素的指针但在sizeof和取地址运算符中不会退化。这个“退化”规则就是面试官想考察的知识点。再延伸一步卷子里还有一道类似的题int a[5] {1, 2, 3, 4, 5}; int *ptr (int *)(a 1); printf(%d %d\n, *(a 1), *(ptr - 1));答案是多少*(a1)是2这个好理解数组名退化为首元素地址加1指向第二个元素。a取的是整个数组的地址类型是int(*)[5]加1跳过了整个数组指向数组末尾之后的位置。(int *)强转后ptr - 1回退一个int大小所以*(ptr-1)是5。这类题考察的是指针运算的“步长”概念。很多人在笔试中丢分不是因为不知道指针是什么而是忽略了指针类型决定步长这一关键点。int *加1跳4个字节char *加1跳1个字节int (*)[5]加1跳20个字节全都由类型来定。我在实际写代码时对这类问题的建议是不要在工程代码里写这种“看似能跑但可读性极差”的指针运算。笔试里考察它只是为了确认你有没有真正理解C/C的内存模型而不是鼓励你在项目里炫技。2.2 算法题百度之星“left and right”背后的经典模型这套卷子的算法部分有一道题和当年百度之星编程赛里的“left and right”模型非常像。题目大意是给定一个长度为n的整数数组nums要求输出一个新的数组answer其中answer[i]是原数组中除nums[i]以外所有元素的乘积。不能使用除法时间复杂度要求O(n)。这道题是LeetCode 238的原型放在2016年算是比较有区分度的算法题。第一次看到“不能使用除法”这个条件很多人会愣一下因为最容易想到的思路就是先算全部乘积再逐一除以每个元素。但这个做法有几个问题一是如果数组里有0除以0直接崩溃二是如果题目明确要求不能用除法就要立刻切换到“前缀积 后缀积”的思路。解题思路分两步先从左往右遍历用一个变量left记录当前元素左边所有元素的乘积。每次乘完把结果放进answer[i]再更新left * nums[i]。再从右往左遍历用一个变量right记录当前元素右边所有元素的乘积。把answer[i] * right再更新right * nums[i]。代码实现也很简洁int* productExceptSelf(int* nums, int numsSize, int* returnSize) { int* answer (int*)malloc(numsSize * sizeof(int)); int left 1; for (int i 0; i numsSize; i) { answer[i] left; left * nums[i]; } int right 1; for (int i numsSize - 1; i 0; i--) { answer[i] * right; right * nums[i]; } *returnSize numsSize; return answer; }这个解法的时间复杂度是O(n)空间复杂度是O(1)除了返回数组本身也是面试官最想看到的答案。如果面试官追问“能不能减少遍历次数”你可以回答已经在两次遍历内完成了理论上无法低于O(n)因为每个位置的值至少依赖左右两侧的所有元素。当年我做这道题时第一次提交的错误点在于answer[0]初始值应该是1而不是nums[0]。因为answer[i]代表“左侧所有元素的乘积”第一个元素左侧没有元素所以应该是1。这种边界条件恰恰是笔试中最容易丢分的地方。2.3 手写快速排序别只会背模板复杂度分析要过关算法题里有一道“手写快速排序”的题目看起来基础但能写得又快又对的人其实不多。这道题我会专门拿出来讲是因为它考察的不只是“能不能背出模板”而是你到底理解不理解分治思想。快速排序的核心是分治选一个基准值pivot把数组分成小于等于基准值的左半部分和大于基准值的右半部分然后递归排序左右两半。实现上有“Hoare版本”和“Lomuto版本”之分笔试里写哪个都可以重要的是别写错边界。这里给一份我常用的Lomuto分区写法void quickSort(int* arr, int low, int high) { if (low high) return; int pivot arr[high]; int i low - 1; for (int j low; j high; j) { if (arr[j] pivot) { i; int tmp arr[i]; arr[i] arr[j]; arr[j] tmp; } } int tmp arr[i 1]; arr[i 1] arr[high]; arr[high] tmp; int pi i 1; quickSort(arr, low, pi - 1); quickSort(arr, pi 1, high); }代码的关键在于分区函数里i和j的关系i指向最后一个小于等于基准值的元素j用于遍历。循环结束后把基准值放到i1的位置这样基准值左右两侧就满足条件了。如果只是在卷子上写出这段代码可能只能拿一半分。剩下的一半在复杂度分析。平均时间复杂度O(n log n)最坏情况O(n²)——当数组已经是升序或降序且每次选最后一个元素作为基准时分区严重不平衡递归深度退化为n。要避免最坏情况可以用“三数取中”法选基准或者随机选一个下标交换到末尾再分区。在笔试答案里我会额外加一句快速排序是不稳定排序因为在分区过程中相同的元素可能被交换到彼此的另一侧。面试官看到这句话就知道你对排序算法的理解不是背出来的。2.4 链表与二叉树这些题看似简单实际上手全是坑除了数组和排序这套卷子还考了链表和二叉树。比如有一道题是“判断链表中是否有环”这属于经典快慢指针题slow每次走一步fast每次走两步如果有环两者必然相遇。代码不难但边界条件需要仔细处理空链表返回false只有一个节点返回falsefast和fast-next都要判断非空否则会空指针崩溃。二叉树部分有一道“层序遍历二叉树”的题目要求输出每层节点。这里面就涉及队列的运用先根节点入队然后每次取出队首节点把它的左右子节点入队循环直到队列为空。有一个容易犯的错是没有提前记录当前层的节点数size导致无法区分“这一层”和“下一层”。正确做法是在遍历每层之前先取int size queue.size()然后只处理size个节点。我当时做这种题的习惯是先在草稿纸上画一个三层的小树手动模拟一遍层序遍历的整个过程再动手写代码。千万别觉得画图浪费时间二叉树和链表的指针操作画图能帮你避免至少一半的边界错误。3. Linux与操作系统题题目看着常规答案要写出深度3.1 僵尸进程这个概念你背过但你真的处理过吗这套卷子里有一道Linux题“什么是僵尸进程如何产生如何处理”这是典型的送分题但大部分人的答案都在背概念没有写到让面试官眼前一亮的程度。僵尸进程是指子进程先于父进程退出父进程没有调用wait或waitpid回收子进程的退出状态子进程的进程描述符仍然保留在内核中此时用ps命令查看会看到状态是Z或defunct。处理方式有三个层面父进程调用wait或waitpid主动回收子进程状态。子进程退出时给父进程发送SIGCHLD信号父进程通过信号处理函数调用waitpid统一回收。对于“父进程先死、子进程变成孤儿”的情况子进程会被init进程收养由init负责回收。但如果你写的是一个长期运行的服务父进程自己不回收子进程就会积累大量僵尸最终可能导致进程号耗尽无法创建新进程。我当时在答案里还补了一个实际排查的步骤先用ps -ef | grep defunct查看僵尸进程的数量再用ps -o ppid -p 僵尸进程PID找到它的父进程最后定位是哪个服务没有调用wait。这一段补充很加分因为它把概念题变成了场景题。3.2 进程与线程的区别别只答“进程有独立地址空间线程共享”另一道常见的题目是“进程和线程的区别”。基础答案是进程是资源分配的基本单位线程是CPU调度的基本单位进程有独立的地址空间线程共享进程的地址空间进程间通信复杂管道、消息队列、共享内存等线程间通信简单直接读写共享变量进程切换开销大线程切换开销小。但如果你想拿高分得再往下想一层在Linux里线程本质上也是一种进程只是共享了地址空间和其他资源。所以fork创建进程和pthread_create创建线程底层都是复制或共享任务结构体task_struct区别在于是否共享内存空间和文件描述符表。再补充一个实际场景如果一个php-fpm或nginx worker进程崩溃了因为它自己就是独立进程操作系统会回收它的资源所以不会拖垮整个服务但如果是多线程程序里的一个线程因为野指针崩溃整个进程都会退出其他线程也跟着遭殃。这就是为什么很多服务端程序宁愿用多进程模型也不愿意用多线程模型——隔离性和稳定性优先。这道题答到这个深度基本就不用担心面试官再追问了。3.3 TCP握手和TIME_WAIT网络基础题也要往工程上靠网络部分这套卷子考了TCP三次握手和TIME_WAIT。三次握手基本人人会背但TIME_WAIT的细节容易模糊。TIME_WAIT出现在主动关闭连接的一方状态持续2MSL最大报文段生存时间约60秒。为什么要等2MSL两个原因一是确保最后一个ACK能够到达对端如果丢失对端会重发FIN这边可以重新发送ACK二是让本连接产生的所有报文段在网络中消失避免影响后续使用相同端口的连接。面试官如果接着问“线上服务器TIME_WAIT过多怎么办”你需要知道的排查思路是调整net.ipv4.tcp_tw_reuse和net.ipv4.tcp_timestamps参数在发起连接时复用TIME_WAIT状态的连接同时检查代码里有没有频繁短连接的情况尽量改成连接池复用。注意tcp_tw_recycle这个参数在NAT环境下容易出问题现在内核默认不建议开启。这些细节不只是在笔试中有用线上排查问题的时候你会感谢自己当年多看了两眼答案背后的原理。4. Java语言基础题从HashMap到String细节决定offer4.1 HashMap的底层结构与put流程这套卷子里Java题目比重不算特别大但很有代表性。最经典的一道是“说说HashMap的底层结构以及put操作的流程。”答案框架如下HashMap底层是“数组 链表 红黑树”的结构。JDK8中当链表长度超过8且数组长度大于等于64时链表会转换成红黑树。put流程先对key计算hash值再用(n - 1) hash计算在数组中的下标如果当前位置为空直接插入如果不为空遍历链表或红黑树找到相同key就覆盖value否则在链表末尾追加。当元素数量超过阈值容量 × 负载因子默认0.75时触发扩容。扩容后元素需要重新计算下标这也是HashMap性能开销比较大的地方。能答出这些说明你有基础。但我会在回答里再加一句HashMap非线程安全多线程put可能导致数据覆盖JDK7中扩容时还可能形成环形链表导致get死循环JDK8中通过引入尾插法和红黑树缓解了这个问题但并发环境下仍推荐使用ConcurrentHashMap。这最后一句往往就是面试官对你“加分”和“一般”的分水岭。4.2 equals与hashCode为什么重写equals必须重写hashCode还有一道Java题“为什么重写equals时必须重写hashCode”这里的关键在于HashMap、HashSet等集合类的存储逻辑先根据hashCode定位桶再用equals判断桶内是否有相同元素。如果两个对象equals相等但hashCode不相等它们在HashMap中可能被分到不同的桶里导致明明“相等”却无法找到。用一个生活化的类比来说hashCode相当于图书馆的楼层号equals相当于楼层内的座位号。两个人虽然座位号相同equals相等但如果你把其中一个人的楼层号写错了hashCode不一致图书馆管理员就找不到他。这个类比在进行笔试答题时可以让你的答案更生动也更容易让阅卷人理解你确实掌握了原理。4.3 String不可变与字符串拼接的性能问题这套卷子里还有一道关于String不可变性的题。为什么Java的String设计成不可变主要原因有三个一是字符串常量池的缓存需要不可变性否则多个变量引用同一个字符串时会被意外修改二是安全性String常被用作类名、文件路径、网络地址等参数不可变可以避免恶意修改三是线程安全不可变对象天然适合并发访问。笔试中常考的延伸点是“用拼接10000次字符串和用StringBuilder拼接有什么区别”String每次拼接都会创建新的String对象循环10000次就会创建10000个中间对象浪费内存又拖慢速度。正确做法是用StringBuilder的append方法。这个问题在真实的Java开发中很常见特别是写日志、拼SQL语句的时候。5. 做题顺序与时间分配90分钟怎么拿高分一套笔试题发下来最忌讳的是从头做到尾遇到难题卡壳二十分钟后面的简单题反而没时间写。我当时总结了一套做题策略分享给大家参考第一步花3到5分钟快速浏览全部题目。把题分成三类一眼就会的、“好像会但需要想一想的”、完全没有思路的。用铅笔在题目上做个标记。第二步优先做“一眼就会”的部分。这类题是基本盘先把能拿的分拿稳。尤其是选择题、判断题、概念简答题不要犹豫快速做完。按照一场90分钟的笔试来算这部分建议控制在20分钟内。第三步集中攻克“好像会但需要想一想”的编程题和设计题。这类题是区分度最高的部分值得花40到50分钟。先写解题思路再写代码写完务必检查边界条件。这样即使代码没完全跑通阅卷人也能看到你的分析过程会酌情给分。第四步最后留10分钟检查。重点检查这种内容数组下标有没有越界、指针有没有判空、递归有没有退出条件、HashMap有没有考虑并发。这些“低级错误”一旦出现往往会让阅卷人怀疑你的代码基本功。这中间有一个细节很多在线笔试支持编译器但也有的笔试系统只有文本编辑器。如果你不确定系统是否支持编译检查就在答题一开始先写一小段hello world的代码提交一下看看系统反馈。这能帮你判断后续写代码的时候要不要更加仔细。6. 丢分重灾区与复盘心得这几种错我见过太多次刷完这套题我再整理几个“丢分重灾区”。这些坑我自己踩过也看身边同学踩过无数次。第一类算法题只写代码不写思路。有些同学看到算法题上手就写代码也基本正确但阅卷人只能在代码里猜你的想法。凡是复杂度分析、边界条件、解题思路这类内容都应该用注释或在代码前后写清楚。哪怕代码有小bug清晰的思路也能帮你挽回不少分。第二类数组和指针没搞清就手写内存操作。真题里有一道C语言题要求实现字符串拷贝我见过不少版本调用了strcpy但压根没想到要处理src和dst重叠的情况。工程里用memmove能处理重叠而memcpy不行。这种细节答案简单但背后是对内存操作的理解深度。第三类操作系统题只答概念不答处理方式。比如“什么是死锁”很多人只写了死锁的四个必要条件却忘了写“如何避免和解除”。题目没明确问也要主动写因为面试官想看到的不是背诵而是解决实际问题的能力。第四类时间分配严重失衡。前面的一道算法题卡了四十分钟后面的Linux题和网络题只能草草了事。我做题时给自己定了一个硬规矩一道题如果十五分钟还没有任何进展就先跳过等做完其他题再回来。笔试不是打擂台不需要“死磕”。第五类代码写完不检查。哪怕只剩三分钟也要把写过的代码逐行读一遍看看有没有少分号、漏括号、变量名拼写错误。有时候一次简单的检查就能挽回一道题的全部得分。回到开头那句话2016年的百度笔试题技术上到现在并不算“新潮”但它考察的知识点足够硬核。数组与指针、排序、HashMap、Linux进程模型这些内容在大厂面试中一次次出现说明基础永远不会过时。刷这套题的时候记得把每一道题当成一个知识入口顺着它梳理出完整的知识网络。这样哪怕题目本身改变你也能从从容容地应对。最后再分享一个小技巧每次刷完一套真题不要急着做下一套花半小时写一个“错题复盘文档”把每道错题的错误原因和正确解法记下来。到了正式笔试前只需要翻这个文档避免在同一个地方反复跌倒。这套方法陪我从校招走到社招实测非常稳。
返回列表