前二十答案加试题.docx
《前二十答案加试题.docx》由会员分享,可在线阅读,更多相关《前二十答案加试题.docx(44页珍藏版)》请在冰豆网上搜索。
前二十答案加试题
永久勘误:
微软等面试100题系列,答案V0.2版[第1-20题答案]
1.把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/\
614
/\/\
481216
转换成双向链表
4=6=8=10=12=14=16。
首先我们定义的二元查找树节点的数据结构如下:
structBSTreeNode
{
intm_nValue;//valueofnode
BSTreeNode*m_pLeft;//leftchildofnode
BSTreeNode*m_pRight;//rightchildofnode
};
//引用245楼tree_star的回复
#include
#include
structBSTreeNode
{
intm_nValue;//valueofnode
BSTreeNode*m_pLeft;//leftchildofnode
BSTreeNode*m_pRight;//rightchildofnode
};
typedefBSTreeNodeDoubleList;
DoubleList*pHead;
DoubleList*pListIndex;
voidconvertToDoubleList(BSTreeNode*pCurrent);
//创建二元查找树
voidaddBSTreeNode(BSTreeNode*&pCurrent,intvalue)
{
if(NULL==pCurrent)
{
BSTreeNode*pBSTree=newBSTreeNode();
pBSTree->m_pLeft=NULL;
pBSTree->m_pRight=NULL;
pBSTree->m_nValue=value;
pCurrent=pBSTree;
}
else
{
if((pCurrent->m_nValue)>value)
{
addBSTreeNode(pCurrent->m_pLeft,value);
}
elseif((pCurrent->m_nValue){
addBSTreeNode(pCurrent->m_pRight,value);
}
else
{
//cout<<"重复加入节点"<}
}
}
//遍历二元查找树中序
voidergodicBSTree(BSTreeNode*pCurrent)
{
if(NULL==pCurrent)
{
return;
}
if(NULL!
=pCurrent->m_pLeft)
{
ergodicBSTree(pCurrent->m_pLeft);
}
//节点接到链表尾部
convertToDoubleList(pCurrent);
//右子树为空
if(NULL!
=pCurrent->m_pRight)
{
ergodicBSTree(pCurrent->m_pRight);
}
}
//二叉树转换成list
voidconvertToDoubleList(BSTreeNode*pCurrent)
{
pCurrent->m_pLeft=pListIndex;
if(NULL!
=pListIndex)
{
pListIndex->m_pRight=pCurrent;
}
else
{
pHead=pCurrent;
}
pListIndex=pCurrent;
cout<m_nValue<}
intmain()
{
BSTreeNode*pRoot=NULL;
pListIndex=NULL;
pHead=NULL;
addBSTreeNode(pRoot,10);
addBSTreeNode(pRoot,4);
addBSTreeNode(pRoot,6);
addBSTreeNode(pRoot,8);
addBSTreeNode(pRoot,12);
addBSTreeNode(pRoot,14);
addBSTreeNode(pRoot,15);
addBSTreeNode(pRoot,16);
ergodicBSTree(pRoot);
return0;
}
///////////////////////////////////////////////
4
6
8
10
12
14
15
16
Pressanykeytocontinue
//////////////////////////////////////////////
2.设计包含min函数的栈。
定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。
要求函数min、push以及pop的时间复杂度都是O
(1)。
结合链表一起做。
首先我做插入以下数字:
10,7,3,3,8,5,2,6
0:
10->NULL(MIN=10,POS=0)
1:
7->[0](MIN=7,POS=1)用数组表示堆栈,第0个元素表示栈底
2:
3->[1](MIN=3,POS=2)
3:
3->[2](MIN=3,POS=3)
4:
8->NULL(MIN=3,POS=3)技巧在这里,因为8比当前的MIN大,所以弹出8不会对当前的MIN产生影响
5:
5->NULL(MIN=3,POS=3)
6:
2->[2](MIN=2,POS=6)如果2出栈了,那么3就是MIN
7:
6->[6]
出栈的话采用类似方法修正。
所以,此题的第1小题,即是借助辅助栈,保存最小值,
且随时更新辅助栈中的元素。
如先后,push26415
stackAstackB(辅助栈)
4:
51//push5,min=p->[3]=1^
3:
11//push1,min=p->[3]=1|//此刻push进A的元素1小于B中栈顶元素2
2:
42//push4,min=p->[0]=2|
1:
62//push6,min=p->[0]=2|
0:
22//push2,min=p->[0]=2|
push第一个元素进A,也把它push进B,
当向Apush的元素比B中的元素小,则也push进B,即更新B。
否则,不动B,保存原值。
向栈Apush元素时,顺序由下至上。
辅助栈B中,始终保存着最小的元素。
然后,pop栈A中元素,51462
AB->更新
4:
511//pop5,min=p->[3]=1|
3:
112//pop1,min=p->[0]=2|
2:
422//pop4,min=p->[0]=2|
1:
622//pop6,min=p->[0]=2|
0:
22NULL//pop2,min=NULLv
当popA中的元素小于B中栈顶元素时,则也要popB中栈顶元素。
3.求子数组的最大和
题目:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
求所有子数组的和的最大值。
要求时间复杂度为O(n)。
例如输入的数组为1,-2,3,10,-4,7,2,-5,和最大的子数组为3,10,-4,7,2,
因此输出为该子数组的和18。
//July2010/10/18
#include
intmaxSum(int*a,intn)
{
intsum=0;
intb=0;
for(inti=0;i{
if(b<0)
b=a[i];
else
b+=a[i];
if(sum
sum=b;
}
returnsum;
}
intmain()
{
inta[10]={1,-8,6,3,-1,5,7,-2,0,1};
cout<return0;
}
运行结果,如下:
20
Pressanykeytocontinue
------------------------------------------------------------
intmaxSum(int*a,intn)
{
intsum=0;
intb=0;
for(inti=0;i{
if(b<=0)//此处修正下,把b<0改为b<=0
b=a[i];
else
b+=a[i];
if(sum
sum=b;
}
returnsum;
}
//////////////////////////////////////////////
解释下:
例如输入的数组为1,-2,3,10,-4,7,2,-5,
那么最大的子数组为3,10,-4,7,2,
因此输出为该子数组的和18
所有的东西都在以下俩行,
即:
b:
01-1313916187
sum:
01131313161818
其实算法很简单,当前面的几个数,加起来后,b<0后,
把b重新赋值,置为下一个元素,b=a[i]。
当b>sum,则更新sum=b;
若b:
)。
July、10/31。
///////////////////////////////////////////////
第4题,
当访问到某一结点时,把该结点添加到路径上,并累加当前结点的值。
如果当前结点为叶结点并且当前路径的和刚好等于输入的整数,则当前的路径符合要求,我们把它打印出来
。
如果当前结点不是叶结点,则继续访问它的子结点。
当前结点访问结束后,递归函数将自动回到父结点。
因此我们在函数退出之前要在路径上删除当前结点并减去当前结点的值,
以确保返回父结点时路径刚好是根结点到父结点的路径。
我们不难看出保存路径的数据结构实际上是一个栈结构,因为路径要与递归调用状态一致,
而递归调用本质就是一个压栈和出栈的过程。
voidFindPath
(
BinaryTreeNode*pTreeNode,//anodeofbinarytree
intexpectedSum,//theexpectedsum
std:
:
vector&path,//apathfromroottocurrentnode
int¤tSum//thesumofpath
)
{
if(!
pTreeNode)
return;
currentSum+=pTreeNode->m_nValue;
path.push_back(pTreeNode->m_nValue);
//ifthenodeisaleaf,andthesumissameaspre-defined,
//thepathiswhatwewant.printthepath
boolisLeaf=(!
pTreeNode->m_pLeft&&!
pTreeNode->m_pRight);
if(currentSum==expectedSum&&isLeaf)
{
std:
:
vector:
:
iteratoriter=path.begin();
for(;iter!
=path.end();++iter)
std:
:
cout<<*iter<<'\t';
std:
:
cout<:
endl;
}
//ifthenodeisnotaleaf,gotoitschildren
if(pTreeNode->m_pLeft)
FindPath(pTreeNode->m_pLeft,expectedSum,path,currentSum);
if(pTreeNode->m_pRight)
FindPath(pTreeNode->m_pRight,expectedSum,path,currentSum);
//whenwefinishvisitinganodeandreturntoitsparentnode,
//weshoulddeletethisnodefromthepathand
//minusthenode'svaluefromthecurrentsum
currentSum-=pTreeNode->m_nValue;
path.pop_back();
}
5.查找最小的k个元素
题目:
输入n个整数,输出其中最小的k个。
例如输入1,2,3,4,5,6,7和8这8个数字,
则最小的4个数字为1,2,3和4。
//July2010/10/18
//引用自116楼wocaoqwer的回复。
#include
usingnamespacestd;
classMinK{
public:
MinK(int*arr,intsi):
array(arr),size(si){}
boolkmin(intk,int*&ret){
if(k>size)
{
ret=NULL;
returnfalse;
}
else
{
ret=newint[k--];
inti;
for(i=0;i<=k;++i)
ret[i]=array[i];
for(intj=(k-1)/2;j>=0;--j)
shiftDown(ret,j,k);
for(;iif(array[i]{
ret[0]=array[i];
shiftDown(ret,0,k);
}
returntrue;
}
}
voidremove(int*&ret){
delete[]ret;
ret=NULL;
}
private:
voidshiftDown(int*ret,intpos,intlength){
intt=ret[pos];
for(ints=2*pos+1;s<=length;s=2*s+1){
if(s++s;
if(t{
ret[pos]=ret[s];
pos=s;
}
elsebreak;
}
ret[pos]=t;
}
int*array;
intsize;
};
intmain()
{
intarray[]={1,2,3,4,5,6,7,8};
MinKmink(array,sizeof(array)/sizeof(array[0]));
int*ret;
intk=4;
if(mink.kmin(k,ret))
{
for(inti=0;icout<mink.remove(ret);
}
return0;
}
/////////////////////////////////////////
运行结果:
4
2
3
1
Pressanykeytocontinue
/////////////////////////////////////////
---------------------
第5题,也可以:
用类似快排的partition的方法,只求2边中的一边,在O(N)时间得到第k大的元素v;
再扫描一遍(O(N)),得到所有小于等于v的元素。
其中,快速排序每一趟比较用时O(n),要进行lgn次比较,才最终完成整个排序。
所以快排的复杂度才为O(n*lgn)。
像现在,只需要,找到第k大的元素,排一趟是O(n),排俩趟是O(2n),
其中第二趟,可以在第一趟后选择排序哪边,即只要排一边了,
递归找到第k大的元素,是O(N)的复杂度,最后遍历那一边O(k),输出这k个元素,即可。
所以,总的运行时间复杂度为O(N)。
(如果,要有序输出的话,则再将那k的元素排序,时间复杂度为O(N+klgk)。
)
-------------------------------------------------
第5题,再具体阐述下:
类似快排中的分割算法(如果不清楚快速排序,请参考本BLOG内关于快速排序的倆篇文章):
每次分割后都能返回枢纽点在数组中的位置s,然后比较s与k的大小
若大的话,则再次递归划分array[s..n],
小的话,就递归array[left...s-1]//s为中间枢纽点元素。
否则返回array[s],就是partition中返回的值。
//就是要找到这个s。
找到符合要求的s值后,再遍历输出比s小的那一边的元素。
代码实现:
//引自飞羽。
intkth_elem(inta[],intlow,inthigh,intk)
{
intpivot=a[low];
intlow_temp=low;
inthigh_temp=high;
while(low{
while(lowpivot)
--high;
a[low]=a[high];
while(low++low;
a[high]=a[low];
}
a[low]=pivot;
//上面为快排的那个分割算法
//以下就是主要思想中所述的内容
if(low==k-1)//找k值
returna[low];
elseif(low>k-1)
returnkth_elem(a,low_temp,low-1,k);
else
returnkth_elem(a,low+1,high_temp,k);
}
----------------------------------------
后序补充:
关于分治思想:
如果第一次分划,找到的第s小符合要求m,则停止,
如果找到的第s小,s如果找到的第s小,s>m,则到s的左边继续找。
举例说明:
213645
假设第一次划分之后,3为中间元素:
1、如果要求找第3小的元素,而找到的s=m(要求),则返回3
2、如果要求找第1小的元素,而找到的s>m,则在s的左边找
3、如果要求找第4小的元素,而找到的s在算法导论上,第九章中,以期望线性时间做选择,一节中,
我找到了这个寻找数组中第k小的元素的,平均时间复杂度为O(N)的证明。
中文版是第110页,英文版约是第164页。
不是一趟O(N),而是最终找到那第k个数,然后将其输出,的复杂度为O(N)。
(或者,O(n+k),最后遍历那k个元素输出。
)。
利用的正是上述类似快排中的分治算法,而与快速排序不同的是:
即快速排序要递归处理划分后的俩边,而此上述程序,即本题需处理划分之后的一边。
所以,快速排序的期望运行时间为O(nlgn),
而上述程序的期望运行时间,最后证明可得O(n),且假定元素是不同的。
第6题
------------------------------------
腾讯面试题:
给你10分钟时间,根据上排给出十个数,在其下排填出对应的十个数
要求下排每个数都是先前上排那十个数在下排出现的次数。
上排的十个数如下:
【0,1,2,3,4,5,6,7,8,9】
初看此题,貌似很难,10分钟过去了,可能有的人,题目都还没看懂。
举一个例子,
数值:
0,1,2,3,4,5,6,7,8,9
分配:
6,2,1,0,0,0,1,0,0,0
0在下排出现了6次,1在下排出现了2次,
2在下排出现了1次,3在下排出现了0次....
以此类推..
//引用自July2010年10月18日。
//数值:
0,1,2,3,4,5,6,7,8,9
//分配:
6,2,1,0,0,0,1,0,0,0
#include
#definelen10
classNumberTB
{
private:
inttop[len];
intbottom[len];
boolsuccess;
public:
NumberTB();
int*getBottom();
voidsetNextBottom();
intgetFrequecy(intnum);
};
NumberTB:
:
NumberTB()
{
success=false;
//formattop
for(inti=0;i{
top[i]=i;
}
}
int*NumberTB:
:
getBottom()
{
inti=0;
while(!
success)
{
i++;
setNextBottom();
}
returnbottom;
}
//setnextbottom
voidNumberTB:
:
setNextBottom()
{
boolreB=true;
for(inti=0;i{
intfrequecy=getFrequecy(i);
if(bottom[i]!
=frequecy)
{
bottom[i]=frequecy;
reB=false;
}
}
success=reB;
}
//getfrequencyinbottom
intNumberTB:
:
getFrequecy(intnum)//此处的num即指上排的数i
{
intcount=0;
for(inti=0;i{
if(bottom[i]==num)
count++;
}
returncount;//cout即对应frequecy
}
intmain()
{
NumberTBnTB;
int*result=nTB.getBottom();
for(inti=0;i{
cout<<*result++<}