《程序员面试精选2584.docx》由会员分享,可在线阅读,更多相关《程序员面试精选2584.docx(25页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。
1、程序员经典1双向链表表的查找节节点。考点点:双向链链表的操作作出现频率率:解析:使用riight指指针遍历,直直至找到数数据为daata的节节点,如果果找到节点点,返回节节点,否则则返回NUULL。11/查查找节点,成成功则返回回满足条件件的节点指指针,否则则返回NUULL2DbNoode*FinddNodee(DbNNode*heaad,iintddata)/参参数1是链链表的表头头节点3/参数2是是要查找的的节点,其其数据为ddata44DbNNode*pnoode=heaad;56iff(heead=NUULL)/链表表为空时返返回NULLL78reeturnnNULLL;91011/*
2、找到到数据或者者到达链表表末尾退出出whille循环*/12whille(ppnodee-riight!=NNULL&ppnodee-daata!=daata)113114pnnode=pnnode-rigght;/使用用righht指针遍遍历151617/没有有找到数据据为datta的节点点,返回NNULL118iff(pnnode-rigght=NUULL)119220reeturnnNULLL;21122223rreturrnpnnode;242考点:模模板的特化化的理解出出现频率:解解析:模板板的特化(ttempllatespeccialiizatiion)分分为两类:函数模板板的特化
3、和和类模板的的特化。(11)函数模模板的特化化:当函数数模板需要要对某些类类型进行特特别处理,称称为函数模模板的特化化。例如:1booolIIsEquual(TTt1,Ttt2)23rreturrnt11=t2;4456inntmaain()78charrstrr1=HHelloo;9charrstrr2=HHelloo;100couutIsEEquall(1,1)enndl;111cooutIssEquaal(sttr1,str22)enddl;/输出0012rreturrn0;13代码111行比较字字符串是否否相等。由由于对于传传入的参数数是chaar*类类型的,mmax函数数模板只是是简
4、单的比比较了传入入参数的值值,即两个个指针是否否相等,因因此这里打打印0。显显然,这与与我们的初初衷不符。因因此,maax函数模模板需要对对charr*类型型进行特别别处理,即即特化:11temmplatte2booolIIsEquual(cchar*t1,chaar*tt2)/函数模模板特化334retuurnsstrcmmp(t11,t22)=0;55这样样,当IssEquaal函数的的参数类型型为chaar*时时,就会调调用IsEEquall特化的版版本,而不不会再由函函数模板实实例化。(22)类模板板的特化:与函数模模板类似,当当类模板内内需要对某某些类型进进行特别处处理时,使使用类模
5、板板的特化。例例如:1tempplatee2cllasscomppare334publlic:55booolIssEquaal(Tt1,Tt22)67reeturnnt1=tt2;89;1011intmainn()122133chaarsttr1=Helllo;114chharsstr2=Helllo;15ccompaarecc1;166commpareec2;17ccoutcc1.IssEquaal(1,1)eendl;/比比较两个iint类型型的参数118cooutc22.IsEEquall(strr1,sstr2)endll;/比较两个个charr*类型型的参数119reeturnn0;
6、220这里代码118行也是是调用模板板类commparee的IsEEquall进行两个个字符串比比较,显然然这里存在在的问题和和上面函数数模板中的的一样,我我们需要比比较两个字字符串的内内容,而不不是仅仅比比较两个字字符指针。因因此,需要要使用类模模板的特化化:1ttempllate2cclassscommparee/特特化(chhar*)34publlic:55booolIssEquaal(chhar*t1,charr*t22)67reeturnnstrrcmp(t1,t2)=00;/使用sttrcmpp比较字符符串89;注意:进进行类模板板的特化时时,需要特特化所有的的成员变量量及成员函函
7、数。3考点:双双向链表的的操作出现现频率:解解析:与测测长的方法法一样,使使用rigght指针针进行遍历历。1/打印整整个链表22voiidPrrintLList(DbNoode*headd)/参数为链链表的表头头节点34DDbNodde*ppnodee=NNULL;56iff(heead=NUULL)/heead为NNULL表表示链表空空78retuurn;99100pnoode=headd;11whille(ppnodee!=NULLL)1213prinntf(%d,pnnode-datta);114pnnode=pnnode-rigght;/使用用righht指针遍遍历1516prinn
8、tf( );174考点:类类模板的实实例化的理理解出现频频率:1tempplatee2cllassArraay334;5voiddfooo()667Arraayarrr1;88Arrrayaarr4,arrr5;9Arraayarrr2,arr33;10Arraayarrr6;111112HHowmmanyinsttanceesofftheetemmplattecllassArraaywiillggetiinstaantiaatedinsiidettheffuncttionfoo()A33B66C44D11解析:模板板类(teemplaatecclasss)的实例例个数是由由类型参数数的种类决
9、决定的。代代码7行和和9行实例例化的模板板类都是AArrayy,代码88行实例化化的模板类类是Arrray,代代码10行行实例化的的模板类是是Arraay。一共共是三个实实例。答案案:A5考点:双双向链表的的操作出现现频率:解解析:为了了得到双向向链表的长长度,需要要使用riight指指针进行遍遍历,直到到得到NUULL为止止。1/获取链链表的长度度2inntGeetLenngth(DbNoode*headd)/参数为链链表的表头头节点34iintccountt=11;5DDbNodde*ppnodee=NNULL;67iff(heead=NUULL)/heead为NNULL表表示链表空空89
10、retuurn00;1011pnodde=headd-riight;12wwhilee(pnnode!=NNULL)1314ppnodee=ppnodee-riight;/使使用rigght指针针遍历155couunt+;161718retuurnccountt;19更多精精彩内容,请请到“融智智技术学苑苑rzchhina”使用模板有什么缺点?如何避免?6考点:理解模板编程的缺陷出现频率:解析:templates(模板)是节省时间和避免代码重复的极好方法,我们可以只输入一个类模板,就能让编译器实例化所需要的很多个特定类及函数。类模板的成员函数只有被使用时才会被实例化,所以只有在每一个函数都在实
11、际中被使用时,我们才会得到这些函数。确实这是一个很重要的技术,但是如果不小心,使用模板可能会导致代码膨胀。什么是代码膨胀?请看下面的例子:1template2classA34public:5voidwork()67coutwork()endl;8coutnumendl;910;1112intmain()1314Av1;15Av2;16Av3;17Av4;18v1.work();19v2.work();20v3.work();21v4.work();22return0;23类模板A取得一个类型参数T,并且它还有一个类型为int的参数,一个非类型参数(non-typeparameter),与类型参
12、数相比,虽然非类型参数不是很通用,但他们是完全合法的。在本例中,由于num的不同,代码14到17行的调用将会生成了三个A的实例,然后在1821行又生成了不同的函数调用。虽然这些函数做了相同的事情(打印一个“work()”和num),但他们却有不同的二进制代码。这就是所说的由于模板导致的代码膨胀。也就是说,虽然源代码看上去紧凑而整洁,但是目标代码却臃肿而松散,会严重影响程序的运行效率。如何避免由于这种代码膨胀呢?有一个原则,就是把C+模板中与参数无关的代码分离出来。也就是让与参数无关的代码只有一份拷贝。对类模板A可以进行如下地修改:1template2classBase34public:5voi
13、dwork(intnum)67coutwork;8coutnumdata=data;6pnode-left=pnode-right=pnode;/创建新节点时7/让其前驱和后继指针都指向自身8returnpnode;91011/创建链表12DbNode*CreateList(inthead)/参数给出表头节点数据13/表头节点不作为存放有意义数据的节点14DbNode*pnode=(DbNode*)malloc(sizeof(DbNode);15pnode-data=head;16pnode-left=pnode-right=pnode;1718returnpnode;192021/插入新节点
14、,总是在表尾插入;返回表头节点22DbNode*AppendNode(DbNode*head,intdata)/参数1是链表的表头节点,23/参数2是要插入的节点,其数据为data24DbNode*node=CreateNode(data);/创建数据为data的新节点25DbNode*p=head,*q;2627while(p!=NULL)2829q=p;30p=p-right;3132q-right=node;33node-left=q;3435returnhead;36我们可以使用其中的CreateList()和AppendNode()来生成一个链表,下面是一个数据生成从0到9含有10个
15、节点的循环链表。1DbNode*head=CreateList(0);/生成表头,表头数据为023for(inti=1;i10;i+)45head=AppendNode(head,i);/添加9个节点,数据为从1到968考点:函数模板与类模板的基本概念和区别出现频率:解析:(1)什么是函数模板和类模板。函数模板是一种抽象函数定义,它代表一类同构函数。通过用户提供的具体参数,C+编译器在编译时刻能够将函数模板实例化,根据同一个模板创建出不同的具体函数,这些函数之间的不同之处主要在于函数内部一些数据类型的不同,而由模板创建的函数的使用方法与一般函数的使用方法相同。函数模板的定义格式如下:1temp
16、lateFunction_Definition其中,Function_Definition为函数定义;TYPE_LIST被称为类型参数表,是由系列代表类型的标识符组成的,其间用逗号分隔,这些标识符的通常风格是由大写字母组成,ARG_LIST称为变量表,其中含有由逗号分隔开的多个变量说明,相当于一般函数定义中的形式参数。前面例题中的max就是函数模板的一个例子,因此这里不再另外举例。C+提供的类模板是一种更高层次的抽象的类定义,用于使用相同代码创建不同类模板的定义与函数模板的定义类似,只是把函数摸板中的函数定义部分换作类说明,并对类的成员函数进行定义即可。在类说明中可以使用出现在TYPE_LIS
17、T中的各个类型标识以及出现在ARG_LIST中的各变量。1template2class3,例如我们需要定义一个表示平面的点(Point)类,这个类有两个成员变量分别表示横坐标和纵坐标,并且这两个坐标的类型可以是int、float、double等等类型。因此可能写出类似Point_int_int、Point_float_int、Point_float_float等这样的类。通过类模板,我们只需要写一个类。1#include2usingnamespacestd;34template5classPoint_T67public:8T1a;/成员a为T1类型9T2b;/成员b为T2类型10Point_T
18、():a(0),b(0)/默认构造函数11Point_T(T1ta,T2tb):a(ta),b(tb)/带参数的构造函数12Point_T&operator=(Point_T&pt);/赋值函数13friendPoint_Toperator+(Point_T&pt1,Point_T&pt2);/重载+14;1516template17Point_T&Point_T:operator=(Point_T&pt)/赋值函数1819this-a=pt.a;20this-b=pt.b;21return*this;222324template25Point_Toperator+(Point_T&pt1,P
19、oint_T&pt2)/重载+2627Point_Ttemp;28temp.a=pt1.a+pt2.a;/结果对象中的a和b分别为两个参数对象的各自a和b之和29temp.b=pt1.b+pt2.b;30returntemp;313233template34ostream&operator(ostream&out,Point_T&pt)/重载输出流操作符3536out(pt.a,;/输出(a,b)37outpt.b);38returnout;394041intmain()4243Point_TintPt1(1,2);/T1和T2都是int44Point_TintPt2(3,4);/T1和T2都
20、是int45Point_TfloatPt1(1.1f,2.2f);/T1和T2都是float46Point_TfloatPt2(3.3f,4.4f);/T1和T2都是float4748Point_TintTotalPt;49Point_TfloatTotalPt;5051intTotalPt=intPt1+intPt2;/类型为Point_T的对象相加52floatTotalPt=floatPt1+floatPt2;/类型为Point_T的对象相加5354coutintTotalPtendl;/输出Point_T的对象55coutfloatTotalPtendl;/输出Point_T的对象56
21、57return0;58Point_T类就是一个类模板,它的成员a和b分别为T1和T2类型,这里我们还实现了它的构造函数、赋值函数、“+”运算符的重载以及输出流操作符“”的重载。使用Point_T类非常方便,我们可以进行各种类型的组合。代码43、44行,定义了两个Point_T类的对象intPt1和intPt2,表明这两个对象的成员a和b都是int类型。代码45、46行,定义了两个Point_T类的对象floatPt1和floatPt2,表明这两个对象的成员a和b都是float类型。代码51行,对intPt1和intPt2进行对象加法,结果保存到intTotalPt中,此过程先调用“+”函数,
22、再调用了“=”函数。代码52行,与51行类似,只是相加的对象和结果对象都是Point_T类的对象。代码54、55行,输出对象intTotalPt和floatTotalPt的内容。可以看出,通过使用类模板Point_T我们可以创建不同的类,大大提高了代码的可维护性以及可重用性。有一些概念需要区别:函数模板与模板函数,类模板和模板类是不同的意思。函数模板的重点是模板,它表示的是一个模板,用来生产函数。例如前面例题的max是一个函数模板。而模板函数的重点是函数,它表示的是由一个模板生成而来的函数。例如max,max等都是模板函数。类模板和模板类的区别与上面的类似,类模板用于生产类,例如Point_T
23、就是一个类模板。而模板类是由一个模板生成而来的类,例如Point_T和Point_T都是模板类。(2)函数模板和类模板有什么区别。在面试例题1的程序代码中,我们在使用函数模板max时不一定要必须指明T的类型,函数模板max的实例化是由编译程序在处理函数调用时自动完成的,当调用max(1,2)时自动生成实例max,而调用max(1.1f,2.2f)时自动生成实例max。当然也可以显示指定T的类型。对于本例题的类模板Point_T来说,其实例化必须被显示地指定,比如Point_T、Point_T。答案:函数模板是一种抽象函数定义,它代表一类同构函数。类模板是一种更高层次的抽象的类定义。函数模板的实
24、例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。9约瑟夫问问题的解答答考点:循循环链表的的操作出现现频率:编编号为1,22,.,N的的N个人按按顺时针方方向围坐一一圈,每人人持有一个个密码(正正整数),一一开始任选选一个正整整数作为报报数上限值值M,从第第一个人开开始按顺时时针方向自自1开始顺顺序报数,报报到M时停停止报数。报报M的人出出列,将他他的密码作作为新的MM值,从他他在顺时针针方向上的的下一个人人开始重新新从1报数数,如此下下去,直至至所有人全全部出列为为止。试设设计一个程程序求出出出列顺序。解析:显然当有人退出圆圈后,报数的工作要从下一个
25、人开始继续,而剩下的人仍然是围成一个圆圈的,因此可以使用循环单链表,由于退出圆圈的工作对应着表中结点的删除操作,对于这种删除操作频繁的情况,选用效率较高的链表结构,为了程序指针每一次都指向一个具体的代表一个人的结点而不需要判断,链表不带头结点。所以,对于所有人围成的圆圈所对应的数据结构采用一个不带头结点的循环链表来描述。设头指针为p,并根据具体情况移动。为了记录退出的人的先后顺序,采用一个顺序表进行存储。程序结束后再输出依次退出的人的编号顺序。由于只记录各个结点的data值就可以,所以定义一个整型一维数组。如:intquitn;n为一个根据实际问题定义的一个足够大的整数。程序代码如下:1#in
26、clude2usingnamespacestd;34/*结构体和函数声明*/5typedefstructnode67intdata;8node*next;9node;1011node*node_create(intn);1213/构造节点数量为n的单向循环链表14node*node_create(intn)1516node*pRet=NULL;1718if(0!=n)1920intn_idx=1;21node*p_node=NULL;2223/*构造n个node*/24p_node=newnoden;25if(NULL=p_node)/申请内存失败,返回NULL2627returnNULL;2
27、829else3031memset(p_node,0,n*sizeof(node);/初始化内存3233pRet=p_node;34while(n_idxdata=n_idx;37p_node-next=p_node+1;38p_node=p_node-next;39n_idx+;4041p_node-data=n;42p_node-next=pRet;434445returnpRet;464748intmain()4950node*pList=NULL;51node*pIter=NULL;52intn=20;53intm=6;5455/*构造单向循环链表*/56pList=node_crea
28、te(n);5758/*Josephus循环取数*/59pIter=pList;60m%=n;61while(pIter!=pIter-next)6263inti=1;6465/*取到第m-1个节点*/66for(;inext;697071/*输出第m个节点的值*/72printf(%d,pIter-next-data);7374/*从链表中删除第m个节点*/75pIter-next=pIter-next-next;76pIter=pIter-next;7778printf(%d ,pIter-data);7980/*释放申请的空间*/81deletepList;82return0;8310举
29、例说说明什么是是泛型编程程考点:泛泛型编程的的基本概念念出现频率率:解析:泛型型编程指编编写完全一一般化并可可重复使用用的算法,其其效率与针针对某特定定数据类型型而设计的的算法相同同。所谓泛泛型,是指指具有在多多种数据类类型上皆可可操作的含含意,在CC+中实实际上就是是使用模板板实现。举举一个简单单的例子,比比如我们要要比较两个个数的大小小,这两个个数的类型型可能是iint,也也可能是ffloatt,还有可可能是dooublee。一般编编程时我们们可能这样样写函数(不不考虑使用用宏的情况况):1intmax(inta,iintbb)/参数和返返回值都是是int类类型23reeturnnab?a
30、:bb;456flloatmax(floaata,flooatbb)/参数和返返回值都是是floaat类型778retuurnaabb?a:b;9910011ddoubllemaax(dooubleea,doubblebb)/参数和返返回值都是是doubble类型型1213rreturrnab?a:b;144可以以看到,上上面写了33个重载函函数,他们们的区别仅仅仅只是类类型(参数数及返回值值)不同,而而函数体完完全一样。而如果使用模板,我们可以这样写:1template/class也可用typename替换2Tmax(Ta,Tb)/参数和返回值都是T类型34returnab?a:b;5这里的
31、class不代表对象的类,而是类型(可用typename替换)。这样max函数的各个参数以及返回值类型都为T,对于任意类型的两个数,我们都可以调用max求大小,测试代码如下:1intmain()23coutmax(1,2)endl;/隐式调用int类型的max4coutmax(1.1f,2.2f)endl;/隐式调用float类型的max5coutmax(1.11l,2.22l)endl;/隐式调用double类型的max6coutmax(A,C)endl;/隐式调用char类型的max7coutmax(1,2.0)”操作符(因为max函数体使用了这个操作符)。显然,使用泛型编程(模板)可以极
32、大地增加了代码的重用性。11考点:单链表的的操作出现现频率:已已知两个链链表heaad1和和headd2各自自有序,请请把它们合合并成一个个链表依然然有序。使使用非递归归方法以及及递归方法法。解析:首先介绍绍非递归方方法。因为为两个链表表headd1和hhead22都是有序序的,所以以我们只需需要找把较较短链表的的各个元素素有序的插插入到较长长的链表之之中就可以以了。源代代码如下:1noode*inseert_nnode(nodee*heead,nodee*ittem)/heead!=NUULL23nnode*p=heaad;4nodee*q=NUULL;/始终终指向p之之前的节点点56whh
33、ile(p-ddatadataa&p!=NULLL)78qq=pp;9pp=pp-neext;110111iff(p=hhead)/插插入到原头头节点之前前1213iitem-nexxt=p;144retturnitemm;1516/插入入到q与pp之间之间间17qq-neext=iteem;188iteem-nnext=p;19rreturrnheead;22022122/*两两个有序链链表进行合合并*/23nnode*merrge(nnode*heaad1,nodee*heead2)2425nnode*heaad;/合并后后的头指针针26nnode*p;227noode*nexttP;/指
34、向pp之后28829iif(headd1=NULLL)/有一一个链表为为空的情况况,直接返返回另一个个链表300311retturnheadd2;322333elsseiff(hhead22=NULLL)344355retturnheadd1;36637738/两个个链表都不不为空399if(lenggth(hhead11)=lenngth(headd2)/选取取较短的链链表40/这样进行行的插入次次数要少些些41hhead=heead1;42pp=hhead22;4344elsee4546hhead=heead2;47pp=hhead11;484950whille(p!=NNULL)5152
35、nnextPP=pp-neext;/保存存p的下一一个节点553heead=inssert_nodee(heaad,pp);/把p插插入到目标标链表中554p=neextP;/指指向将要插插入的下一一个节点55555657retuurnhhead;58这里innsertt_nodde()函函数是有序序的插入节节点,注意意与前面例例题中的函函数有区别别,这里它它传入的参参数是noode*类类型。然后后在merrge()函数中(代代码5255行)循循环把短链链表中的所所有节点插插入到长链链表中。接接下来介绍绍非递归方方法。比如如有下面两两个链表:链表1:1-33-5链链表2:22-4-6递归归方法的步步骤如下:(1)比比较链表11和链表22的第一个个节点数据据,由于115)和链表2再调用本过程,比较得到结果链表的第二个节点,即2与3比较得到2。此时合并后的链表节点为1-2。接下来的过程类似(2),如此递归知道两个链表的节点都被加到结果链表中。1node*MergeRecursive(node*head1,node*head2)23node*head=NULL
限制150内