缘起
不久前,我去一家创业公司面试,因为他们没有专业的java开发,于是请了一个颇有经验的人出了两道笔试题:1.二分查找的代码实现,2.谈谈关于软件设计原则的看法。也是比较难较难得了,很少有人会问到这些方面,特别是设计原则这块,有很多开发者都不了解这方面的知识。而第一题是很容易,但要是撸一串代码也会为难一批有十年以经验的开发者了(当然我还是有这个自信的)。
在远程口试中,对方问到了两个简单的排序,我觉得这两个排序虽然简单,但很有意思。因为两道题目很相似,但解法确有些异差。
正文
题一
给定三个数组:
数组a,长度m;
数组b,长度n;
数组c长度m+n;
其中a与b存放了有序的数据,c为空数组,要求将a与了组合放入c中,且让c为有序数组。跟我以前见过的面试题很相似
以下是伪代码的实现示例:
int pa=0,pb=0,pc=0;//指向数组a,b,c的三个指针;
while(pa<a.lenght && pb < b.lenght){
if(a[pa]<=b[pb]){
c[pc] = p[pa++];
}else{
c[pc]=p[pb++]
}
pc++;
}
if(pa<a.a.lenght){
arrayCopy(a,pa,(a.lenght-pa),c);// 将a中剩余的数组考贝到c中
}else if(pb <b.lenght){
arrayCopy(b,pb,(b.lenght-pb),c);// 将b中剩余的数组考贝到c中
}
题二
给定两数组:
a:长度n,已存放n个有序数。
b:长度(m+n),其中从0至m-1中已存放两个有序序列
要求:将a中的数据拷贝到B中,且保持B为有序。
两题看起来很相似,但其实解法有些不同,题一我们设了三个指针,且按顺序移动,如果题二也按类似实现,那会让b中的数据整体移动(可以参考一下插入排序),于是采用逆序方式
伪代码如下:
int pa = n-1; //指向a的指针
int pb = m -1 ; //指向b的指针
int putNext = a+b -1; 下一个在存放位置的指针
while(pa >=0 && pb >=0){
if(a[pa]>= b[pb]){
b[putNext] = a[pa];
pa--;
}else{
b[putNext] = b[pb];
pb--;
}
putNext--;
}
if(pa != 0){
...
}else if(pb != 0){
....
}
扩展
1 生成组织树
有人会问,这种算法题对自己的工作真的有帮助吗?或能没有帮助,但养成一好思维模式很重要。比如我在工作中见到这样的需求,有这样一张组织表:
id | 名称 | 层级 | 父id |
---|---|---|---|
1 | 总部 | 0 | 0 |
2 | 上海总部 | 1 | 1 |
3 | 北京总部 | 1 | 1 |
4 | 上海技术部 | 2 | 2 |
5 | 上海销售部 | 2 | 2 |
5 | 北京销售部 | 2 | 3 |
要求输出一颗树,或类似这样的json格式,再或者要和业务数据关联,进行统计分析。我们能有什么解决方案呢,要用递归一次次的查询,或作一次查询,然后递归操作,再或者用类似动态规划的方式用map保存有个节点的引用(这种方式还是蛮好的,以后我会给一个示例代码)?但我们的查询语句写成这样:
select * from 部门表 order by 层级 asc,父id asc, id asc.
我们在拼装树时真的需要用到递归吗?用上面的sql语句查询出的数据集已带有某种特性了,我们可以利用这些特性,来选择更优的算法。
2.查询时间段交集数据
id | 实例开始时间 | 实例停止时间 |
---|---|---|
1 | 220-1-1 00:00 | 2010-1-3 00:44 |
2 | 220-1-3 00:00 | 2010-1-4 00:44 |
3 | 220-1-8 00:00 | 2010-1-20 00:44 |
我曾在一家名企与其成员共事过一段时间,有个需求:给一个起始时间以及一个结束时间,查询这段时间类所有运行的实例。也就是给定起始时间与结束时间,查询表中所有交集数据。项目组的SE特别交待:你要考虑很多情况:比如给定的时间段包含了起始时间,或者包含了结束时间,或者记录的时间段段包含了查询的时间……
如果要按照那样的分析去做,我或许会写一个有一堆or组成的超级语句。
但只要考虑到表中实例开始时间与实例停止时间 的顺序性,以及查询中所带的顺序性,这个语句只需要一个简单的and.
总结
一种特定的算法可能不重要,但思想很重要,要利用你现有数据集的特征,设计出合理的算法,有时能少写代码,有时能提高你程序的性能。