20130506微软实习生面试二面题目:
一个数组,大小为n,其元素大小范围为1-n。其中一些数缺失了,另外一些数则重复。先要求找出其中重复的数和缺失的数。
我几乎没怎么想,很快的写出:
/**************************
* a[n],元素范围:1-n
* 输出重复的和丢失的数
**************************/
void fa(int a[],int n)
{
int *p = new int[n+1]();
for(int i=0;i<n;i++)
{
p[a[i]]++;
}
set<int> m,r;///保存丢失和重复的数,也可以直接输出
for(int i=1;i<=n;i++)
{
if(p[i]>1)
r.insert(i);
else if(p[i]==0)
m.insert(i);
}
delete[] p;
}
面试官说,你的程度复杂度是多少?
我说时间复杂度是O(n),由于使用了辅助数组,空间复杂度也是O(n)。
“如果数组很大,需要的辅助空间太多,现在要求你优化,降低空间复杂度到O(1),怎么做?”
我想了几秒,不能辅助空间了,那时间上可能有损失。就说“那排序吧,排序后遍历一遍就行了。”
这时时间复杂度最好是O(nlogn),使用堆排序。(快速排序时间复杂度不是O(1),最好是O(logn),最坏是O(n))
面试官显然不满意。。。我自己都不满意,算法总不能牺牲时间。。。。
他说,现在要求时间O(n),空间O(1).
然后有想了会,说用位图的方法,他让我写出来,
然后我就把上面的数组改成位存储,悲剧的是好久没用bitset,一时想不起来bitset的定义的基本操作了。
就大致的写了下,并说明忘记bitset了,如果有手册我马上能写好。
面试官说那你不用stl,自己用内存吧。
想了下,平时都用一个int表示多个位,没有遇这种申请一块内存的情况,如果一次申请一大块内存该怎么进行位操作呢?
更不应该的是我把我的想法说出来了,他让我把一个int的位操作写出来,我写出来了,他说那多个int不是类似么?
我当时没想到要移动指针。。。和他多次纠结才说出来。。。
(以前项目经验太少,没做过,现场思考。。。就是不顺利。。。尤其是在面试那样紧张的氛围下)
最后他说,那你自己写个BitMap类吧,可以实现stl中bitset的操作,例如
BitMap bm(30);
bm[2] = 1;
bm[5] = 0;
这样的操作。
一开始写就觉得麻烦了。。。:vector什么的写过,重载下[]就行,返回元素的引用。这样才可以实现赋值。
可是bit引用,貌似又没遇到过??怎么办。。。。
我只好用函数的型式写了set()和reset()2个接口,虽然等价于上面的赋值,可是毕竟没满足他的要求。
然后他看了下,说“这就是你写的?”
我只好说重载操作符不太确定。。。。
然后他说:“作为一个工程师,写代码是很平常的事……”
我知道这次面试到此为止了。
回来后马上收拾东西,第二天要去sh实习了。
后来过了几天,问了好几个周围认识的人,都不会。
然后在网上,看到别人写的下面的算法:
void fun()
{
int a[]={-1,3,3,3,3,3,3,8,9,8};///a[0]not use
int n = sizeof(a)/sizeof(int)-1;
for(int i=1;i<=n;i++)
{
while(a[a[i]]!=a[i])
{
int t = a[i];
a[i] = a[t];
a[t] = t;
}
}
for(int i=1;i<=n;i++)
{
if(a[i]!=i)
{
cout<<i<<"\t";///丢失的数
cout<<a[i]<<endl;///重复的数
}
}
}
好像是《编程珠玑》上的,这书我买了本英文版的,但是没看。。。。
唉,悲剧。
总结:之前面了好几家公司,基本都是一些常规算法,自己看到过的,基本很快的都答出来了。而且很少现场写代码的,基本是说出思路就行。然后就是问一些语言、OS、网络等知识点。一部分拿下了,得到offer,也有一部分悲剧了,几乎都是偏算法的。没见过的很少现场想出来。
微软的难度在于,随便你怎么说,说完我就让你写代码。一面的时候更悲剧。。。要写一个调度程序,我把数据结构写好后面试官说,很好,那你现在把你刚才说的用代码写出来。语言任意。
我室友面试的时候比我幸运多了,二面时算法是写冒泡排序!
当然一面和三面还有别的问题。他现在已经得到口头调剂offer了,去不去还没定。