今天去百度大厦面试,职位是测试开发工程师(暑期实习),面试官是一位漂亮、友善的JJ
第一道很简单,问程序在内存有几个数据区,堆和栈区有啥区别
第二道是两个有序数组(可能升序或降序),要求合并到一个大数组中,使其仍然有序,讲了大体思路,并用C简单写了实现代码
第三道是给出int型的x、 y两个数,要求不借助第三个变量交换x和y的值,
啊。从大一开始学C,碰到过用value传参、引用传参、指针传参、宏定义交换x和y,但都要用到辅助变量,这次遇到JJ给的不借助第三变量交换x 和y,还真是第一次,My god。。。 第一反应,卡壳了,不过也到觉得很新鲜,特别好奇
JJ似乎也看出了我不会,于是说:你先想一会吧,在纸上写写,尽量写出算法代码。 哎 我没遇到过,不过第七感马上告诉我:这算法肯定不难,但很有技巧,可能是脑筋急转弯,想出来了就会豁然开朗,可惜此刻只缘我身在庐山中,不会呀
于是乎,我就抛开了指针、引用之类的,想用异或、求反、求交等逻辑运算,还画出了00001,000010之类的来比较,可怜一时半会也倒弄不出 来,JJ提示我想想有数学运算试试,于是我把考研那会的三角运算的加减乘除都想了个遍,也凑合了一个(x+y)和(x-y)的四则运算,可怜想不出下一步 该如何做,哎,再加上突然遇到这个简单的不能再简单的问题却搞不定,再百度处女面心脏也跳得厉害,急得我坐在中央空调的大厦里,就像掉进了热锅里的蚂蚁 ——急得流汗了,这天好热啊,哈哈,JJ看出来了,于是就跳过了
第四题有1.txt、2.txt。。。。n.txt的n个文件,要求查出含有字符串“love”的文件,并返回文件名。 我初看太容易了,直接用shell命令 find + grep搞定,可怜JJ要求我用高级语言写伪代码,我于是小声的问了JJ可以用库函数么,她说行. OK 直接str.substring()或者split()在strcmp()搞定. 结果JJ似乎不太满意,似乎感觉这样做效率太低了,哎, 我也想直接一个char的去比较,但感觉太麻烦,效率哟
第五道题就是让我测试一台自动售货机,我想百度应该只测试程序,于是我就没说测试按键、电源、硬币纸币等物理特性,直接跳到了测试程序逻辑部分,要 用等价划分法、边界值分析法以及经验评估法,并讲了塞钱又强行拉出、正常购物、购完物继续再塞钱三个区间,再在正常购物两侧的区间进行边界值测试以及凭经 验测试经常容易出错的地方,JJ看了看我,没说话,继续。。
第六道题以后就是一些概念题了,如死锁的四个条件、进程通信的方式(我只想到了管道、消息、全局变量、信号量四个)、空机测试(JJ似乎把宕 dang机读成了空kong机,不过当时我没反应过来,我管理实验室两台服务器也没遇到过宕机。可怜我当时不知道她空机的意思可能就是宕机,哎 反应的确不够敏捷,于是JJ问我懂不懂,我说没遇到过空机,于是JJ也就没再问了)
我记得就这些题,JJ说话很和气,待人也很好,最后JJ问我有什么问题,x和y交换,我的心结呀心结。 于是我第一个问题就是问JJ如何不用变量交换x和y的值,JJ笑而不语,留给我当“家庭作业”,让我回家自己查查 哎,现在可是恍然大悟啊,我的思路用异或和数学加减运算,现在网上看了,豁然开朗啊,就简单的三句,汗 (当天下午面试后回实验室,我就立即用四种方法补做完家庭作业百度面试及总结2 ,但不知道还有没有去百度补交作业的机会啦)。JJ问我还有什么问题,我对身后的那个闪闪发光的太空舱,很感兴趣,于是问JJ那太空舱是干什么的,JJ说 是休息的,赞啊 百度睡觉的地儿,居然那么的梦幻、可爱——发光的太空舱,哈哈 ^_^ ,JJ看着我好奇的目光,又笑啦 :可能笑我好幼稚的孩子呀 就这样,百度初面OVER la ;我看看手机时间,从我13:25提前五分钟预约面试官(原定13:30面试),到14:14面试结束后签到离开,一共仅用了49分钟,其中还包括上楼、 下楼、走路、寒暄等时间,看看周围的GGJJ还在面试ing,JJ做事很干练啊,赞
huhu,总体感觉面试题都不是很难,只是当时考虑、描述不是很清楚,x和y那道easyest题,思维一下子就短路啦,把我的自信打击得荡然无 存。
罢啦罢啦,继续等吧,不管结果如何,能够去百度面试,遇到这么友善、干练、聪明JJ,这次百度之行,也算值啦 哈哈
众里寻他千百度
百度面试,深度总结,不找理由,但求努力,为了梦想,继续奋斗!
今天上完课回实验室后,把昨天面试的三道算法题,完完整整的全部自己重做实现了一遍(多种方法)
以下代码原则上,力争全部都是用C语言实现,但考虑到扩展性和兼容性,算法1使用了模板
测试环境:VS2008
测试结果: 测试通过
view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
1. /**** BaiduInterview.cpp : Defines the entry point for the console application. ***/
2. /**** 欢迎访问杨刚的CSDN技术交流博客:http: //blog.csdn.net/Sunboy_2050 ***/
3. /**************************************/
4. /***** 百度面试 - 算法设计 ****/
5. /***** 面试官:漂亮姐姐 **** /
6. /***** 面试人:杨 刚 ****/
7. /***** 时 间:2010.6.28 **** /
8. /**************************************/
9. #include "stdafx.h"
10. #include
11. #include
12. #include
13. #include
14. #include
15. /**************************************/
16. /***** 面试题1:两数组归并排序 ****/
17. /**************************************/
18. template
19. void PrintArray(T *array, int len)
20. {
21. int i;
22. for(i=0; i
23. {
24. printf("%6d", array[i]); /* printf("%6.2f", array[i]); printf("%6c", array[i]);*/
25. if(9==i%10) /* 每输出5个数据后,就换行 */
26. printf("\n");
27. }
28. printf("\n"); /* 数组全部 输出后,换行 */
29. }
30. /******* 算法实现1: 升(降)序判定法,时间复杂度为 O(n) *******/
31. template
32. void MergySort(T *array1, int len1, T *array2, int len2, T *array3, int len3)
33. {
34. int i, j, k;
35. int flag1, flag2;
36. /* 记录数组Array1和Array2的升序或降序规则(首 尾两个元素相比较) */
37. /* 表示规则:1表示升序,0表示降序 */
38. flag1=(array1[0]
39. flag2=(array2[0]
40. k=0; /* 目标数组Array3的下标初始化 */
41. /* if只比较一次,即进入for循环,因此时间复杂度 为O(n) */
42. if(1==flag1 && 1==flag2) /* 升-升: 数组Array1升序,数组Array2升序,则数组Array3仍为升序 */
43. {
44. i=0;
45. j=0;
46. while(i
47. {
48. if(array1[i]
49. array3[k++]=array1[i++];
50. else
51. array3[k++]=array2[j++];
52. }
53. while(i
54. array3[k++]=array1[i++];
55. while (j
56. array3[k++]=array2[j++];
57. }
58. else if(1==flag1 && 0==flag2) /* 升-降: 数组Array1升序,数组Array2降序,则数组Array3仍为升序 */
59. {
60. i=0;
61. j=len2-1; /* 从末尾开始升序向前比 较,依次都为升序进行排序 */
62. while (i
63. {
64. if(array1[i]
65. array3[k++]=array1[i++];
66. else
67. array3[k++]=array2[j--];
68. }
69. while(i
70. array3[k++]=array1[i++];
71. while (j>=0)
72. array3[k++]=array2[j--];
73. }
74. else if(0==flag1 && 1==flag2) /* 降-升: 数组Array1降序,数组Array2升序,则数组Array3仍为降序 */
75. {
76. i=0;
77. j=len2-1; /* 从末尾开始倒序向前比 较,依次都为降序进行排序 */
78. while (i
79. {
80. if(array1[i]>array2[j])
81. array3[k++]=array1[i++];
82. else
83. array3[k++]=array2[j--];
84. }
85. while(i
86. array3[k++]=array1[i++];
87. while (j>=0)
88. array3[k++]=array2[j--];
89. }
90. else if(0==flag1 && 0==flag2) /* 降-降: 数组Array1降序,数组Array2降序,则数组Array3仍为降序 */
91. {
92. i=0;
93. j=0;
94. while (i
95. {
96. if(array1[i]>array2[j])
97. array3[k++]=array1[i++];
98. else
99. array3[k++]=array2[j++];
100. }
101. while(i
102. array3[k++]=array1[i++];
103. while (j
104. array3[k++]=array2[j++];
105. }
106. }
107. /* 归并两个有序数组(升序或降序)到一个大数组 */
108. void MergeArray()
109. {
110. /*************************************************/
111. /* 测试用例1:等价划分(升升、升降、降升、降 降) */
112. /*************************************************/
113. /*int array1[10]={1,2,3,4,5,6,7,8,9,10};
114. int array2[5]={0,3,6,9,12};*/
115. int array1[10]={1,2,3,4,5,6,7,8,9,10};
116. int array2[5]={12,9,6,3,0};
117. /*int array1[10]={10,9,8,7,6,5,4,3,2,1};
118. int array2[5]={0,3,6,9,12};*/
119. /*int array1[10]={10,9,8,7,6,5,4,3,2,1};
120. int array2[5]={12,9,6,3,0};*/
121. int array3[15]={0};
122. /*************************************************/
123. /* 测试用例2:边界值分析(左边界、包含、右边 界) */
124. /*************************************************/
125. /*int array1[10]={1,2,3,4,5,6,7,8,9,10};
126. int array2[5]={1,3,6,9,12};*/
127. /*int array1[10]={1,2,3,4,5,6,7,8,9,10};
128. int array2[5]={2,3,6,8,9};*/
129. /*int array1[10]={1,2,3,4,5,6,7,8,9,10};
130. int array2[5]={1,3,6,9,10};*/
131. /*int array1[10]={1,2,3,4,5,6,7,8,9,10};
132. int array2[5]={0,3,6,9,10};*/
133. /*int array1[10]={1,2,3,4,5,6,7,8,9,10};
134. int array2[5]={1,1,1,1,1};*/
135. /*int array1[10]={10,10,10,10,10,10,10,10,10,10};
136. int array2[5]={1,1,1,1,1};*/
137. /*int array1[10]={10,10,10,10,10,10,10,10,10,10};
138. int array2[5]={10,10,10,10,10};*/
139.
140. //int array3[15]={0};
141. /*************************************************************/
142. /* 测试用例3:经验评估测试(浮点型、字符型等非整型异常数 据) */
143. /*************************************************************/
144. /*float array1[10]={1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.0};
145. float array2[5]={1.2, 3.4, 6.7, 9.8, 12.0};
146. float array3[15]={0.0};*/
147. /*char array1[10]={'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
148. char array2[5]={'0', 'A', 'J', 'a', 'z'};
149. char array3[15]={0.0};*/
150.
151. printf("\n有序数组Array1:\n");
152. PrintArray(array1, 10);
153.
154. printf("\n有序数组Array2:\n");
155. PrintArray(array2, 5);
156. MergySort(array1, 10, array2, 5, array3, 15);
157. printf("\n归并Array1和Array2后的有 序数组:\n");
158. PrintArray(array3, 15);
159. }
160.
161. /****************************************************************/
162. /***** 面试题2:不借助第三方变量,交换两个整型数x和y的 值 ****/
163. /****************************************************************/
164. void swap()
165. {
166. int x, y;
167. x=5;
168. y=10;
169. /*函数声明,引用传地址但未用第三方变量*/
170. void swap1(int &x, int &y); /* 方 法1 */
171. void swap2(int &x, int &y); /* 方法2 */
172. void swap3(int &x, int &y); /* 方法3 */
173. printf("\n原始值:x=%d, y=%d\n", x, y);
174. swap3(x, y);
175. printf("\n交换后:x=%d, y=%d\n", x, y);
176. }
177.
178. /****************************************/
179. /******* 方法1:算术运算(加 减) *******/
180. /****************************************/
181. void swap1(int &x, int &y)
182. {
183. x=x+y; /* x存储x与y的和值(核心思想:x同时先把x和y两者的信息都存储下来) */
184. y=x-y; /* 保持x内存和值不变,y先赋值,即减去y的 原始值使其等于x原始的值 */
185. x=x-y; /* 保持x内存和值不变,x再赋值,即减去y现在存储的原始x值,更新x值为原始y的值 */
186. }
187. /**************************************************************/
188. /******* 方法2:算术运算(乘除、指数运算、三角运算 等) *******/
189. /**************************************************************/
190. void swap2(int &x, int &y)
191. {
192. x=x*y; /* x存储x与y的积值,核心思想同方法1,x同时先把x和y两者的信息都存储下来,本方法以乘除为例 */
193. y=x/y; /* 保持x内存积值不变,y先赋值,即除 去y的原始值使其等于x原始的值 */
194. x=x/y; /* 保持x内存积值不变,x再赋值,即除去y现在存储的原始x值,更新x值为原始y的值 */
195. }
196. /****************************************/
197. /******* 方法3:逻辑运算(异 或) *******/
198. /****************************************/
199. void swap3(int &x, int &y)
200. {
201. x^=y; /* x存储x与y的异或值(核心思想同上,即x先存储x和y两者信息(异或表示)) */
202. y^=x; /* 保持x内存和值不变,y先赋值,即利用x异或 反转y的原始值使其等于x原始的值 */
203. x^=y; /* 保持x内存和值不变,x再赋值,即利用x异或反转y的原始值使其等于y原始的值 */
204. }
205. /*********************************************************/
206. /******* 方法4:Linux系统下,利用Python语言实 现 *******/
207. /*********************************************************/
208. /********** 源代码 **********/
209. /*
210. # !/bin/sh/python
211. # FileName: swap.py
212. # Function: swap x and y not by other variable
213. x=5
214. y=10
215. print("swap before...")
216. print("x=%d, y=%d") % (x,y)
217. x,y=y,x # swap x and y
218. print("swap after...")
219. print("x=%d, y=%d") % (x,y)
220. */
221. /********** 编译方法 **********/
222. /*
223. 说明:
224. 编译环境:Redhat Linux Server 5.2
225. 代码工具:vim文本编辑器
226. 因为绝大部分Linux系统在安装时都会默认自带安装python
227. 在python语言中,#表示注释
228. 第一行 # !/bin/sh/python 告诉系统:编译此py文件 的解释器路径,来编译此py源文件
229. 第二行 Filename 注释表示此程序的文件名称为 swap.py,文件执行时不执行
230. 第三行 Filename 注释表示此程序实现的功能,即不利用其它变量 交换x和y的值
231. 具 体编译运行方法
232. 1、登陆 进入Linux系统的终端 Shell 命令界面
233. 2、python swap.py
234. */
235. /********** 运行结果 **********/
236. /*
237. [root@localhost python]# python swap.py
238. swap before...
239. x=5, y=10
240. swap after...
241. x=10, y=5
242. */
243.
244. /****************************************************************/
245. /***** 面试题3:统计单词出现过的文件,并给出其 文件名 ****/
246. /****************************************************************/
247. /* 查找匹配文件中是否包含word单词,如果第一次查找匹配成 功,则立即返回文件名 */
248. const char *Find_Word(const char *filePath, const char *word)
249. {
250. FILE *pf=NULL;
251. const char *pword=word;
252. char ch;
253. int i, len, flag;
254. if (NULL==(pf=fopen(filePath, "r"))) /* 判断是否 成功以只读方式打开文件 */
255. {
256. printf("Sorry! Cannot open file...\n");
257. return NULL;
258. }
259. i=0;
260. while (pword[i++]!='\0')
261. ;
262. len=i-1;
263. i=0;
264. flag=1;
265. ch=fgetc(pf);
266. while(ch!=EOF) /* 循环单个字符读取文件 */
267. {
268. if(ch==' ') /* 英文单词以空格分隔,每当遇到空格,则flag=1表示开始匹配新单词 */
269. {
270. flag=1;
271. ch=fgetc(pf);
272. continue;
273. }
274. if(ch==pword[i] && i
275. i++;
276. else /* 匹配失败, 则重置i=0 */
277. {
278. i=0;
279. flag=0;
280. }
281.
282. if(i>=len && flag) /* 如果全部匹配成功,则i>=len 返回文件名 */
283. {
284. fclose(pf); /* 关闭打开文件 */
285. pf=NULL; /* 防止野指 针 */
286. return filePath;
287. }
288. ch=fgetc(pf);
289. }
290. fclose(pf);
291. pf=NULL;
292. return NULL;
293. }
294. /* 遍历文件夹下的所有*.txt格式文件 */
295. void Search_TxtFiles(const char *dirPath, const char *word)
296. {
297. char *filePath=NULL;
298. const char *findFileName=NULL;
299. struct _finddata_t c_file;
300. long hFile;
301. if((hFile=_findfirst("*.txt", &c_file))==-1)
302. {
303. printf( "There is no *.txt files in current directory!\n" );
304. return;
305. }
306. else
307. {
308. printf( "\nListing of files\n" );
309. printf( "\n%-12s %9ld\n", c_file.name, c_file.size );
310. findFileName=Find_Word(c_file.name, word); /* 读取第一个文件 */
311. if(NULL!=findFileName)
312. printf("\n\n%s is found in file: %s\n\n", word, findFileName);
313. while( _findnext( hFile, &c_file ) == 0 ) /* 循环读取其它剩余文件 */
314. {
315. printf( "\n%-12s %9ld\n", c_file.name, c_file.size );
316. findFileName=Find_Word(c_file.name, word);
317. if(NULL!=findFileName)
318. printf("\n\n%s is found in file: %s\n\n", word, findFileName);
319. }
320.
321. _findclose( hFile );
322. }
323. printf("\n");
324. }
325. /* 显示目录文件夹下(相对路径)所有包含单词word的文件名 (*.txt) */
326. void Display_FileName()
327. {
328. char *dirPath="..\\..\\data\\txt\\*.txt";
329. char *word="baidu"; /* 匹配 文件中是否含有baidu的单词 */
330. Search_TxtFiles(dirPath, word);
331. }
332. /* 主函数,实现面试三道C语言算法题 */
333. int _tmain(int argc, _TCHAR* argv[])
334. {
335. MergeArray(); /* 算法题1: 归并两个有序数组(升序或降序) */
336.
337. swap(); /* 算法题2: 不借助第三方变量,交换两个整型数x和y的值 */
338. Display_FileName(); /* 算法题3: 统 计单词出现过的文件,并给出其文件名 */
339. return 0;
340. }
在经历了半个多月的漫长等待、翘首以盼的期待以后,今天终于有幸再次去百度大厦总部二面。此乃谓真应了百度的那句名言:众里寻他千百度,蓦然回首, 那人却在灯火阑珊处——为了信念,执著追求、永不言弃!
这次面试官是一位GG,很帅气,干练,自信中散发着内在的睿智——三张纸、两个人、一支笔,没带笔记本——这便是今天面试的主题。
在深刻分析和总结了一面失利的教训后,为了防止一面因听不清题目而要求面试官JJ重述题目的失误,再因这次面试官是一位GG,因此可以和GG坐得尽 量靠近些,这样一来便于一次性清晰、准确、完整的理解题目的确切信息,另一方面也方便和面试官进行面对面充分的交流,避免在同一个地方跌倒两次——重蹈一 面的覆辙。不过这次面试,我的这些担心看来似乎都是多余的:GG说话声音适量,很有底气,题意、需求、提问也都很清晰、简洁、明了,而且GG在提问的时候 还特意重点强调了题意的重要信息,因此这次面试没有因听不清题目而要求面试官重述问题的情况,而且我在理解了GG表述的意思后也尽量迅捷作出反馈。总体 上,这次面试感觉比较轻松,交流也很充分,GG待人很好,气氛也很Happy
OK 介绍了这些面试的“前传”以后,下面就让我们直入今天面试的主题
第一道,给出两个集合A和B,其中集合A={name},集合B={age、sex、scholarship、address、...},要求:问 题1、根据集合A中的name查询出集合B中对应的属性信息;问题2、根据集合B中的属性信息(单个属性,如age<20等),查询出集合A中对应 的name
第二道,给出一个文件,里面包含两个字段{url、size},即url为网址,size为对应网址访问的次数,要求:问题1、利用Linux Shell命令或自己设计算法,查询出url字符串中包含“baidu”子字符串对应的size字段值;问题2、根据问题1的查询结果,对其按照size 由大到小的排列。(说明:url数据量很大,100亿级以上)
第三道,测试一部手机(手机是普通手机,除了系统软件外,可能已经安装了某些应用软件)
第四道,根据我简历上的项目经历,选出了其中的一个“Gloss搜索推荐系统”项目,让我介绍其架构以及我负责的工作
第五道,询问了是否比较熟悉的编程语言(C/C++/C#)、VS2008以及对Linux Shell、Python认识等,主要是聊天交流方式,感觉很轻松
最后,是自由提问环节啦,问我有些什么问题,我主要提问了三个问题:框计算与云计算有哪些区别以及框计算目前的进展如何、百度面对团购网和移动互联 网等新兴市场将有哪些战略方面的调整以及质量测试部门的工作和后期面试将要准备或学习些什么(这三个问题依次涉及到:1、框计算,关注百度的发展战略和核 心技术创新;2、百度在面对不断变革和创新的互联网市场,将如何定位和战略调整;3、涉及到个人如果有幸三面或最终被录用,我将可能需要提前学习和准备哪 些方面的知识储备)
对于第一道的问题1:因为最近我一直都负责处理实验室的海量文本数据(500G以上的log文本数据)进行数据挖掘分析,因此我直接给出了我正在用 到的文本处理方法。
方法1、直接利用HashTable的{key、value}来做映射,即利用key记录集合A中的name属性,利用value记录集合B中的属 性向量,然后直接key来查询value值
方法2、使用C++的容器类map进行一对多的映射(其实应该是multimap,当时忘了一对一与一对多的关系,在此更正一下)
方法3、自定义映射关系,大体思路是index+vector模式,vector可以利用struct结构体做,index主要对name建立索 引,提高查询效率这题的的核心解决思想,其实就是如何建立起一对多的映射关系,以及如何高效的进行查询匹配
对于第一道的问题2:一时我没有非常完整、高效的解决方案,但是我给面试官GG讲了我思考的大体思路:
第一种方案,集合A和集合B,逐行读取并按属性字段判断,如age<20则打印出name,否则直接读取下一行。但GG指出这样做的缺点是需 要遍历整个文件,效率较低
第二种方案,于是提高效率,我借鉴了倒排索引的思想,提出把关键字段建立索引,进行“全文”搜索(其实就是集合A中的name),但是GG提出建立 索引会占用额外存储空间,不是最优的方案
第三种方案,为了不占用额外存储空间,我再想借鉴数据库的Group+Order方法,对集合B关键字段先分组然后排序,这样既不占用额外存储空 间,也比方案一提高了效率,但GG说这不是最优方案,并给我提示说其实很简单,没我想的这么复杂,让我继续想想。。。
于是我继续想了一小会儿,在头脑中快速搜索我曾经还遇到过哪些能够既不占额外存储空间,又能提高效率的方法或借鉴思想。火花一冒,对呀,最近炒得很 热的框计算、云计算,它们的设计思想或许可以一用,于是我接着想分布存储计算,对,利用分布式的思想来提高效率。可GG说似乎不行,这仍然不是解决问题2 的最佳解决方案。不过还好的是,GG没有在这个问题上继续损杀我的脑细胞和自信心,而是循于转入下一道题目(GG不死抠面试者最薄弱的环节,而是适时直接 转入下一环节,GG提问的技巧和哲学,给了面试者足够的自信和勇气,赞)
好啦,出于对我所崇拜的技术型创新公司——百度面试题目知识产权的保护以及对面试官GG(超牛级人物)劳动成果的尊重,我不再详细介绍本次面试的具 体细节 O(∩_∩)O哈哈~,谨以上述第一道题的大体思考过程和分析解决问题的方法,以飨各位支持我、鼓励我的博友们 ^_^
不过做事为了有始有终,不半途而废,下面我还是简单描述一下第2-5题我能想到的解决方法和细节,也希望大家能帮我提出更好更优化的方法
第二道,利用Linux Shell查询文件中的子字符串,需要用grep命令进行内容查询;分离显示文件的url和size两字段,似乎需要用到awk命令(没用过,不太熟 悉),二者通过管道|传值。不过当时我没用Shell命令(不敢在GG高手面前Show,否则出了问题可不太好ban滴 呵呵 面试回来后,我在man sort查看详细参数后,利用Shell的解法请见:百度面试及总结4 )于是我采用了自己设计算法解决的,分解成三步(1、字符匹配:字节流逐行读取,进行子字符串匹配KMP算法,查出所有包含“baidu”的url;2、 打印满足1对应的size结果;3、根据2的结果,先把字段size字段值的字符型转换成整型,然后再用排序算法进行排序)当时说了三种排序算法:二分 法、快排、堆排序
这里面还有一个小插曲, GG让我简单写写排序算法的代码,我开始选择采用二分法,复杂度O(nlog2n) 不过这次我在下笔写代码之前,先吸取了一面时JJ说我代码不规范的建议,于是我便从void QuickSort(int sizeArray[], int low, int high)、int i, j, tmp; ... 最基本格式写起,虽然感觉比较麻烦,耗费了宝贵的面试时间,但毕竟吃一堑、长一智嘛——从细节做起,严格要求自己。好吧,就这样写着写着,当写到一半时, 突然发现不对头,我原先写过二分法,但忘记了边界值最后到底取上限high还是取下限low 于是我小声请示GG:我想换一种排序算法写,不用二分法,改用快排写,可以么。GG没有马上简单地回复我是或否,而是笑着问我怎么写到一半突然想换算法啦 呵呵。 面试提问的哲学或许就体现在这里,GG问得非常精妙,颇有提问的艺术。因为他问出了我在写算法的过程中,我头脑中预见、思考、判断、选择等一系列的无法看 到的思考过程(GG洞察问题的能力、提问的技巧艺术这些优点也非常值得我学习学习的)于是我也直接告诉GG我的弱点和担心,我怕二分插入排序移动位置后, 忘了到底取上标还是下标插入新值的细节所以我想改用我比较熟悉的快排算法,这样一来不用担心上下限选择的问题,二来快排利用递归,代码简洁、清晰、易懂。 GG笑了笑,同意了我的请求,哈哈,于是我改用快排写出了代码,并详细给GG讲了算法遍历的细节和递归过程。(具体快排、二分法(此处代表折半插入排 序)、堆排序的实现代码,请详见我原先写的博文 各种基本算法实现小结(五)—— 排序算法 )
第三道,测试题,在一面时也问过的。不过一面JJ问的是如何测试一台露天售货机,当时我心里想到了需要测试硬件部分,但却没说出来,直接跳到了测试 软件部分。这次我也吸取了一面叙述不完整、表达不充分的缺点,而是明确给出需要测试硬件部分等物理特性的。其实测试,只要把握三条最基本的原则:1、等价 划分;2、边界值测试;3、经验评估查错(当然啦,经理还需要考虑效益、进度等经济因素)然后遵照这些原则进行分析、思考测试用例、总结。例如本次测试一 部手机,先划分为硬件与软件测试两部分。硬件测试如温度、按键、开机、抗摔、抗震、抗摸等等物理特性;软件部分需要细划分为系统软件与应用软件两部分,因 为系统软件和应用软件的功能和使用频度不一样,因此需要考虑测试的轻重缓急,由重点软件(如电话薄)到次重点软件(如电子书)再到非重点软件(如游戏), 由使用频繁到较频繁再到较少使用等频度划分,如此依次划分进行计划、安排、进行测试。当时我举了硬件、软件;系统软件、应用软件;百度软件和其它应用软 件;以及在Android手机上详细测试百度手机输入法等测试过程。核心思路:等价划分——》边界值测试——》凭经验重点测试经常或较易出bug的地方 (如果大家想深入学习软件测试的核心思想和方法,推荐一本经典——大师级的Myers编写的《The Art of Software Testing》(软件测试的艺术)
第四道,主要是按照提供的简历,详细询问负责项目的设计思想、实现过程、部分功能实现细节以及采用的技术方法。GG询问了我简历上的一个搜索推荐相 关的项目(现导师已让我转交给了一研三师兄,作为他毕业论文开题项目)我详细讲了利用solr做全文搜索的基本原理以及我负责实现的一些功能模块。收集数 据源(中科院学生毕业论文、IEEE国际会议、各种学术期刊等等)、Solr建立索引和数据库存储(Solr是在Lucene全文搜索开源系统上封装后的 改良系统)、ASP.NET(C#)制作客户端前台页面、用户Search过程等。对这些功能模块我依次做了比较详细的介绍。GG似乎对Search和 sort很感兴趣,于是接着深入询问了solr建立、排序等细节问题,于是我把我曾经负责的工作,如Analyzer分词、语义分析;document全 文通过field字段建立索引及存储;用户Search parser的string分词抽取、排序等都做了较为深入的讲解和介绍。这道系统设计题,主要考察的是面试者进行系统设计的整体思想,以及分析问题、解 决问题的实战能力、对功能模块技术细节的掌握应用水平。GG问的这些问题,一针见血,切中要害,对系统整体、核心、细节等都关注了,而且还提出了我们设计 之初采用MySQL数据库进行存储数据的弊端(GG提出的这个问题,也的确是我们导师最近想改用文本进行海量数据存储)——很有预见能力,值得我在以后的 系统设计中,好好学习。。
第五道,就是以聊天为主啦,很轻松愉快,谈了一些比较熟悉的语言编程(C/C++/C#以及Python)、熟悉的编译环境(VS2008和 Linux GCC)、以及对C#是否做过界面(客户端和网页开发)、数据库设计(熟悉MySQL、SQL Server数据库设计,并独立设计过逻辑较为复杂的数据库系统)
最后,自由提问环节,这次我没像一面那样,问“太空舱”睡觉等幼稚的问题啦,而是关注百度的产品战略和技术创新(上面已说过,在此不再赘述)
好啦 终于写完了 这次面试总体感觉比一面要轻松、愉快,交流很顺畅,沟通也很友好,不过给我印象最深的还是GG的面试风格——三张纸(其中两张还是我的简历)、二个人 (GG和我)、一支笔(共同交流的语言)、不带笔记本(他将如何记录我面试的过程呢)——简单、率直、干练,体现出的是GG自信背后的智慧与才华,我终于 看到了我心目中大牛级的技术高手啦,以后还得多多向他请教、学习 ^_^ 同时,从GG面试风格上,反映出了百度的核心价值观——Simple and Reliable(简单可依赖)
今天周末,导师让我来实验室,帮他一起把数据从服务器上拷贝下来,然后做预处理,周一上午给出结果。 现在干得都差不多了,放在服务器让自个跑着,我闲来没事就随便看看新闻,逛逛论坛,写写博客,回顾总结一下前天面试的经验教训和心得体会
先说说前天百度面试的经历吧,那天下课后匆匆在教学楼打印代码后,便直接骑车飞奔五道口13号地铁站,进站、刷卡、等车、呼呼……、下车、刷卡、出 站、然后从西二旗以100米冲刺的速度狂奔百度大厦,跨进B座门后,手机显示16:48分,还好,总算没迟到!
和一面一样,提前5分钟预约面试官,17:00面试正式开始 balabala... 18:00面试over。刚好面试完后,哥哥就给我打电话问我面试的情况如何(可能哥哥就站在4楼看着我面试呢 ^_^),并让我在一楼大厅等一会,他下楼带我去百度一楼吃饭(这个点回学校肯定也都没饭啦) 于是我和哥哥一块下楼到百度食堂,也随便感受一下百度的食堂文化——哇,人好多啊!需要排队、需要刷卡、也有免费的绿豆汤。。。有点像中科院的食堂,颇具 学校食堂的气氛——于是点了一份喜欢吃的鱼和一小盘海带丝,要了一大份米饭,端了一大碗绿豆汤,哥哥替我打卡,好好蹭饭呗 哈哈
面试的序幕和片尾都讲完了,下面再回放一段精彩的剧情吧——回顾一下面试GG让我用Linux Shell命令进行字符串匹配的那道题吧(第二道)
题目大意:给出一个文件,里面包含两个字段{url、size},url即为网址,size为网址对应访问的次 数,要求:问题1、利用Linux Shell命令或自己设计算法,查询出url字符串中包含“baidu”子字符串对应的size字段值;问题2、根据问题1的查询结果,对其按照size 由大到小的排列
面试时我采用的是自己设计算法,面试回来后我man sort查了排序命令的参数使用手册,下面我就详细讲一下用Shell命令的做法
分析题意:baidu.txt{url, size},baidu.txt是文件,{url, size}是文件中的两个字段,并且url和size都是字符串型,字段之间用tab(\t)隔开
第一步,查询匹配url字符串中的字串"baidu",直接用grep命令,具体格式 grep "baidu" baidu.txt(每行仅url可能含有baidu子字符串)
第二步,显示含有字串"baidu"的url及其对应的size,可以直接用ls命令,具体格式 ls -l | grep "baidu" baidu.txt(管道传值)
第三步,将步骤2的结果,通过重定向命令>>保存在baidu2.txt文件中,即 grep "baidu" baidu.txt >> baidu2.txt 保存匹配结果
第四步,排序,直接利用sort命令,即格式ls -l | sort -rnk 2 baidu.txt (sort反向r、以第二个字段k 2、数值型n进行排序)
第五步,将步骤4的结果,通过重定向命令>>保存在baidu3.txt文件中,即 sort -rnk 2 baidu2.txt >> baidu3.txt 保存排序结果
按照上面五步,我们先看结果,用数据说话,然后我将在下面依次详细介绍上面五步中用到的Shell命令及其参数的确切含义:
首先,新建baidu.txt文件,即用Vim编辑器输入创建的测试用例(我用过的主流搜索引擎及百度的部分产品),然后利用cat命令查看文件
其次,我们查询url字符串中包含子串"baidu"的项,并打印出url及其size
然后,我们新建baidu2.txt文件,用于保存问题1的结果(即匹配字符串url中包含子串"baidu"的结果项url和size)
接着,我们对上述问题1的结果,利用sort命令按照size由大到小进行排序
最后,我们新建baidu3.txt文件,用于保存sort排序结果
附图说明:
以上截图,均截自我电脑Linux RedHat 5.2(安装在VMWare 7.0虚拟机上)
其中的Shell命令都在Redhat Linux OS环境已测试通过
====================================================================================
好啦,今晚在man sort 查看了sort详细参数使用方法后,似乎可以不用awk命令就可以搞定此题,看来并没有我面试时想得那么复杂。现在就让我们具体看看sort的参数以及 grep、重定向等Shell命令的详细使用方法吧
sort命令
格式:sort 【参数】 【文件】
举例:sort -rnk 2 baidu.txt
参数:r 逆序; n字符串按数值处理; k 2 表示第二个字段(列)
说明:在文件baidu.txt中,按照第二个字段的数值型由大到小进行排序
首先,在 Linux Shell命令行界面输入 man sort 查看 sort 的帮助手册
然后,查看本题中,我们需要用到的三个参数 r n k 的详细使用方法
由sort帮助文档显示:
1、参数n是把string字符串转换成numberical的值value进行比较(即把字符串转换成数值型,再进行比较)
2、参数r是反向,即逆序。由于sort默认排序是由小到大,而题意需要从大到小排序,因此此处需要逆序(注意:v在某些命令中也可表示反向,如正 则表达式中)
3、参数k是字段分隔,即从哪个字段开始直到哪个字段结束,按其进行排序(注意:Linux Redhat 第一列从1开始,而不是0,本题格式中size为第二列,因此我们k定为2,而不是1。区别于通常用的Array数组下限和Python语言中分组的下 限,即从0开始)
grep命令
格式:grep 【参数】 【查询字符串】 【文件】
举例:grep "baidu" baidu.txt
参数:此处无参数(省略了参数)
说明:查询匹配文件baidu.txt中,判断是否包含"baidu"子字符串
在Linux Shell命令行输入:man grep
管道| 命令
格式:【命令1】(目标) |【命令2】(源数据)
参数:为进程通信,无参数
举例:ls -l | grep "baidu" baidu.txt
说明:把文件baidu.txt中,包含"baidu"子字符串的结果,通过管道|命令,传给ls -l 作为目标内容,进行显示
重定向>或>> 命令
格式:【命令1】(源数据)> 【命令2】(目标) 或者 【命令1】(源数据)>> 【命令2】(目标)
参数:为信道传值,无参数
举例:sort -rnk 2 baidu.txt > baidu2.txt
说明:把文件baidu.txt中,对第二个字段按照数值型进行由大到小的排序,并将结果保存到文件baidu2.txt(清空后重写)
附图举例说明:先清空文件baidu2.txt中原有内容,然后再将sort结果重定向保存到 baidu2.txt文件中
附图举例说明:先并未清空文件baidu2.txt中原有内容,而是直接追加sort结果,重定向保存到 baidu2.txt文件中(保留baidu2.txt原有内容)
ls touch cat等其它基本命令,请详见我先前的博客 Linux Shell 常用命令与目录分区的学习总结
====================================================================================
第二步,查询匹配url字符串中是否含有字串"baidu"的另一种做法(在网友showmsg 的提示下,改用 awk 命令代替 grep 命令进行正则匹配)
当然,我们还是先看结果,然后我再介绍awk命令的使用方法
首先,我们查询并打印出含有"baidu"字串的url及其size
其次,我们对查询含有"baidu"的结果,对size进行有大到小的排序
最后,保存查询匹配"baidu"并对size由大到小排序后的结果到baidu2.txt文件中(重定向)
附加1,如果只想按照size由大到小打印出url(即不打印size,也就是分离字段),则如下
附加2,如果只想按照size由大到小打印出size(即不打印url,也就是分离字段),则如下
这就是我面试时想用的awk命令解法,不过我当时只知道此命令功能但没具体用过,在网友showmsg 高手指点下,总算明白 一二,对此表示谢意 ^_^
====================================================================================
awk 命令
格式:awk commands file 或者 awk script-file file
参数:print 打印;$1 第一个字段; $2 第二个字段; ' ' 单引号需加上,并可写入正则表达式,如查询baidu字串
举例:awk '/baidu/ && $2<800' baidu.txt
说明:查询文件baidu.txt中满足第一字段含有"baidu"字串并且第二个字段数字size<800的所有记录,并显示出结果
附图1:查询文件baidu.txt中满足第一字段含有"baidu"字串并且第二个字段数字 size<800的所有记录,并显示出结果
附图2:查询文件baidu.txt中满足第一字段含有"baidu"字串并且第二个字段数字 size<800的所有记录,并打印出结果的第一个字段(url)