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

笔试面试题

2016-07-29 00:00 323 查看
此微软100题V0.2版的前60题,请见这:微软、谷歌、百度等公司经典面试100题[第1-60题]。关于本人整理微软100题的一切详情,请参见这:横空出世,席卷Csdn [评微软等数据结构+算法面试100题]

声明
1、下面的题目来不及一一细看,答案大部是摘自网友,且个人认为比较好一点的思路,对这些思路和答案本人未经细细验证,仅保留意见。
2、为尊重作者劳动成果,凡是引用了网友提供的面试题、思路,或答案,都一一注明了网友的昵称。若对以下任何一题的思路,不是很懂的,欢迎留言或评论中提出,我可再做详细阐述。
3、以下的每一题,都是自个平时一一搜集整理的,转载请务必注明出处。任何人,有任何问题,欢迎不吝指正。谢谢。

微软、Google等公司一些非常好的面试题、第61-70题
61
、腾讯现场招聘问题
liuchen1206
今天参加了腾讯的现场招聘会,碰到这个一个题目:
在一篇英文文章中查找指定的人名,人名使用二十六个英文字母(可以是大写或小写)、空格以及两个通配符组成(*、?),通配符“*”表示零个或多个任意字母,通配符“?”表示一个任意字母。
如:“J* Smi??” 可以匹配“John Smith” .
请用C语言实现如下函数:
void scan(const char* pszText, const char* pszName);
注:pszText为整个文章字符,pszName为要求匹配的英文名。
请完成些函数实现输出所有匹配的英文名,除了printf外,不能用第三方的库函数等。
代码一(此段代码已经多个网友指出,bug不少,但暂没想到解决办法):

view plain

//copyright@ falcomavin && July

//updated:

//多谢Yingmg网友指出,由于之前这代码是从编译器->记事本->本博客,辗转三次而来的,

//所以,之前的代码不符合缩进规范,

//特此再把它搬到编译器上,调整好缩进后,不再放到记事本上,而是直接从编译器上贴到这里来。

//July,说明。2011.04.17。

#include <iostream>

using namespace std;

int scan(const char* text, const char* pattern)

{

const char *p = pattern; // 记录初始位置,以便patten匹配一半失败可返回原位

if (*pattern == 0) return 1; // 匹配成功条件

if (*text == 0) return 0; // 匹配失败条件

if (*pattern != '*' && *pattern != '?')

{

if (*text != *pattern) //如果匹配不成功

return scan(text+1, pattern); //text++,寻找下一个匹配

}

if (*pattern == '?')

{

if (!isalpha(*text)) // 通配符'?'匹配失败

{

pattern = p; // 还原pattern初始位置

return scan(text+1, pattern); //text++,寻找下一个匹配

}

else // 通配符'?'匹配成功

{

return scan(text+1, pattern + 1); //双双后移,++

}

}

return scan(text, pattern+1); // 能走到这里,一定是在匹配通配符'*'了

}

int main()

{

char *i, *j;

i = new char[100];

j = new char[100];

cin>>i>>j;

cout<<scan(i,j);

return 0;

}

代码二:

view plain

//qq120848369:

#include <iostream>

using namespace std;

const char *pEnd=NULL;

bool match(const char *pszText,const char *pszName)

{

if(*pszName == '/0') // 匹配完成

{

pEnd=pszText;

return true;

}

if(*pszText == '/0') // 未匹配完成

{

if(*pszName == '*')

{

pEnd=pszText;

return true;

}

return false;

}

if(*pszName!= '*' && *pszName!='?')

{

if(*pszText == *pszName)

{

return match(pszText+1,pszName+1);

}

return false;

}

else

{

if(*pszName == '*')

{

return match(pszText,pszName+1)||match(pszText+1,pszName);

//匹配0个,或者继续*匹配下去

}

else

{

return match(pszText+1,pszName+1);

}

}

}

void scan(const char *pszText, const char *pszName)

{

while(*pszText!='/0')

{

if(match(pszText,pszName))

{

while(pszText!=pEnd)

{

cout<<*pszText++;

}

cout<<endl;

}

return;

}

}

int main()

{

char pszText[100],pszName[100];

fgets(pszText,100,stdin);

fgets(pszName,100,stdin);

scan(pszText,pszName);

return 0;

}

wangxugangzy05:
这个是kmp子串搜索(匹配),稍加改造,如 abcabd*?abe**??de这个窜,我们可以分成abcabd,?,abe,?,?,并按顺序先匹配abcabd,当匹配后,将匹配的文章中地址及匹配的是何子串放到栈里记录下来,这样,每次匹配都入栈保存当前子串匹配信息,当一次完整的全部子串都匹配完后,就输出一个匹配结果,然后回溯一下,开始对栈顶的子串的进行文中下一个起始位置的匹配。

62、微软三道面试题
yeardoublehua
1. 给一个有N个整数的数组S..和另一个整数X,判断S里有没有2个数的和为X,
请设计成O(n*log2(n))的算法。
2. 有2个数组..里面有N个整数。
设计一个算法O(n log2(n)),看是否两个数组里存在一个同样的数。
3. 让你排序N个比N^7小的数,要求的算法是O(n)(给了提示..说往N进制那方面想)
qq120848369:
1,快排,头尾夹逼.


view plain

#include <iostream>

#include <algorithm>

#include <utility>

using namespace std;

typedef pair<int,int> Pair;

Pair findSum(int *s,int n,int x)

{

sort(s,s+n); //引用了库函数

int *begin=s;

int *end=s+n-1;

while(begin<end) //俩头夹逼,很经典的方法

{

if(*begin+*end>x)

{

--end;

}

else if(*begin+*end<x)

{

++begin;

}

else

{

return Pair(*begin,*end);

}

}

return Pair(-1,-1);

}

int main()

{

int arr[100]=

{

3, -4, 7, 8, 12, -5, 0, 9

};

int n=8,x;

while(cin>>x)

{

Pair ret=findSum(arr,n,x);

cout<<ret.first<<","<<ret.second<<endl;

}

return 0;

}

2,快排,线性扫描


view plain

#include <iostream>

#include <algorithm>

using namespace std;

bool findSame(const int *a,int len1,const int *b,int len2,int *result)

{

int i,j;

i=j=0;

while(i<len1 && j<len2)

{

if(a[i]<b[j])

{

++i;

}

else if(a[i]>b[j])

{

++j;

}

else

{

*result=a[i];

return true;

}

}

return false;

}

int main()

{

int a[100],b[100],len1,len2,result;

cin>>len1;

for(int i=0;i<len1;++i)

{

cin>>a[i];

}

cin>>len2;

for(int i=0;i<len2;++i)

{

cin>>b[i];

}

if( findSame(a,len1,b,len2,&result) )

{

cout<<result<<endl;

}

return 0;

}

3,基数排序已经可以O(n)了,准备10个vector<int>,从最低位数字开始,放入相应的桶里,然后再顺序取出来,然后再从次低位放入相应桶里,在顺序取出来.比如:N=5,分别是:4,10,7,123,33
0 :10
1
2
3 :123,33
4 :4
5
6
7 :7
8
9
顺次取出来:10,123,33,,4,7
0 :4,7
1 :10
2 :123
3 :33
4
5
6
7
8
9
依次取出来:4,7,10,123,33
0 :4,7,10,33
1 :123
2
3
4
5
6
7
8
9
依次取出来:4,7,10,33,123
完毕。
代码,如下:

view plain

#include <iostream>

#include <string>

#include <queue>

#include <vector>

using namespace std;

size_t n; //n个数

size_t maxLen=0; //最大的数字位数

vector< queue<string> > vec(10); //10个桶

vector<string> result;

void sort()

{

for(size_t i=0;i<maxLen;++i)

{

for(size_t j=0;j<result.size();++j)

{

if( i>=result[j].length() )

{

vec[0].push(result[j]);

}

else

{

vec[ result[j][result[j].length()-1-i]-'0' ].push(result[j]);

}

}

result.clear();

for(size_t k=0;k<vec.size();++k)

{

while(!vec[k].empty())

{

result.push_back(vec[k].front());

vec[k].pop();

}

}

}

}

int main()

{

cin>>n;

string input;

for(size_t i=0;i<n;++i)

{

cin>>input;

result.push_back(input);

if(maxLen == 0 || input.length()>maxLen)

{

maxLen=input.length();

}

}

sort();

for(size_t i=0;i<n;++i)

{

cout<<result[i]<<" ";

}

cout<<endl;

return 0;

}

xiaoboalex:
第一题,1. 给一个有N个整数的数组S..和另一个整数X,判断S里有没有2个数的和为X,
请设计成O(n*log2(n))的算法。
如果限定最坏复杂度nlgn的话就不能用快排。
可以用归并排序,然后Y=X-E,用两分搜索依次查找每一个Y是否存在,保证最坏复杂度为nlgn.

63、微软亚洲研究院
hinyunsin
假设有一颗二叉树,已知这棵树的节点上不均匀的分布了若干石头,石头数跟这棵二叉树的节点数相同,石头只可以在子节点和父节点之间进行搬运,每次只能搬运一颗石头。请问如何以最少的步骤将石头搬运均匀,使得每个节点上的石头上刚好为1。
个人,暂时还没看到清晰的,更好的思路,以下是网友mathe、casahama、Torey等人给的思路:
mathe:
我们对于任意一个节点,可以查看其本身和左子树,右子树的几个信息:
i)本身上面石子数目
ii)左子树中石子数目和节点数目的差值
iii)右子树中石子数目和节点数目的差值
iv)通过i),ii),iii)可以计算出除掉这三部份其余节点中石子和节点数目的差值。
如果上面信息都已经计算出来,那么对于这个节点,我们就可以计算出同其关联三条边石子运送最小数目。比如,如果左子树石子数目和节点数目差值为a<0,那么表示比如通过这个节点通向左之数的边至少运送a个石子;反之如果a>0,那么表示必须通过这个节点通向左子树的边反向运送a个石子。同样可以计算出同父节点之间的最小运送数目。
然后对所有节点,将这些数目(ii,iii,iv中)绝对值相加就可以得出下界。
而证明这个下界可以达到也很简单。每次找出一个石子数目大于1的点,那么它至少有一条边需要向外运送,操作之即可。每次操作以后,必然上面这些绝对值数目和减1。所以有限步操作后必然达到均衡。所以现在唯一余下的问题就是如何计算这些数值问题。这个我们只要按照拓扑排序,从叶节点开始向根节点计算即可。
casahama:
节点上的石头数不能小于0。所以当子节点石头数==0 并且 父节点石头数==0的时候,是需要继续向上回溯的。基于这一点,想在一次遍历中解决这个问题是不可能的。
这一点考虑进去的话,看来应该再多加一个栈保存这样类似的结点才行.
Torey:
后序遍历
证明:
在一棵只有三个节点的子二叉树里,石头在子树里搬运的步数肯定小于等于子树外面节点搬运的步数。
石头由一个子树移到另一个子数可归结为两步,一为石头移到父节点,二为石头由父节点移到子树结点,所以无论哪颗石头移到哪个节点,总步数总是一定。
关于树的遍历,在面试题中已出现过太多次了,特此稍稍整理以下:
二叉树结点存储的数据结构:
typedef char datatype;
typedef struct node
{
datatype data;
struct node* lchild,*rchild;
} bintnode;
typedef bintnode* bintree;
bintree root;
1.树的前序遍历即:
按根 左 右 的顺序,依次
前序遍历根结点->前序遍历左子树->前序遍历右子树
前序遍历,递归算法
void preorder(bintree t)
//注,bintree为一指向二叉树根结点的指针
{
if(t)
{
printf("%c",t->data);
preorder(t->lchild);
preorder(t->rchild);
}
}
2.中序遍历,递归算法
void preorder(bintree t)
{
if(t)
{
inorder(t->lchild);
printf("%c",t->data);
inorder(t->rchild);
}
}
3.后序遍历,递归算法
void preorder(bintree t)
{
if(t)
{
postorder(t->lchild);
postorder(t->rchild);
printf("%c",t->data);
}
}
关于实现二叉树的前序、中序、后序遍历的递归与非递归实现,的更多,请参考这(微软100题第43题答案):
http://blog.csdn.net/v_JULY_v/archive/2011/02/01/6171539.aspx

64、淘宝校园笔试题
goengine
N个鸡蛋放到M个篮子中,篮子不能为空,要满足:对任意不大于N的数量,能用若干个篮子中鸡蛋的和表示。
写出函数,对输入整数N和M,输出所有可能的鸡蛋的放法。
比如对于9个鸡蛋5个篮子
解至少有三组:
1 2 4 1 1
1 2 2 2 2
1 2 3 2 1
思路一、
Sorehead在我的微软100题,维护地址上,已经对此题有了详细的思路与阐释,以下是他的个人思路+代码:
Sorehead
思路:
1、由于每个篮子都不能为空,可以转换成每个篮子先都有1个鸡蛋,再对剩下的N-M个鸡蛋进行分配,这样就可以先求和为N-M的所有排列可能性。
2、假设N-M=10,求解所有排列可能性可以从一个比较简单的递规来实现,转变为下列数组:
(10,0)、(9,1)、(8,2)、(7,3)、(6,4)、(5,5)、(4,6)、(3,7)、(2,8)、(1,9)
这里对其中第一个元素进行循环递减,对第二个元素进行上述递规重复求解,
例如(5,5)转变成:(5,0)、(4,1)、(3,2)、(2,3)、(1,4)
由于是求所有排列可能性,不允许有重复记录,因此结果就只能是非递增或者非递减队列,这里我采用的非递增队列来处理。
3、上面的递规过程中对于像(4,6)这样的不符合条件就可以跳过不输出,但递规不能直接跳出,必须继续进行下去,因为(4,6)的结果集中还是有不少能符合条件的。
我写的是非递规程序,因此(4,6)这样的组合我就直接转换成4,4,2,然后再继续做处理。
4、N-M的所有排列可能性已经求出来了,里面的元素全部加1,如果N-M<M,剩下的元素就全部是1,这样N个鸡蛋放入M个篮子的所有可能性就全部求出来了。注意排列中可能元素数量会超过篮子数量M,去除这样的排列即可。
5、接下来的结果就是取出上述结果集中不满足“对于任意一个不超过N的正整数,都能由某几个篮子内蛋的数量相加得到”条件的记录了。
首先是根据这个条件去除不可能有结果的情况:如果M>N,显而易见这是不可能有结果的;那对于给定的N值,M是否不能小于某个值呢,答案是肯定的。
6、对于给定的N值,M值最小的组合应该是1,2,4,8,16,32...这样的序列,这样我们就可以计算出M的最小值可能了,如果M小于该值,也是不可能有结果的。
7、接下来,对于给定的结果集,由于有个篮子的鸡蛋数量必须为1,可以先去掉最小值大于1的记录;同样,篮子中鸡蛋最大数量也应该不能超过某值,该值应该在N/2左右,具体值要看N是奇数还是偶数了,原因是因为超过这个值,其它篮子的鸡蛋数量全部相加都无法得到比该值小1的数。
8、最后如何保证剩下的结果中都是符合要求的,这是个难题。当然有个简单方法就是对结果中的每个数挨个进行判断。

view plain

//下面是他写的代码:

void malloc_egg(int m, int n)

{

int *stack, top;

int count, max, flag, i;

if (m < 1 || n < 1 || m > n)

return;

//得到m的最小可能值,去除不可能情况

i = n / 2;

count = 1;

while (i > 0)

{

i /= 2;

count++;

}

if (m < count)

return;

//对m=n或m=n-1进行特殊处理

if (m >= n - 1)

{

if (m == n)

printf("1,");

else

printf("2,");

for (i = 0; i < m; i++)

printf("1,");

printf("/n");

return;

}

if ((stack = malloc(sizeof(int) * (n - m))) == NULL)

return;

stack[0] = n - m;

top = 0;

//得到篮子中鸡蛋最大数量值

max = n % 2 ? n / 2 : n / 2 - 1;

if (stack[0] <= max)

{

printf("%d,", n - m + 1);

for (i = 1; i < m; i++)

printf("1,");

printf("/n");

}

do

{

count = 0;

for (i = top; i >= 0 && stack[i] == 1; i--)

count++;

if (count > 0)

{

top -= count;

stack[top]--;

count++;

//保证是个非递增数列

while (stack[top] < count)

{

stack[top + 1] = stack[top];

count -= stack[top];

top++;

}

stack[++top] = count;

}

else

{

stack[top]--;

stack[++top] = 1;

}

//去除元素数量会超过篮子数量、超过鸡蛋最大数量值的记录

if (top >= m - 1)

continue;

if (stack[0] > max)

continue;

//对记录中的每个数挨个进行判断,保证符合条件二

flag = 0;

count = m - top;

for (i = top; i >= 0; i--)

{

if (stack[i] >= count)

{

flag = 1;

break;

}

count += stack[i] + 1;

}

if (flag)

continue;

//输出记录结果值

for (i = 0; i < m; i++)

{

if (i <= top)

printf("%d,", stack[i] + 1);

else

printf("1,");

}

printf("/n");

}

while (stack[0] > 1);

free(stack);

}

存在的问题:
1、程序我没有进行严格的测试,所以不能保证中间没有问题,而且不少地方都可以再优化,中间有些部分处理得不是很好,有时间我再好好改进一下。
2、有些情况还可以特殊处理一下,例如M>N/2时,似乎满足条件一的所有组合都是满足条件二的;当N=(2的n次方-1),M=n时,结果只有一个,就是1、2、4、...(2的n-1次方),应该可以根据这个对其它结果进行推导。
3、这种方法是先根据条件一得到所有可能性,然后在这个结果集中去除不符合条件二的,感觉效率不是很好。个人觉得应该有办法可以直接把两个条件一起考虑,靠某种方式主动推出结果,而不是我现在采用的被动筛选方式。其实我刚开始就是想采用这种方式,但得到的结果集中总是缺少一些了排列可能。
思路二、以下是晖的个人思路:
qq675927952
N个鸡蛋分到M个篮子里(N>M),不能有空篮子,对于任意不大于于N的数,保证有几个篮子的鸡蛋数和等于此数,编程实现输入N,M两个数,输出所有鸡蛋的方法。
1、全输出的话本质就是搜索+剪枝。
2、(n,m,min)表示当前状态,按照篮子里蛋的数目从小到大搜索。搜到了第m个篮子,1..m个篮子面共放了n个蛋,当前的篮子放了min个蛋。下一个扩展(n+t,m+1,t),for t=min...n+1。当n+(M-m)*min>N (鸡蛋不够时)或者2^(M-m)*n+2^(M-m)-1<N(鸡蛋太多)时 把这个枝剪掉……  ; 
3、太多时的情况如下: n,n+1,2n+2,4n+4,8n+8....。代码如下:

view plain

//copyright@ 晖

//updated:

//听从网友yingmg的建议,再放到编译器上,调整下了缩进。

#include <iostream>

using namespace std;

long pow2[20];

int N,M;

int ans[1000];

void solve( int n , int m , int Min )

{

if(n == N && m == M)

{

for(int i=1;i<=M;i++)

{

cout<<ans[i]<<" ";

}

cout<<endl;

return ;

}

else if( n + (M-m)*Min > N || N > pow2[M-m]*n + pow2[M-m]-1)

return ;

else

{

for(int i = Min; i <= n+1; i++)

{

ans[m+1] = i;

solve(n+i,m+1,i);

}

}

}

int main()

{

pow2[0] = 1;

for(int i=1;i<20;i++)

{

pow2[i] = pow2[i-1]<<1;

}

cin>>N>>M;

if( M > N || pow2[M]-1 < N)

{

cout<<"没有这样的组合"<<endl;

}

solve( 0 , 0 , 1 );

system("pause");

return 0;

}

此思路二来自:
http://blog.csdn.net/qq675927952/archive/2011/03/30/6290131.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: