记录几个面试题
1. 不使用循环把十进制数转换成八进制数:int trans(int num, int len); (提示:使用递归)
#include<stdio.h> const int MAX=101010; char s[MAX]; int tmp; void trans(int len,int x) { if(x>0) { tmp=x%8; s[len]='0'+tmp; trans(len+1,x/8); } if(len>0)printf("%c",s[len-1]); } int main() { int n; while(scanf("%d",&n)!=EOF) { trans(0,n); printf("\n"); } return 0; }
2. 非递归实现二叉树的后序遍历;http://blog.csdn.net/race604/article/details/6908640
3 进程之间的通信方式;http://zhidao.baidu.com/question/1450364.html
4 死锁的必要条件,以及预防措施;http://www.cnblogs.com/lancidie/archive/2011/08/20/2147040.html
5 进程同步的方式;http://wenku.baidu.com/view/bdcb8aeae009581b6bd9eb88.html
有n*m支足球队,分成n个小组,每组m支球队。每个小组内部进行循环淘汰赛,每支队伍都要和组内的其他所有队伍进行一场比赛,胜者得3分,平者得1分,负者得0分。每个小组的第一名(积分最高者)可以出线,剩下的所有队伍中再选一支积分最高的出线,一共出线n+1支球队。问在所有出线的队伍中,可能出现的最少积分是多少?(即球队最少积多少分就有可能出线)在什么情况下出现?(百度2008年面试题)
具体可见:http://blog.sina.com.cn/s/blog_46e25d670100nx4u.html。
找出规律,写出下一个数:61, 52, 63, 94, 46, ?
不会发散思维的人是做不出这道题的。把十位数字和个位数字调个个儿,就会发现它们分别是4的平方,5的平方,6的平方,7的平方和8的平方。所以答案是18。
有2000个小球,甲和乙轮流从里面拿。一次只能拿1个,2个或者4个,拿到最后一个球的人算输。甲先拿。问甲有没有必胜的把握?
很早以前就有类似的题,不过是一次只能拿1个,2个,3个或4个,拿到最后一个球的人赢。那道题只用保证每个回合拿的球是5的倍数就行了,比如甲先拿2个,乙就拿3个;甲先拿4个,乙就拿1个。只要总数是5的倍数,后拿的人一定赢。
现在这个题思路也类似,先找出可能的每个回合的固定数目。但是现在1+2=3,2+4=6,1+4=5,好像找不出一个固定的数字——一个不行可以用两个,3和6。仔细一想就会发现,剩3个球和剩6个球是一回事。另外,这题是拿最后一球的人输,怎么办?只用把球的总数减一,就又变回了拿最后一球赢的老问题。因为后拿的人刚好拿了倒数第二个球,剩下最后一个,肯定赢。同时这题球的总数不是3个倍数,减1以后仍然不是。这时候就要靠先拿的人把它变成3的倍数。
答案:有。甲第一次拿1个球。然后使用如下策略:如果乙拿1个或4个,甲就拿2个;如果乙拿2个,甲就拿1个。最后会剩下1个或4个球,乙一定会拿到最后一个。
给出一个大于0的正整数x,写一个函数f(x),求能够整除x的最大的2的整数次幂。也就是说,在1, 2, 4, 8, 16...这个数列里面找一个能整除x的最大的数。例如:f(7)=1; f(8)=8; f(28)=4。
大多数有经验的程序员都会想到逐个向右移位法,并检查最低位是否为0。但是标准答案用一个精妙的按位与运算解决了所有问题:
f(x) = x & (-x)
遇到1~8 输出8;遇到9~16 输出16;遇到17~24 输出24。观察规律,写一个只有一条语句的代码,并优化使CPU执行时间比较短。
有了上一题的经验,你能够顺利地做出这题了么?
答案:(x + 7) & (-8)
有4枚硬币,初始状态未知。你的眼睛被蒙住,看不到硬币的状态,但可以随便翻任何几个硬币。你每翻一次以后,如果4枚硬币的状态是全正面朝上或者全背面朝上,旁边的人会告诉你翻成功了。现在问你,最少翻几次可以保证成功?每次翻哪几枚?(百度2008年面试题)
我们用4位二进制数表示硬币的状态。由于最终的结果只要4枚硬币状态一样就行,正面还是背面没有关系,因此0000或1111都可以成功。由此又可以推出任何两个互为反码的状态是等价的,比如1001和0110,对于我们的目标来讲没有区别。因此也不必关心0和1代表的是正面还是背面,只要知道他们的位置关系就可以
0001
0010
0011
0100
0101
0110
0111
除了成功状态以外,一共就只有这7种状态,其他的所有状态都和这7种中的某一种等价。(相当于以最前面一个硬币的状态为基准,枚举其他的3个硬币的状态)
共有7种状态需要枚举(翻面做相应的改变即可)。这里设X表示翻面,0表示不翻面。
操作 操作后相对原始状态的改变
000X 硬币4翻面,
00XX 硬币3翻面。
000X 3,4翻面
0XXX 2,翻面
…………
第二问:现在将4枚硬币排成正方形(2*2的矩阵),每翻过一次以后,旁边有个人会将整个硬币图案旋转90度的任意倍数。有没有办法仍然保证能翻成功?最少需要几步?
面试官说20分钟内能解出这道题的人是天才。不过就我看来,找到解题的思路并不困难。
由于每一次都会随机的旋转,所以硬币的某个状态旋转成各种角度都是等价的,也就是说,下面每一行的状态都是等价的:
状态A | 0 1 0 0 |
1 0 0 0 |
0 0 0 1 |
0 0 1 0 |
状态B | 1 1 0 0 |
1 0 1 0 |
0 1 0 1 |
0 0 1 1 |
状态C | 0 1 1 0 |
1 0 0 1 |
可以看到,事实上只有3种不同的状态。同样的,由于在翻硬币之前会旋转,所以我们根本无法知道自己翻了哪一个硬币,只知道自己翻了几个,怎么排列。所以实际上只有3种翻法,和3种状态正好是类似的。
翻法x | O X O O |
X O O O |
O O O X |
O O X O |
翻法y | X X O O |
X O X O |
O X O X |
O O X X |
翻法z | O X X O |
X O O X |
下面分析对每一种状态,执行每一种翻法之后会变成什么状态。结果单元格对应行是初始状态,列是翻法。用end表示成功(全0),“/”分隔几种可能的状态。
x O X O O |
y X X O O |
z O X X O |
|
A 0 1 0 0 |
B/C/end | A | A |
B 1 1 0 0 |
A | C/end | B |
C 0 1 1 0 |
A | B | end |
最后一行是突破的关键点,因为对C状态进行z翻法必定会结束,而其他的都不能保证结束。那么,我们的策略是想办法让A和B转换成C,然后用z就结束了。整个翻法如下:(列出每一种初始状态在进行到一定步数后的状态)
步骤 | 翻法 | 初始状态A | 初始状态B | 初始状态C |
第1步 | z | A | B | end |
第2步 | y | A | C/end | |
第3步 | z | A | end | |
第4步 | x | B/C/end | ||
第5步 | z | B/end | ||
第6步 | y | C/end | ||
第7步 | z | end |
最少7步保证结束。另外给出最少步数的证明:因为在不旋转的情况下都最少要7步,所以旋转情况下的步数不可能比7步少,否则的话就可以用旋转的解法来解决不旋转的问题,故最少需要7步。
有25匹马,速度都不同,但每匹马的速度都是定值。现在只有5条赛道,无法计时,即每赛一场最多只能知道5匹马的相对快慢。问最少赛几场可以找出25匹马中速度最快的前3名?(百度2008年面试题)
每匹马都至少要有一次参赛的机会,所以25匹马分成5组,一开始的这5场比赛是免不了的。接下来要找冠军也很容易,每一组的冠军在一起赛一场就行了(第6场)。最后就是要找第2和第3名。我们按照第6场比赛中得到的名次依次把它们在前5场比赛中所在的组命名为A、B、C、D、E。即:A组的冠军是第6场的第1名,B组的冠军是第6场的第2名……每一组的5匹马按照他们已经赛出的成绩从快到慢编号:
A组:1,2,3,4,5
B组:1,2,3,4,5
C组:1,2,3,4,5
D组:1,2,3,4,5
E组:1,2,3,4,5
从现在所得到的信息,我们可以知道哪些马已经被排除在3名以外。只要已经能确定有3匹或3匹以上的马比这匹马快,那么它就已经被淘汰了。可以看到,只有上表中红色的那5匹马是有可能为2、3名的。即:A组的2、3名;B组的1、2名,C组的第1名。取这5匹马进行第7场比赛,第7场比赛的前两名就是25匹马中的2、3名。故一共最少要赛7场。
这道题有一些变体,比如64匹马找前4名。方法是一样的,在得出第1名以后寻找后3名的候选竞争者就可以了。
有n个苹果,n个小孩排队一个个地去拿苹果,每一个小孩在剩下的苹果中至少拿1个,至多全拿光。编程输出所有可能的拿法。(百度2008年面试题)
我一看到这个问题就想到了0-1背包问题:每个小孩可以拿1到n个,看成n种规格的物品放入大小为n的背包,用递归方法解出。但事实上,这道题没有这么复杂,让我们以n=4为例,按一定顺序列出所有的拿法:
4 0 0 0
3 1 0 0
2 2 0 0
2 1 1 0
1 3 0 0
1 2 1 0
1 1 2 0
1 1 1 1
可以得出这样一个规律:第n+1行的数列可以在第n行的基础上用如下的算法计算出:从右至左找到第一个大于1的数字,将它减1,然后将这个数字右边的数字赋为苹果总数减去左边所有数字后剩下的值,其余更右边的数全赋为0。
用这种算法可以编出一个非递归的程序,显然,它跑起来更快。
#include<stdio.h> #include<string.h> const int MAX=101000; int a[MAX]; int main() { int n; int i,j; int len; int flag; while(scanf("%d",&n)!=EOF) { memset(a,0,sizeof(a)); a[0]=n; len=0; flag=1; while(flag) { flag=0; for(i=0;i<n;i++) printf("%d ",a[i]); printf("\n"); for(j=n-1; j>=0; j--) { if(a[j]>1) { if(j==0) { a[j]--; a[j+1]=n-a[j]; for(i=j+2;i<n;i++) a[i]=0; } else { a[j]--; a[j+1]++; } flag=1; break; } } } } return 0; }
http://blog.sina.com.cn/s/articlelist_1189240167_2_1.html