您的位置:首页 > 职场人生

程序员面试题精选100题:11-40解题报告

2013-06-25 14:37 183 查看
程序员面试题精选100题(11)-求二元查找树的镜像[数据结构]
题目:输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点。用递归和循环两种方法完成树的镜像转换。
例如输入:
8

/ \

6 10

/\ /\

5 7 9 11
输出:
8

/ \

10 6

/\ /\

11 9 7 5
非递归就是使用栈模拟。
=====================================================================
程序员面试题精选100题(12)-从上往下遍历二元树[数据结构]
题目:输入一颗二元树,从上往下按层打印树的每个结点,同一层中按照从左往右的顺序打印。
例如输入
8

/ \

6 10

/\ /\

5 7 9 11
输出8 6 10 5 7 9 11。
分析:层次遍历,使用队列存储数据。
我们知道树是图的一种特殊退化形式。同时如果对图的深度优先遍历和广度优先遍历有比较深刻的理解,将不难看出这种遍历方式实际上是一种广度优先遍历。因此这道题的本质是在二元树上实现广度优先遍历

==============================================================================
程序员面试题精选100题(13)-第一个只出现一次的字符[算法]
题目:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b


分析:1.hash_map
2.因为是字符0-255,所以我们可以用256长度的数组作为hash,每个元素记录对应ascii码个数。
==============================================================================
程序员面试题精选100题(14)-圆圈中最后剩下的数字[算法]
题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。
分析:这是著名的约瑟夫环问题。其实自己推也不太会,先记下递归关系吧。
f(n,m)=f’(n-1,m)=[f(n-1,m)+m]%n

========================================================================
程序员面试题精选100题(15)-含有指针成员的类的拷贝[C/C++/C#]
分析:指针浅拷贝导致的指针悬挂问题
解决方法:1.私有化,不能复制构造,赋值操作,这肯定不是上策
2.每个对象保留一份数据副本
3.自己实现引用计数,多个对象共同指向同一数据内存
4.直接使用boost的shard_ptr
有时间要总结下boost的智能指针了。
========================================================================

程序员面试题精选100题(16)-O(logn)求Fibonacci数列[算法]
分析:
1.纯递归 O(2^n)
2.记忆化递归,空间换时间O(n)
3.递推,类似于DP,时间O(n),空间O(1)
4.logn的真想不到,智商拙计啊,还是看答案吧:
{f(n), f(n-1), f(n-1), f(n-2)} ={1, 1, 1,0}n-1
f(n)为矩阵的第一个元素
/ an/2*an/2 n为偶数时

an=

\ a(n-1)/2*a(n-1)/2 n为奇数时
========================================================================

程序员面试题精选100题(17)-把字符串转换成整数[算法]
分析:这个真不一定能写对,我想说能直接用atoi吗?哈
========================================================================
程序员面试题精选100题(18)-用两个栈实现队列[数据结构]
已经提过了在第二题

========================================================================
程序员面试题精选100题(19)-反转链表[数据结构]
题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。
分析:假设n之前的n-1个都已经逆序了,pnext=n->next,n->next=ppre;
然后更新ppre为n,n为pnext
========================================================================

程序员面试题精选100题(20)-最长公共子串[算法]
典型DP,算法导论上有详细介绍
如果我们记字符串Xi和Yj的LCS的长度为c[i,j],我们可以递归地求c[i,j]:

/ 0 if i<0 or j<0

c[i,j]= c[i-1,j-1]+1 if i,j>=0 and xi=xj

\ max(c[i,j-1],c[i-1,j] if i,j>=0 and xi≠xj



扩展:
如果题目改成求两个字符串的最长公共子字符串,应该怎么求?
最长公共字串:不连续;最长公共子字符串:连续
DP[i][j]定义为以I,j为结尾的最长公共子字符串长度。
状态转移方程为DP[i][j]=DP[i-1][j-1]if a[i]==b[j] else = 0;
最终结果为max{DP[i][j]},其中可以放入到状态递推中不必单独O(n^2)循环。
初始:DP[0][j],DP[i][0]=0,浪费一个空间,从1开始有效。
结果序列也好求:记录max对应的i和j,然后依次递减,知道不等即结束。

========================================================================
程序员面试题精选100题(21)-左旋转字符串[算法]
翻转句子中的单词已经讨论过了。

========================================================================
程序员面试题精选100题(22)-整数二进制表示中1的个数[算法]
位操作果然很奇妙。
int NumberOf1_Solution1(int i)

{

int count = 0;

while(i)

{

if(i & 1)

count ++;

i = i >> 1;

}

return count;

}
这个思路当输入i是正数时没有问题,但当输入的i是一个负数时,不但不能得到正确的1的个数,还将导致死循环。以负数0x80000000为例,右移一位的时候,并不是简单地把最高位的1移到第二位变成0x40000000,而是0xC0000000。这是因为移位前是个负数,仍然要保证移位后是个负数,因此移位后的最高位会设为1。如果一直做右移运算,最终这个数字就会变成0xFFFFFFFF而陷入死循环。
解决方法:
不右移I,可以等价的左移1。。。。很巧妙
上述算法复杂度为i的位数,下面方法可以降到i中1的个数:
另外一种思路是如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减去1,那么原来处在整数最右边的1就会变成0,原来在1后面的所有的0都会变成1。其余的所有位将不受到影响。举个例子:一个二进制数1100,从右边数起的第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成1,而前面的1保持不变,因此得到结果是1011。

我们发现减1的结果是把从最右边一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000。也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

扩展:
如何用一个语句判断一个整数是不是二的整数次幂?
是2的次幂,就是之含有一个1,那就判断i&(i-1)是不是0就可以了。。。。
========================================================================
程序员面试题精选100题(23)-跳台阶问题[算法]
Fibonacci序列,怎么这么像奥数题。。。。主要考察分析抽象问题能力,递推关系。。。
========================================================================
程序员面试题精选100题(24)-栈的push、pop序列[数据结构]
题目:输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。
比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。因为可以有如下的push和pop序列:push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,这样得到的pop序列就是4、5、3、2、1。但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。
这道题的一个很直观的想法就是建立一个辅助栈,每次push的时候就把一个整数push进入这个辅助栈,同样需要pop的时候就把该栈的栈顶整数pop出来。
如果我们希望pop的数字正好是栈顶数字,直接pop出栈即可;如果希望pop的数字目前不在栈顶,我们就到push序列中还没有被push到栈里的数字中去搜索这个数字,并把在它之前的所有数字都push进栈。如果所有的数字都被push进栈仍然没有找到这个数字,表明该序列不可能是一个pop序列。
========================================================================

程序员面试题精选100题(25)-在从1到n的正数中1出现的次数[算法]
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。

例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。

分析:这是一道广为流传的google面试题。用最直观的方法求解并不是很难,但遗憾的是效率不是很高;而要得出一个效率较高的算法,需要比较强的分析能力,并不是件很容易的事情。当然,google的面试题中简单的也没有几道。

我只会暴力解法,智商拙计啊。。。。高效的推导,略
========================================================================
程序员面试题精选100题(26)-和为n连续正数序列[算法]
题目:输入一个正数n,输出所有和为n连续正数序列。

例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-5、4-6和7-8。

分析:起始值不超过(n+1)/2,最小2个元素
1. 暴力:sum(I,i+1…n)判断是否为n,对于(n+1)/2个I,复杂度为O(n^2)
2. 没有使用连续这个特征。(i+j)(j-i+1)/2=n,可以解出j,循环i判断j是否合理即可解决,复杂度O(n)
3. 这种接法是为了引出下面的扩展问题,同样的思路而且不用有序。这道题和本面试题系列的第10题有些类似。我们用两个数small和big分别表示序列的最小值和最大值。首先把small初始化为1,big初始化为2。如果从small到big的序列的和大于n的话,我们向右移动small,相当于从序列中去掉较小的数字。如果从small到big的序列的和小于n的话,我们向右移动big,相当于向序列中添加big的下一个数字。一直到small等于(1+n)/2,因为序列至少要有两个数字。这种方法相对于第二种复杂度一样的,但big会多走一些步。
扩展:
google面试题:无序数组是否存在连续几个数的和等于N
有一串没有排好序的正整数,中间可能有重复,查看这串数中是否存在连续的几个数之和为N
分析:1.暴力O(n^3)
2.优化下f(I,n)=f(I,n-1)+a[i]
一趟O(n),对于n个i总复杂度为O(n^2)
3.快慢指针I,j。如果小于K,j增加;否则i增加,需要注意I==J的情况(当sumN时,要分两种情况,i==j,或者i!=j,主要是因为可能数组中的某个元素自己就比N大,这样的话最终会走到i==j这一步,这时就得跳过当前自身数值都会比N大的元素,直接采用下一个元素的值作为sum,i和j的值也都做相应的更新)ij不回溯,最多2n步,所以复杂度为O(n)
========================================================================
程序员面试题精选100题(27)-二元树的深度[数据结构]
题目:输入一棵二元树的根结点,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

例如:输入二元树:

10

/ \

6 14

/ / \

4 12 16

输出该树的深度3。

分析:递归。
非递归:先序遍历。参考/article/2365603.html
========================================================================

程序员面试题精选100题(28)-字符串的排列[算法]
题目:输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
分析:递归,交换。参考/article/2365601.html
#include<iostream>
#include<stdio.h>
#include<assert.h>
usingnamespace std;

voidswap(char* a,char* b)
{
char tmp = *a;
*b = tmp;
*a = *b;
}

voidPermutation(char* pStr, char* pBegin)
{
assert(pStr && pBegin);
if(*pBegin == '\0')
printf("%s\n",pStr);
else
{
for(char* pCh = pBegin; *pCh != '\0';pCh++)
{
swap(*pBegin,*pCh);
Permutation(pStr, pBegin+1);
swap(*pBegin,*pCh);
}
}
}

intmain(void)
{
char str[] = "abcd";
Permutation(str,str);
return 0;
}
有重复元素情况:参考/article/2365601.html
扩展:
组合问题:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

intstate[5] = {0};

voidprint(char* str)
{
int i;
for(i=0; i<strlen(str); ++i)
{
if(state[i] )
printf("%c",str[i]);
}
printf("\n");
}

voiddigui(char* str,int b,int e)
{
if(b == e)
{
print(str);
return ;
}
state = 1;//yes
digui(str,b+1,e);
state[b] = 0;//no
digui(str,b+1,e);
}

intmain()
{
char *str = "abcef";
digui(str,0,strlen(str));
return 0;
}

扩展2:输入一个含有8个数字的数组,判断有没有可能把这8个数字分别放到正方体的8个顶点上,使得正方体上三组相对的面上的4个顶点的和相等。
===========================================================================
[b]程序员面试题精选100题(29)-调整数组顺序使奇数位于偶数前面[算法]

解法:前后指针向中间靠拢,类似于快排的partition。
讨论:

上面的代码有三点值得提出来和大家讨论:

1.函数isEven判断一个数字是不是偶数并没有用%运算符而是用&。理由是通常情况下位运算符比%要快一些;

2.这道题有很多变种。这里要求是把奇数放在偶数的前面,如果把要求改成:把负数放在非负数的前面等,思路都是都一样的。

3.在函数Reorder中,用函数指针func指向的函数来判断一个数字是不是符合给定的条件,而不是用在代码直接判断(hardcode)。这样的好处是把调整顺序的算法和调整的标准分开了(即解耦,decouple)。当调整的标准改变时,Reorder的代码不需要修改,只需要提供一个新的确定调整标准的函数即可,提高了代码的可维护性。例如要求把负数放在非负数的前面,我们不需要修改Reorder的代码,只需添加一个函数来判断整数是不是非负数。这样的思路在很多库中都有广泛的应用,比如在STL的很多算法函数中都有一个仿函数(functor)的参数(当然仿函数不是函数指针,但其思想是一样的)。如果在面试中能够想到这一层,无疑能给面试官留下很好的印象。

===========================================================================
程序员面试题精选100题(30)-赋值运算符重载函数[C/C++/C#]
C++基础,没啥说的。。。
===========================================================================
程序员面试题精选100题(31)-从尾到头输出链表[数据结构]
分析:1.之前的反转链表,然后从新的头到尾输出。缺点:额外操作,破坏链表结构了。
2.放入栈中,弹栈输出即可。
3.递归。
扩展:1. 从尾到头输出一个字符串;

2. 定义一个函数求字符串的长度,要求该函数体内不能声明任何变量。

===========================================================================
程序员面试题精选100题(32)-不能被继承的类[C/C++/C#]
题目:用C++设计一个不能被继承的类。

分析:这是Adobe公司2007年校园招聘的最新笔试题。这道题除了考察应聘者的C++基本功底外,还能考察反应能力,是一道很好的题目。

分析:/article/2365607.html
1.私有构造、析构函数可以实现。
2.更好的方法友元+虚继承。
C++虚继承的作用
C++虚继承可以防止多重继承产生的二义性问题。
上面的试验结果表面,在多重继承的时候,如果父类中有同名的成员变量(类似这篇文章中谈及的例子),为了防止二义性,一般要采用虚继承的方式,并且最右边的基类中的那个成员变量会出现在派生类对象中
===========================================================================
程序员面试题精选100题(33)-在O(1)时间删除链表结点[数据结构]
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。
脑筋急转弯。。。
(2)给定的要删除的结点是链表的头结点情况。
===========================================================================
程序员面试题精选100题(34)-数组中只出现一次的数字[算法]
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

分析:这是一道很新颖的关于位运算的面试题。

限定空间复杂度为O(1)了,不能使用hash_map了。
异或运算,最终是a^b,关键在怎么把数组分成两部分,两部分分别异或就得出a,b了。
===========================================================================
程序员面试题精选100题(35)-两链表的第一个公共结点[数据结构]
题目:两个单向链表,找出它们的第一个公共结点。
分析:长的先走l-s步,再一起走,
扩展:如果有环呢????
===========================================================================
程序员面试题精选100题(36)-在字符串中删除特定的字符[算法]
题目:输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。
分析:快慢指针。查找使用hash,因为是字符,可以申请256大小数组。
===========================================================================
程序员面试题精选100题(37)-寻找丑数[算法]
题目:我们把只包含因子
2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第1500个丑数。
分析:这是一道在网络上广为流传的面试题,据说google曾经采用过这道题。
很巧妙,第一次想不到,只会暴力法。。。智商拙计。。。。

===========================================================================
程序员面试题精选100题(38)-输出1到最大的N位数[算法]
题目:输入数字n,按顺序输出从1最大的n位10进制数。比如输入3,则输出1、2、3一直到最大的3位数即999。
分析:1.直接,可能溢出
2.大整数递增,短时间很难写完写正确。
3.没想到的递归啊。。。。
===========================================================================
程序员面试题精选100题(39)-颠倒栈[数据结构]
题目:用递归颠倒一个栈。例如输入栈{1, 2, 3, 4, 5},1在栈顶。颠倒之后的栈为{5, 4, 3, 2, 1},5处在栈顶。
题目要求递归。
1. pop栈顶
2. 递归反转剩下的,
3. 将栈顶添加到栈底。
这个第三步卡住了,没想到效率高的算法,只想到从栈顶到栈底上一个元素依次后移一位,然后将栈顶放到栈底,但是栈不提供随机访问呐。看完答案恍然大悟,还是递归呐,尼玛,题目也要求递归呀。我们需要考虑的另外一件事情是如何把一个元素e放到一个栈的底部,也就是如何实现AddToStackBottom。这件事情不难,只需要把栈里原有的元素逐一pop出来。当栈为空的时候,push元素e进栈,此时它就位于栈的底部了。然后再把栈里原有的元素按照pop相反的顺序逐一push进栈。

注意到我们在push元素e之前,我们已经把栈里原有的所有元素都pop出来了,我们需要把它们保存起来,以便之后能把他们再push回去。我们当然可以开辟一个数组来做,但这没有必要。由于我们可以用递归来做这件事情,而递归本身就是一个栈结构。我们可以用递归的栈来保存这些元素。

复杂度为O(n^2),每个AddToStackBottom复杂度就为O(n)
这题其实就是考察对递归的熟悉程度。其实完全可以先pop进数组,然后在逆序push进原栈即可,复杂度只有O(n)。
===========================================================================
程序员面试题精选100题(40)-扑克牌的顺子[算法]
题目:从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2-10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。

分析:这题目很有意思,是一个典型的寓教于乐的题目。

这题不难,考察抽象能力。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: