??xml version="1.0" encoding="utf-8" standalone="yes"?> 在这文章:九月腾讯Q创新工场,淘宝{公司最新面试十三题?span style="font-size: 14px;">W??一个未排序整数数组Q有正负敎ͼ重新排列使负数排在正数前面,q且要求不改变原来的正负C间相寚w?Q自从去q九月收录了此题至今Q一直未曄Co人满意的{案Qؓ何呢? 因ؓ一般达不到题目所要求的:旉复杂度O(N),I间O(1)Q且保证原来正负C间的相对位置不变?/span>本编E艺术系列第27章就来阐q这个问题,若有M漏洞Q欢q随时不吝指正。谢谢?/span> 原题是这LQ?/span> 一个未排序整数数组Q有正负敎ͼ重新排列使负数排在正数前面,q且要求不改变原来的正负C间相寚w序?br />比如Q?input: 1,7,-5,9,-12,15 Qans: -5,-12,1,7,9,15 。且要求旉复杂度O(N),I间O(1) ?/span> OKQ下面咱们就来试着一步一步解q道题,如下5U思\Q从复杂度O(N^2)到O(N*logN)Q从不符合题目条件到一步步近于条?Q: 首先Q定义这样一个过Eؓ“{”Q?a1,a2,...,am,b1,b2,...,bn) --> (b1,b2,...,bn,a1,a2,...,am)。其ơ,对于待处理的未排序整数数l,从头到尾q行扫描Q寻?正正...正负...负负)Ԍ每找到这样一个串Q则计数器加1Q若计数为奇敎ͼ则对当前串做一?#8220;{”Q反复扫描,直到再也找不?正正...正负...负负)丌Ӏ?/p> 此思\来自朋友胡果果,I间复杂度虽为O(1)Q?/span>但其旉复杂度O(N*logN)?/span>更多具体l节参看原文Q?span style="font-size: 14px;">http://qing.weibo.com/1570303725/5d98eeed33000hcb.html。故Q不W合题目要求Ql寻找?/span> 如果无翻转点, 则不操作Q如果有{? 则待l止点出现后, 做翻? 即ab => ba q样的操作。翻转后, 负数串一定在左侧, 然后从负C的右侧开始记录v始点, l箋往下找下一个翻转点?/p> 例子中的是(下划U代表要交换序的两个数?Q?/p> 1, 7, -5, 9, -12, 15 10、翻转句子中单词的顺序?br />题目Q输入一个英文句子,{句子中单词的序Q但单词内字W的序不变。句子中单词以空格符隔开。ؓ单v见,标点W号和普通字母一样处理。例如输?#8220;I am a student.”Q则输出“student. a am I”?span style="font-family: 'Arial Black';">而此题可以在O(N)的时间复杂度内解?/span>Q?/p> ׃本题需要翻转句子,我们先颠倒句子中的所有字W。这Ӟ不但{了句子中单词的顺序,而且单词内字W也被翻转了。我们再颠倒每个单词内的字W。由于单词内的字W被{两次Q因此顺序仍然和输入时的序保持一致?br /> 以上面的输入ZQ翻?#8220;I am a student.”中所有字W得?#8220;.tneduts a ma I”Q再{每个单词中字W的序得到“students. a am I”Q正是符合要求的输出(~码实现Q可以参看此文:http://zhedahht.blog.163.com/blog/static/254111742007289205219/)?br /> 对的Q上q思\3是q个意思,单词{便相当于于区间翻转,既如此,׃来验证下上述思\2中那个测试用例,如下Q?/p> 1, 7, -5, -6Q?nbsp;9, -12, 15 但是Q我q想再问Q问题至此被解决了么?真的被KO了么?NOQ咱们来看这样一U情况,正如威士忌所_ 看看q个数据Q?-+-+-+-+------+Q假如Nminus {于 n/2Q由于前面都?-+-+-Q区间交换需?n/2/2 = n/4ơ,每次交换?T(2*(Nminus + Nplus)) >= T(n)Qn/4 * T(n) = T(n*n/4)=O(n^2)?/span> q有一U更坏的情况Q就?-+-+-+-+------+q种数据可能Q后面一大堆的负敎ͼ前面正负交替?span style="font-size: 14px;">所以,׃的美梦再ơ破灭,路O漫其修远兮,此题仍然未找C个完全解决了的方案?/span> 作者:July Jon BentleyQ?0%以上的程序员无法正确无误的写Z分查找代码。也许很多h都早已听说过q句话,但我q是惛_用《编E珠玑》上的如下几D|字: “二分查找可以解决Q?strong>预排序数l的查找Q问题:只要数组中包含TQ即要查扄|Q那么通过不断~小包含T的范_最l就可以扑ֈ它。一开始,范围覆盖整个数组。将数组的中间项与Tq行比较Q可以排除一半元素,范围~小一半。就q样反复比较Q反复羃范_最l就会在数组中找到TQ或者确定原以ؓT所在的范围实际为空。对于包含N个元素的表,整个查找q程大约要经qlog(2)Nơ比较?nbsp; 你能正确无误的写Z分查找代码么Q不妨一试?/p> 二分查找的原理想必不用多解释了,不过有一点必L醒读者的是,二分查找是针对的排好序的数组。OKQ纸上读来终觉浅Q觉知此事要w行。我先来写一份,下面是我写的一份二分查扄实现Q之前去某一家公叔R试也曾被叫当场实C分查找,不过l果可能跟你一P当时未能完整无误写出)Q有M问题或错误,恌不吝指正Q?br />
]]>W二十七章:不改变正负数之间相对序重新排列数组.旉O(N)Q空间O(1)
前言
重新排列使负数排在正数前?/span>
W一ơ翻? 1, 7, -5, -12,9, 15 => 1, -12, -5, 7, 9, 15
W二ơ翻? -5, -12, 1, 7, 9, 15
1, 7, -5, -6Q?nbsp;9, -12, 15Q此U情冉|能处理)
1 7 -5 -6 -12 9 15
1 -12 -5 -6 7 9 15
-6 -12 -5 1 7 9 15 (此时Q正负数之间的相寚w序已l改变,本应该是-5Q?6Q?12Q而现在是-6 -12 -5)思\5之区间翻?/span>
1 7 -5 -6 -12 9 15
-12 -6 -5 7 1 9 15 (借用单词{的方法,先逐个数字{Q后正负数整体原地翻?
-5 -6 -12 1 7 9 15思\5再次被质?/span>
]]>W二十五章:二分查找实现QJon BentleyQ?0%E序员无法正实玎ͼ
出处Q结构之法算法之?/p>引言
多数E序员都觉得只要理解了上面的描述Q写Z码就不难了;但事实ƈ非如此。如果你不认同这一点,最好的办法是放下书本Q自己动手写一写。试试吧?nbsp;
我在贝尔实验室和IBM的时候都q道考题。那些专业的E序员有几个时的时_可以用他们选择的语a把上面的描述写出来;写出高伪代码也可以。考试l束后,差不多所有程序员都认己写Z正确的程序。于是,我们׃半个钟头来看他们~写的代码经q测试用例验证的l果。几ơ课Q一癑֤人的l果相差无几Q?0%的程序员写的E序中有bugQ我q不认ؓ没有bug的代码就正确Q?nbsp;
我很惊讶Q在_的时间内Q只有大U?0%的专业程序员可以把这个小E序写对。但写不对这个小E序的还不止q些人:高dU_《计机E序设计的艺?W??排序和查找》第6.2.1节的“历史与参考文?#8221;部分指出Q虽然早?946q就有h二分查扄Ҏ公诸于世Q但直到1962q才有h写出没有bug的二分查扄序?span style="font-size: 24px;">”——乔恩·本特利,《编E珠玑(W?版)》第35-36c?/p>二分查找代码
//copyright@2011 July
//随时Ƣ迎读者找bugQemailQzhoulei0907@yahoo.cn?nbsp;
//首先要把握下面几个要点:
//right=n-1 => while(left <= right) => right=middle-1;
//right=n => while(left < right) => right=middle;
//middle的计不能写在while循环外,否则无法得到更新?nbsp;
int binary_search(int array[],int n,int value)
{
int left=0;
int right=n-1;
//如果q里是int right = n 的话Q那么下面有两处地方需要修改,以保证一一对应Q?nbsp;
//1、下面@环的条g则是while(left < right)
//2、@环内当array[middle]>value 的时候,right = mid
while (left<=right) //循环条gQ适时而变
{
int middle=left + ((right-left)>>1); //防止溢出Q移位也更高效。同Ӟ每次循环都需要更新?nbsp;
if (array[middle]>value)
{
right =middle-1; //right赋|适时而变
}
else if(array[middle]<value)
{
left=middle+1;
}
else
return middle;
//可能会有读者认为刚开始时p判断相等Q但毕竟数组中不相等的情冉|?nbsp;
//如果每次循环都判断一下是否相{,耗费旉
}
return -1;
}
E序员编E艺术第十一章:最长公共子序列(LCS)问题
E序员编E艺术系列重新开始创作了Q前十章Q请参?a target="_blank" style="color: #336699; text-decoration: initial;">E序员编E艺术第一~十章集锦与ȝQ。回之前的前十章,有些代码是值得商榷的,因当时的代码只顾阐述法的原理或思想Q所以,很多的与代码规范相关的问题都未能做到完美。日后,会着力修~之?/p>
搜遍|上Q讲解这个LCS问题的文章不计其敎ͼ但大多给读者一Uƈ不友好的感觉Q稍感晦涩,且代码也不够清晰。本文力N免此些情c力保通俗Q阐q详。同Ӟl典法研究pd的第三章Q?a target="_blank" style="color: #336699; text-decoration: initial;">三、dynamic programmingQ也了此LCS问题。有M问题Q欢q不吝赐教?/p>
什么是最长公共子序列?好比一个数?nbsp;SQ如果分别是两个或多个已知数列的子序列,且是所有符合此条g序列中最长的Q则S UCؓ已知序列的最长公共子序列?/p>
举个例子Q如Q有两条随机序列Q如 1 3 4 5 5 Qand 2 4 5 5 7 6Q则它们的最长公共子序列便是Q? 5 5?/p>
注意最长公共子ԌLongest CommonSubstringQ和最长公共子序列QLongestCommon Subsequence, LCSQ的区别Q子ԌSubstringQ是串的一个连l的部分Q子序列QSubsequenceQ则是从不改变序列的序Q而从序列中去掉Q意的元素而获得的新序列;更简略地_前者(子串Q的字符的位|必连l,后者(子序列LCSQ则不必。比如字W串acdfg同akdfc的最长公共子串ؓdfQ而他们的最长公共子序列是adf。LCS可以使用动态规划法解决。下文具体描q?/p>
解最长公共子序列问题时最Ҏ惛_的算法是ID搜烦法,卛_X的每一个子序列Q检查它是否也是Y的子序列Q从而确定它是否为X和Y的公共子序列Qƈ且在查过E中选出最长的公共子序列。X和Y的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的一个子序列相应于下标序列{1, 2, …, m}的一个子序列Q因此,X共有2m个不同子序列QY亦如此,如ؓ2^nQ,从而穷举搜索法需要指数时_2^m * 2^nQ?/p>
事实上,最长公共子序列问题也有最优子l构性质?/p>
?
Xi=Hx1Q?#8943;QxiH即X序列的前i个字W?(1≤i≤m)Q前~Q?/p>
Yj=Hy1Q?#8943;QyjH即Y序列的前j个字W?(1≤j≤n)Q前~Q?/p>
假定Z=Hz1Q?#8943;QzkH?#8712;LCS(X , Y)?/p>
?strong>xm=ynQ最后一个字W相同)Q则不难用反证法证明Q该字符必是X与Y的Q一最长公共子序列ZQ设长度为kQ的最后一个字W,xzk = xm = yn 且显然有Zk-1∈LCS(Xm-1 , Yn-1)即Z的前~Zk-1是Xm-1与Yn-1的最长公共子序列。此Ӟ问题化归成求Xm-1与Yn-1的LCSQ?em>LCS(X , Y)的长度等于LCS(Xm-1 , Yn-1)的长度加1Q?/p>
?strong>xm≠ynQ则亦不隄反证法证明:要么Z∈LCS(Xm-1, Y)Q要么Z∈LCS(X , Yn-1)。由于zk≠xm与zk≠yn其中臛_有一个必成立Q若zk≠xm则有Z∈LCS(Xm-1 , Y)Q类似的Q若zk≠yn 则有Z∈LCS(X , Yn-1)。此Ӟ问题化归成求Xm-1与Y的LCS及X与Yn-1的LCS。LCS(X , Y)的长度ؓQmax{LCS(Xm-1 , Y)的长? LCS(X , Yn-1)的长度}?/p>
׃上述?strong>xm≠yn的情况中Q求LCS(Xm-1 , Y)的长度与LCS(X , Yn-1)的长度,q两个问题不是相互独立的Q两者都需要求LCS(Xm-1QYn-1)的长度。另外两个序列的LCS中包含了两个序列的前~的LCSQ故问题h最优子l构性质考虑用动态规划法?/p>
也就是说Q解册个LCS问题Q你要求三个斚w的东西:1、LCSQXm-1QYn-1Q?1Q?strong>2、LCSQXm-1QYQ,LCSQXQYn-1Q;3、max{LCSQXm-1QYQ,LCSQXQYn-1Q}?/p>
行文xQ其实对q个LCS的动态规划解法已叙述D尽Q不q,Z成书的某U必要性,下面Q我试着再多加详l阐q这个问题?/p>
3.1、最长公共子序列的结?/strong>
最长公共子序列的结构有如下表示Q?/p>
讑ֺ列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>Q则Q?/p>
其中Xm-1=<x1, x2, …, xm-1>QYn-1=<y1, y2, …, yn-1>QZk-1=<z1, z2, …, zk-1>?/p>
由最长公共子序列问题的最优子l构性质可知Q要扑ևX=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列Q可按以下方式递归地进行:当xm=ynӞ扑ևXm-1和Yn-1的最长公共子序列Q然后在其尾部加上xm(=yn)卛_得X和Y的一个最长公共子序列。当xm≠ynӞ必须解两个子问题Q即扑ևXm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列?/p>
由此递归l构Ҏ看到最长公共子序列问题h子问题重叠性质。例如,在计X和Y的最长公共子序列Ӟ可能要计出X和Yn-1及Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题Q即计算Xm-1和Yn-1的最长公共子序列?/p>
与矩阵连乘积最优计次序问题类|我们来徏立子问题的最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>QYj=<y1, y2, …, yj>。当i=0或j=0ӞI序列是Xi和Yj的最长公共子序列Q故c[i,j]=0。其他情况下Q由定理可徏立递归关系如下Q?br />
直接利用上节节末的递归式,我们很Ҏp写出一个计c[i,j]的递归法Q但其计时间是随输入长度指数增长的。由于在所考虑的子问题I间中,d只有θ(m*n)个不同的子问题,因此Q用动态规划算法自底向上地计算最优D提高法的效率?/p>
计算最长公共子序列长度的动态规划算法LCS_LENGTH(X,Y)以序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>作ؓ输入。输Z个数lc[0..m ,0..n]和b[1..m ,1..n]。其中c[i,j]存储Xi与Yj的最长公共子序列的长度,b[i,j]记录指示c[i,j]的值是由哪一个子问题的解辑ֈ的,q在构造最长公共子序列时要用到。最后,X和Y的最长公共子序列的长度记录于c[m,n]中?/p>
q法LCS_LENGTH计算得到的数lb可用于快速构造序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列。首先从b[m,n]开始,沿着其中的箭头所指的方向在数lb中搜索?/p>
q种Ҏ是按照反序来找LCS的每一个元素的。由于每个数l单元的计算耗费Ο(1)旉Q算法LCS_LENGTH耗时Ο(mn)?/p>
下面的算法LCS(b,X,i,j)实现Ҏb的内Ҏ印出Xi与Yj的最长公共子序列。通过法的调用LCS(b,X,length[X],length[Y])Q便可打印出序列X和Y的最长公共子序列?/p>
在算法LCS中,每一ơ的递归调用使i或j?Q因此算法的计算旉?em>O(m+n)?/p>
例如Q设所l的两个序列为X=<AQBQCQBQDQAQB>和Y=<BQDQCQAQBQA>。由法LCS_LENGTH和LCS计算出的l果如下图所C:
我来说明下此图(参考算法导论)。在序列X={AQBQCQBQDQAQB}?Y={BQDQCQAQBQA}上,由LCS_LENGTH计算出的表c和b。第i行和Wj列中的方块包含了c[iQj]的g及指向b[iQj]的箭头。在c[7,6]的项4Q表的右下角为X和Y的一个LCS<BQCQBQA>的长度。对于iQj>0Q项c[iQj]仅依赖于是否有xi=yiQ及c[i-1Qj]和c[iQj-1]的|q几个项都在c[iQj]之前计算。ؓ了重构一个LCS的元素,从右下角开始跟tb[iQj]的箭头即可,q条路径标示为阴影,q条路径上的每一?#8220;↖”对应于一个xi=yiZ个LCS的成员的(高亮标示Q?/p>
所以根据上q图所C的l果Q程序将最l输出:“B C B A”?/p>
对于一个具体问题,按照一般的法设计{略设计出的法Q往往在算法的旉和空间需求上q可以改q。这U改q,通常是利用具体问题的一些特D性?/p>
例如Q在法LCS_LENGTH和LCS中,可进一步将数组b省去。事实上Q数l元素c[i,j]的g由c[i-1,j-1]Qc[i-1,j]和c[i,j-1]三个g一定Q而数l元素b[i,j]也只是用来指Cc[i,j]I竟由哪个值确定。因此,在算法LCS中,我们可以不借助于数lb而借助于数lc本n临时判断c[i,j]的值是由c[i-1,j-1]Qc[i-1,j]和c[i,j-1]中哪一个数值元素所定Q代hΟ(1)旉。既然b对于法LCS不是必要的,那么法LCS_LENGTH便不必保存它。这一来,可节?em>θ(mn)的空_而LCS_LENGTH和LCS所需要的旉分别仍然?em>Ο(mn)?em>Ο(m+n)。不q,׃数组c仍需?em>Ο(mn)的空_因此q里所作的改进Q只是在I间复杂性的常数因子上的改进?/p>
另外Q如果只需要计最长公共子序列的长度,则算法的I间需求还可大大减。事实上Q在计算c[i,j]Ӟ只用到数lc的第i行和Wi-1行。因此,只要?行的数组I间可以计出最长公共子序列的长度。更q一步的分析q可空间需求减至min(m, n)?/p>
动态规划的一个计最长公共子序列的方法如下,以两个序?nbsp;X?em>Y Z子:
设有二维数组 f[i][j] 表示 X ?nbsp;i 位和 Y ?nbsp;j 位之前的最长公共子序列的长度,则有Q?/p>
其中Q?em>same(a,b)?nbsp;X 的第 a 位与 Y 的第 b 位完全相同时?#8220;1”Q否则ؓ“0”?/p>
此时Q?em>f[i][j]中最大的C?nbsp;X ?nbsp;Y 的最长公共子序列的长度,依据该数l回溯,便可扑և最长公共子序列?/p>
该算法的I间、时间复杂度均ؓO(n2)Q经q优化后Q空间复杂度可ؓO(n)Q时间复杂度?em>O(nlogn)?/p>
以下是此法的java代码Q?/p>
下面׃来了解一U不同于动态规划法的一U新的求解最长公共子序列问题的方?该算法主要是把求解公共字W串问题转化为求解矩阵L(p,m)的问题,在利用定理求解矩늚元素q程中(1Qwhile(i<k),L(k,i)=nullQ?br /> Q?Qwhile(L(k,i)=k),L(k,i+1)=L(k,i+2)=…L(k,m)=kQ?/p>
求出每列元素Q一直到发现Wp+1 行都为null 旉出@环,得出矩阵L(k,m)后,B[L(1,m-p+1)]B[L(2,m-p+2)]…B[L(p,m)]即ؓA 和B 的LCSQ其中p 为LCS 的长度?/p>
6.1 主要定义及定?/strong>
矩阵中元素L(kQi)=Li(k)Q这?1<i<=mQ?<k<=m)Qnull 表示L(k,i)不存在。当i<k Ӟ昄L(kQi)不存在?br /> 设p=Maxk(L(k Q?m) ≠ null) Q?可以证明L 矩阵中L(p,m) 所在的对角U?L(1,m-p+1),L(2,m-p+2)…L(p-1,m-1),L(p,m) 所对应的子序列B[L(1,m-p+1)]B[L(2,m-p+2)]…B[L(p,m)]即ؓA 和B 的LCSQp LCS 的长度。这PLCS 问题的求解就转化为对m m L × 矩阵的求解?br />
6.2 法思想
Ҏ定理,W一步求出第一行元?L(1,1),L(1,2),…L(1,m),W二步求W二?一直到发现Wp+1 行都为null 为止。在计算q程中遇到i<k ?L(k,i)=null, 及L(k,i)=k?L(k,i+1)=L(k,i+2)=…L(k,m)=k。这?计算每行的时间复杂度为O(n),则整个时间复杂度为O(pn)。在求L 矩阵的过E中不用存储整个矩阵,只需存储当前行和上一行即可。空间复杂度为O(m+n)?/p>
下面l出一个例子来说明:l定字符串A 和BQA=acdabbcQB=cddbacabaQ?m= A =7Qn= B =9)。按照定理给出的递推公式Q求出A 和B 的L 矩阵如图2Q其中的$表示NULL?br />
则A 和B 的LCS 为B[1]B[2]B[4]B[6]=cdbc,LCS 的长度ؓ4?/p>
6.3 法伪代?br />法 L(A,B,L)
输入 长度分别为m,n 的字W串A,B
输出 A,B 的最长公共子序列LCS
6.4 l语
本节主要描述区别于动态规划法的一U新的求解最长公共子序列问题的方法,在不影响_度的前提下,提高序列匚w的速度Q根据定理i 1 L + (k)=Min(j, i L (k))得出矩阵Q在求解矩阵的过E中Ҏ耗时的L(p,m)q行条gU束优化。我们在Intel(R) Core(TM)2 Quad 双核处理器?G 内存QY件环境:windows xp 下试验结果证明,本文法与其他经典的比对法相比,不但能够取得准确的结?而且速度有了较大的提高(本节参考了刘佳梅女士的论文Q?/p>
若有M问题Q恳请不吝指正。谢谢各位。完?/p>
前奏
有这样一个问题:在一条左xqx|的直线轨道上Q选两个点Q放|两个机器hQ请用如下指令系lؓ机器计控制程序,使这两个机器够在直线轨道上相遇。(注意两个机器人用你写的同一个程序来控制Q?br /> 指opȝQ只包含4条指令,向左、向叟뀁条件判定、无条g跌{。其中向左(叻I指o每次能控制机器h向左Q右Q移动一步;条g判定指o能对机器人所在的位置q行条g试Q测试结果是如果Ҏ机器人曾l到q这里就q回trueQ否则返回falseQ无条g跌{Q类似汇~里面的跌{Q可以蟩转到M地方?/p>
okQ这道很有意思的味题是d微Y工程院的题,文末给{(如果急切想知道此问题的答案,可以直接跛_本文W三节)。同Ӟ我们看到其实q个题是一个典型的q赶问题Q那么追赉题在哪种面试题中比较常见?对了Q链表追赶。本章就来阐q这个问题。有不正之处Q望不吝指正?/p>
分析Q?/strong>此题一出,怿Q稍微有?l验的同志,都会说到Q设|两个指针p1,p2Q首先p1和p2都指向headQ然后p2向前走k步,q样p1和p2之间间隔k个节点,最后p1和p2同时向前UdQ直至p2走到链表末尾?/p> 前几日有朋友提醒我说Q让我讲一下此U求链表倒数Wk个结点的问题。我惻Iq种问题Q有点经验的人恐怕都已了解过Q无非是利用两个指针一前一后逐步前移。但他提醒我_如果参加面试的h没有q个意识Q它怎么也想不到那里厅R?/p> 那在qx准备面试的过E中如何加强q一斚w的意识呢?我想Q除了^旉C道面试题Q尽可能用多U思\解决Q以延自己的视野之外,便是qx有意注意观察生活。因为,怿Q你很容易了解到Q其实这U链表追赶的问题来源于生zM长跑比赛Q如果^时注意多多思考,多多U篏Q多多发现ƈ体味生活Q相信也会对面试有所帮助?/p> okQ扯多了Q下面给个题目的M代码Q如下: struct ListNode //@heyaming, W一?求链表倒数Wk个结点应该考虑k大于链表长度的case?br />ListNode* fun(ListNode *head,int k) 扩展Q?/strong> 比如h1、h2Q判断这两个链表是否怺。这里ؓ了简化问题,我们假设两个链表均不带环?/p> 分析Q?/strong>q是来自~程之美上的微Y亚院的一道面试题目。请跟着我的思\步步深入Q部分文字引自编E之)Q?/p> ȝQ?/strong> 1?/strong>那么Q如何编写代码来判断链表是否有环?因ؓ很多的时候,你给Z问题的思\后,面试官可能还要追加你的代码,okQ如下(讄两个指针(p1, p2)Q初始值都指向_p1每次前进一步,p2每次前进二步Q如果链表存在环Q则p2先进入环Qp1后进入环Q两个指针在环中走动Q必定相遇)Q?/p> 2&3?/strong>如果都不带环Q就判断节Ҏ否相{,如果都带环,判断一链表上俩指针盔R的那个节点,在不在另一条链表上。下面是l合解决q个问题的代码: 扩展2Q求两个链表怺的第一个节?/strong> @JoshuaQ这个算法需要处理一U特D情况,卻I其中一个链表的头结点在另一个链表的环中Q且不是环入口结炏V这U情冉|两种意思:1)如果其中一个链表是循环链表Q则另一个链表必为@环链表,即两个链表重合但头结点不同;2)如果其中一个链表存在环(除去循环链表q种情况)Q则另一个链表必在此环中与此环重合,其头l点为环中的一个结点,但不是入口结炏V在q种情况下我们约定,如果链表B的头l点在链表A的环中,且不是环入口l点Q那么链表B的头l点即作为A和B的第一个相交结点;如果A和B重合(定义Ҏ时Ş参A在B之前)Q则取B的头l点作ؓA和B的第一个相交结炏V?nbsp; 代码如下Q?/p> 关于判断单链表是否相交的问题Q还可以看看此篇文章Q?a href="http://m.shnenglu.com/humanchao/archive/2008/04/17/47357.html" target="_blank" style="color: #336699; text-decoration: initial;">http://m.shnenglu.com/humanchao/archive/2008/04/17/47357.html。okQ下面,回到本章前奏部分的那道非常有味的智力题?/p> W三节、微软工E院面试智力?/span> 分析Q?/strong>我尽量以最清晰的方式来说明q个问题Q大部分内容来自ivanQbig{h的讨论)Q?br /> 1?/strong>首先题目要求很简单,是要你惛_法让A最l能赶上BQA在后QB在前Q都向右UdQ如果它们的速度永远一_那A是永q无法追赶上B的。但题目l出了一个条件判断指令,卛_果A或B某个机器人向前移动时Q若是某个机器hl过的点是第二个机器人曾l经q的点,那么E序q回true。对的,是抓住q一点,A到达曄Bl过的点后,发现此后的\是B此前l过的,那么A开始提速两倍,B一直保持原来的一倍速度不变Q那L话,A势必会在|AB|/move_right个单位时间内Q追上B。okQ简单伪代码如下Q?/p> start: 再简单解释下上面的伪代码Q@bigQ: 2?/strong>有个l节又出CQ正如ivan所_ if(at the position other robots have reached) 上面q个分支不一定能提速的。why?因ؓ如果if条gq旉很少Q而move指o发的旉很大Q实际很可能是这PQ那么两个机器h的速度q是基本是一L?/p> 那作如何修改?: start: ------- q样改后QA的速度应该比B快了?/p> 3?/strong>然要是说每个指o处理速度都很快,AB岂不是一直以相同的速度右移?那到底该作何修改?LQ?/p> go_step() go_2step() 一个时间单向右一步向左和向右q旉是同LQƈ且会占用一定时间?如果条g判定指o旉比移令花的时间较的话,应该上面两种步法Q后者比前者快。至此,׃的问题已l得到解冟?/p>
W一节、求链表倒数Wk个结?/span>
W?3题、题目描qͼ
输入一个单向链表,输出该链表中倒数Wk个结?
链表的倒数W?个结点ؓ链表的尾指针?/p>
{
char data;
ListNode* next;
};
ListNode* head,*p,*q;
ListNode *pone,*ptwo;
{
assert(k >= 0);
pone = ptwo = head;
for( ; k > 0 && ptwo != NULL; k--)
ptwo=ptwo->next;
if (k > 0) return NULL;
while(ptwo!=NULL)
{
pone=pone->next;
ptwo=ptwo->next;
}
return pone;
}
q是针对链表单项链表查找其中倒数Wk个结炏V试问,如果链表是双向的Q且可能存在环呢?LW二节、编E判断两个链表是否相交?br />
W二节、编E判断两个链表是否相?/span>
题目描述Q给Z个单向链表的头指针(如下图所C)
q能扑ֈ最后一个结点进行判断么?上面的方法还同样有效?昄Q这个问题的本质已经转化为判断链表是否有环。那么,如何来判断链表是否有环呢?
所以,事实上,q个判断两个链表是否怺的问题就转化成了Q?br />1.先判断带不带?br />2.如果都不带环Q就判断节Ҏ否相{?br />3.如果都带环,判断一链表上俩指针盔R的那个节点,在不在另一条链表上?br />如果在,则相交,如果不在Q则不相交?/p>
思\Q在判断是否怺的过E中要分别遍历两个链表,同时记录下各自长度?/p>
发g? "風過無痕" <luxiaoxun001@qq.com>发件hd到联pMh
收g? "zhoulei0907" <zhoulei0907@yahoo.cn>
你好
看到你在csdn上博客,学习了很多,看到下面一章,有个扩展问题没有代码Q发现自己有个,发给你吧Q思\和别人提出来的一P感觉有代码更加完善一些,呵呵
扩展2Q求两个链表怺的第一个节?br /> 思\Q如果两个尾l点是一LQ说明它们有重合Q否则两个链表没有公ql点?br /> 在上面的思\中,序遍历两个链表到尾l点的时候,我们不能保证在两个链表上同时到达炏V这是因Z个链表不一定长度一栗但如果假设一个链表比另一个长L个结点,我们先在长的链表上遍历L个结点,之后再同步遍历,q个时候我们就能保证同时到达最后一个结点了。由于两个链表从W一个公q点开始到链表的尾l点Q这一部分是重合的。因此,它们肯定也是同时到达W一公共l点的。于是在遍历中,W一个相同的l点是W一个公ql点?br /> 在这个思\中,我们先要分别遍历两个链表得到它们的长度,q求Z个长度之差。在长的链表上先遍历若干ơ之后,再同步遍历两个链表,直到扑ֈ相同的结点,或者一直到链表l束。PSQ没有处理一U特D情况:是一个是循环链表Q而另一个也是,只是头结Ҏ在位|不一栗?nbsp;
题目描述Q?/strong>
在一条左xqx|的直线轨道上Q选两个点Q放|两个机器hQ请用如下指令系lؓ机器计控制程序,使这两个机器够在直线轨道上相遇。(注意两个机器人用你写的同一个程序来控制Q?br /> 指opȝQ只包含4条指令,向左、向叟뀁条件判定、无条g跌{。其中向左(叻I指o每次能控制机器h向左Q右Q移动一步;条g判定指o能对机器人所在的位置q行条g试Q测试结果是如果Ҏ机器人曾l到q这里就q回trueQ否则返回falseQ无条g跌{Q类似汇~里面的跌{Q可以蟩转到M地方?/p>
if(at the position other robots have not reached)
move_right
if(at the position other robots have reached)
move_right
move_right
goto start
A------------B
| |
在A到达B点前Q两者都只有W一条if为真Q即以相同的速度向右UdQ在A到达B后,A只满第二个ifQ即以两倍的速度向右UdQB依然只满第一个ifQ则速度保持不变Q经q|AB|/move_right个单位时_A可以追上B?/p>
move_right
move_right
if(at the position other robots have not reached)
move_right
move_left
move_right
if(at the position other robots have reached)
move_right
goto start
{
向右
向左
向右
}
--------
三个旉单位才向右一?/p>
{
向右
}
------
]]>
前奏
有关虚函数的问题层出不穷Q有兌函数的文章千一律,那ؓ何还要写q一有兌函数的文章呢?看完本文后,怿能懂其意义之所在。同Ӟ?span style="text-decoration: line-through;">狂想曲系?/span>已经更名?span style="text-decoration: underline;">E序员编E艺术系?/strong>Q因Z再只专注?#8220;面试”Q而在“~程”之上了。okQ如果有不正之处Q望不吝赐教。谢谢?/p> 我想Q这道面试题应该是考察虚函数相关知识的相对单的一道题目了。然后,希望你碰到此cL兌函数的面试题Q不论其隑ֺ是难是易Q都能够举一反三Q那么本章的目的也就辑ֈ了。okQ请跟着我的思\Q咱们步步深入(上面E序的输出结果ؓA BQ?/p> W二节、有无虚函数的区?/span> class A class B : public A 对的Q程序此时将输出两个AQA。ؓ什? 2?/strong>那如果加上虚函数?卛_最开始的那段E序那样Q程序的输出l果Q将是什? Ҏ上述b的观点,我们知道Q如果加上虚函数Q如上面q道面试题, class A class B : public A int main() 那么E序的输出结果将是A B?/p> 所以,xQ咱们的q道面试题已l解冟뀂但虚函数的问题Q还没有解决?/p> 请允许我援引自深度探索c++对象模型一书上的一个例子: class Point { virtual Point& mult( float ) = 0; float x() const { return _x; } //非虚函数Q不作存?br /> virtual float y() const { return 0; } protected: 1?/strong>在Point的对象pt中,有两个东西,一个是数据成员_xQ一个是_vptr_Point。其中_vptr_Point指向着virtual table pointQ而virtual tableQ虚表)point中存储着以下东西Q?/p> class Point2d : public Point { //改写base class virtual functions protected: 2?/strong>在Point2d的对象pt2d中,有三个东西,首先是承自基类pt对象的数据成员_xQ然后是pt2d对象本n的数据成员_yQ最后是_vptr_Point。其中_vptr_Point指向着virtual table point2d。由于Point2dl承自PointQ所以在virtual table point2d中存储着Q改写了的其中的~Point2d()、Point2d& mult( float )、float y() constQ以及未被改写的Point::z()函数?/p> class Point3d: public Point2d { // overridden base class virtual functions // ... other operations ... 3?/strong>在Point3d的对象pt3d中,则有四个东西Q一个是_xQ一个是_vptr_PointQ一个是_yQ一个是_z。其中_vptr_Point指向着virtual table point3d。由于point3dl承自point2dQ所以在virtual table point3d中存储着Q已l改写了的point3d的~Point3d()Qpoint3d::mult()的函数地址Q和z()函数的地址Q以及未被改写的point2d的y()函数地址?/p> okQ上q???所有情늚详情Q请参考下图?br /> 本文Q日后可能会酌情考虑增补有关内容。okQ更多,可参考深度探索c++对象模型一书第四章?br />最q几章难度都比较,是考虑到狂x有深有浅的原则,后箋章节会逐步恢复到相应难度?/p> W四节、虚函数的布局与汇~层面的考察 ivan、老梦的两文章l对虚函数进行了一番深入,我看他们已经写得很好了,我就不饶舌了。okQ请看:1?a target="_blank" style="color: #336699; text-decoration: initial;">VC虚函数布局引发的问?/a>Q?、从汇编层面深度剖析C++虚函数?a target="_blank" style="color: #336699; text-decoration: initial;">http://blog.csdn.net/linyt/archive/2011/04/20/6336762.aspx?/p> W五节、虚函数表的详解 本节全部内容来自淄博的共享,非常感谢。注@molixiaogemaoQ?span style="font-family: Arial, Helvetica, sans-serif; line-height: 20px;">只有发生l承的时候且父类子类都有virtual的时候才会出现虚函数指针Q请不要忘了虚函数出现的目的是ؓ了实现多?/strong>?/span> 一般承(无虚函数覆盖Q?/strong> h意,在这个承关pMQ子cL有重载Q何父cȝ函数。那么,在派生类的实例中Q?/span> 对于实例QDerive d; 的虚函数表如下: 我们从表中可以看C面几点, b->f(); 由b所指的内存中的虚函数表的f()的位|已l被Derive::f()函数地址所取代Q?br />于是在实际调用发生时Q是Derive::f()被调用了。这实C多态?/span> 下面Q再让我们来看看多重l承中的情况Q假设有下面q样一个类的承关p(注意Q子cdƈ没有覆盖父类的函敎ͼQ?/span> 我们可以看到Q?br />1Q?每个父类都有自己的虚表?br />2Q?子类的成员函数被攑ֈ了第一个父cȝ表中。(所谓的W一个父cL按照声明序来判断的Q?/span> q样做就是ؓ了解决不同的父类cd的指针指向同一个子cd例,而能够调用到实际的函数?/span> 我们可以看见Q三个父c虚函数表中的f()的位|被替换成了子类的函数指针?br />q样Q我们就可以M静态类型的父类来指向子c,q调用子cȝf()了。如Q?/span> Derive d; 安全?/strong> 一、通过父类型的指针讉K子类自己的虚函数 Base1 *b1 = new Derive(); M妄图使用父类指针惌用子cM的未覆盖父类的成员函数的行ؓ都会被编译器视ؓ非法Q?span style="text-decoration: underline;">卛_cL针不能调用子c自己定义的成员函数?/span>所以,q样的程序根本无法编译通过?br />但在q行Ӟ我们可以通过指针的方式访问虚函数表来辑ֈq反C++语义的行为?br />Q关于这斚w的尝试,通过阅读后面附录的代码,怿你可以做到这一点) 二、访问non-public的虚函数 class Base { class Derive : public Base{ 对上面粗体部分的解释Q@a && xQ: 1. (int*)(&d)取vptr地址Q该地址存储的是指向vtbl的指?br />2. (int*)*(int*)(&d)取vtbl地址Q该地址存储的是虚函数表数组 下图也能很清晰的说明一些东西(@5Q: okQ再来看一个问题,如果一个子c重载的虚拟函数为priveteQ那么通过父类的指针可以访问到它吗Q?/span> #include <IOSTREAM> class D : public B int main(int argc, char* argv[]) 从这个实验,可以更深入的了解虚拟函数~译时的一些特? 可以再做个实?/strong> class D : public B 关于q一点,Effective上讲的很清楚“virtual 函数pd态绑定, 而缺省参数却是静态绑?#8221;Q?br />也就是说在编译的时候已l按照p的静态类型处理其默认参数?转换成了(*p->vptr[1])(p, 1)q样的方式?/span> 补遗 一个类如果有虚函数Q不是几个虚函敎ͼ都会个类声明一个虚函数表,q个虚表是一个含有虚函数的类的,不是说是cd象的。一个含有虚函数的类Q不有多少个数据成员,每个对象实例都有一个虚指针Q在内存中,存放每个cd象的内存区,在内存区的头部都是先存放q个指针变量的(准确的说Q应该是Q视~译器具体情况而定Q,从第nQn视实际情况而定Q个字节才是q个对象自己的东ѝ?/span> 下面再说下通过基类指针Q调用虚函数所发生的一切: 1、上来要取得cȝ虚表的指针,是要得刎ͼ虚表的地址。存攄对象的内存区的前四个字节其实是用来存放虚表的地址的?br />2、得到虚表的地址后,从虚表那知道你调用的那个函数的入口地址。根据虚表提供的你要扄函数的地址。ƈ调用函数Q你要知道,那个虚表是一个存放指针变量的数组Qƈ不是_那个虚表中就是存攄虚函数的实体?/span> 本章完?/p>
W一节、一道简单的虚函数的面试?/span>
题目要求Q写Z面程序的q行l果?
1?/strong>当上q程序中的函数p()不是虚函敎ͼ那么E序的运行结果是如何?卛_下代码所C:
{
public:
void p()
{
cout << "A" << endl;
}
};
{
public:
void p()
{
cout << "B" << endl;
}
};
我们知道Q在构造一个类的对象时Q如果它有基c,那么首先构造基cȝ对象Q然后才构造派生类自己的对象。如上,A* a=new AQ调用默认构造函数构造基cA对象Q然后调用函数p()Qa->p();输出AQ这Ҏ有问题?br /> 然后QA * b = new B;Q构造了zcd象BQB׃是基cA的派生类对象Q所以会先构造基cA对象Q然后再构造派生类对象Q但׃当程序中函数是非虚函数调用时QBcd象对函数p()的调用时在编译时已静态确定了Q所以,不论基类指针b最l指向的是基cd象还是派生类对象Q只要后面的对象调用的函C是虚函数Q那么就直接无视Q而调用基cA的p()函数?/p>
在此之前Q我们还得明以下两点:
a、通过基类引用或指针调用基cM定义的函数时Q我们ƈ不知道执行函数的对象的确切类型,执行函数的对象可能是基类cd的,也可能是zcd的?br /> b、如果调用非虚函敎ͼ则无论实际对象是什么类型,都执行基cȝ型所定义的函敎ͼ如上q第1Ҏqͼ。如果调用虚函数Q则直到q行时才能确定调用哪个函敎ͼq行的虚函数是引用所l定的或指针所指向的对象所属类型定义的版本?/p>
{
public:
virtual void p()
{
cout << "A" << endl;
}
};
{
public:
virtual void p()
{
cout << "B" << endl;
}
};
{
A * a = new A;
A * b = new B;
a->p();
b->p();
delete a;
delete b;
return 0;
}
W三节、虚函数的原理与本质
我们已经知道Q虚QvirtualQ函数的一般实现模型是Q每一个类QclassQ有一个虚表(virtual tableQ,内含该class之中有作用的虚(virtualQ函数的地址Q然后每个对象有一个vptrQ指向虚表(virtual tableQ的所在?/p>
public:
virtual ~Point();
virtual float z() const { return 0; }
// ...
Point( float x = 0.0 );
float _x;
};
public:
Point2d( float x = 0.0, float y = 0.0 )
: Point( x ), _y( y ) {}
~Point2d(); //1
Point2d& mult( float ); //2
float y() const { return _y; } //3
float _y;
};
public:
Point3d( float x = 0.0,
float y = 0.0, float z = 0.0 )
: Point2d( x, y ), _z( z ) {}
~Point3d();
Point3d& mult( float );
float z() const { return _z; }
protected:
float _z;
};
下面Q再让我们来看看l承时的虚函数表是什么样的。假设有如下所C的一个承关p:
1Q覆盖的f()函数被放C虚表中原来父c虚函数的位|?br /> 2Q没有被覆盖的函C旧?br />
q样Q我们就可以看到对于下面q样的程序,
Base *b = new Derive();
多重l承Q无虚函数覆盖)
多重l承Q有虚函数覆盖)
下面我们再来看看Q如果发生虚函数覆盖的情c?br />下图中,我们在子cM覆盖了父cȝf()函数?/span>
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()
b2->f(); //Derive::f()
b3->f(); //Derive::f()
b1->g(); //Base1::g()
b2->g(); //Base2::g()
b3->g(); //Base3::g()
每次写C++的文章,d不了要批判一下C++?br />q篇文章也不例外。通过上面的讲qͼ怿我们对虚函数表有一个比较细致的了解了?br />水可载舟Q亦可覆舟。下面,让我们来看看我们可以用虚函数表来q点什么坏事吧?/span>
我们知道Q子cL有重载父cȝ虚函数是一件毫无意义的事情。因为多态也是要Z函数重蝲的?br />虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数Q但我们Ҏ不可能用下面的语句来调用子cȝ自有虚函敎ͼ
b1->g1(); //~译出错
另外Q如果父cȝ虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,
所以,我们同样可以使用讉K虚函数表的方式来讉Kq些non-public的虚函数Q这是很Ҏ做到的?br />如:
private:
virtual void f() { cout << "Base::f" << endl; }
};
};
typedef void(*Fun)(void);
void main() {
Derive d;
Fun pFun = (Fun)*((int*)*(int*)(&d)+0);
pFun();
}
3. (Fun)*((int*)*(int*)(&d) +0)Q取vtbl数组的第一个元素,即Base中第一个虚函数f的地址
4. (Fun)*((int*)*(int*)(&d) +1)Q取vtbl数组的第二个元素Q这W?点,如下图所C)?/span>
class B
{
public:
virtual void fun()
{
std::cout << "base fun called";
};
};
{
private:
virtual void fun()
{
std::cout << "driver fun called";
};
};
{
B* p = new D();
p->fun();
return 0;
}
q行时会输出 driver fun called
在编译虚拟函数调用的时候,例如p->fun(); 只是按其静态类型来处理? 在这里p的类型就是BQ不会考虑其实际指向的cdQ动态类型)?/span>
也就是说Q碰到p->fun();~译器就当作调用B的fun来进行相应的查和处理?br />因ؓ在B里fun是public的,所以这里在“讉K控制?#8221;q一兛_完全可以通过了?br />然后׃转换?*p->vptr[1])(p)q样的方式处? p实际指向的动态类型是DQ?br /> 所以p作ؓ参数传给fun?cȝ非静态成员函数都会编译加一个指针参敎ͼ指向调用该函数的对象Q我们^常用的this是该指针的|, 实际q行时p->vptr[1]则获取到的是D::fun()的地址Q也p用了该函? q也是动态运行的机理?/span>
Zq一步的实验Q可以将B里的fun改ؓprivate的,D里的改ؓpublic的,则编译就会出错?br />C++的注意条ƾ中有一? l不重新定义l承而来的缺省参数?
QEffective C++ Item37Q?never redefine a function's inherited default parameter value) 也是同样的道理?/span>
class B
{
public:
virtual void fun(int i = 1)
{
std::cout << "base fun called, " << i;
};
};
{
private:
virtual void fun(int i = 2)
{
std::cout << "driver fun called, " << i;
};
};
One *p;
p->disp();
]]>
作者:July?br />出处Q?a target="_blank" style="color: #336699; text-decoration: initial;">http://blog.csdn.net/v_JULY_v ?/p>
前奏
例如输入的数lؓ1, -2, 3, 10, -4, 7, 2, -5Q和最大的子数lؓ3, 10, -4, 7, 2Q?br />因此输出子数l的?8?/p> 分析Q?/strong>q个问题在各大公叔R试中出现频率之频J,被h引用ơ数之多Q非一般面试题可与之匹敌。单凭这点,没有理׃入选狂xpd中了。此题曾作ؓ本h之前整理的微?00题中的第3题,至今反响也很大。okQ下面,׃来一步一步分析这个题Q?br /> 1?/strong>求一个数l的最大子数组和,如此序列1, -2, 3, 10, -4, 7, 2, -5Q我x最直观也是最野蛮的办法便是,三个for循环三层遍历Q求出数l中每一个子数组的和Q最l求些子数组的最大的一个倹{?br />记Sum[i, …, j]为数lA中第i个元素到Wj个元素的和(其中0 <= i <= j < nQ,遍历所有可能的Sum[i, …, j]Q那么时间复杂度为OQN^3Q: //本段代码引自~程之美 sum=0; //q里要记得清Ӟ否则的话sum最l存攄是所有子数组的和。也是~程之美上所说的bug。多谢苍狹{?br /> } 2?/strong>其实q个问题Q在我之前上传的微Y100题,{案V0.2版[W?-20题答案]Q便直接l出了以下OQNQ的法Q?/p> 3?/strong>不少朋友看到上面的答案之后,认ؓ上述思\2的代码,没有处理全是负数的情况,当全是负数的情况Ӟ我们可以让程序返?Q也可以让其q回最大的那个负数Q下面便是前几日重写的,修改后的处理全是负数情况Q返回最大的负数Q的代码Q?/p> 4?/strong>DP解法的具体方E:@ flyingheartsQ设sum[i] 为前i个元素中Q包含第i个元素且和最大的q箋子数l,result 为已扑ֈ的子数组中和最大的。对Wi+1个元素有两种选择Q做为新子数l的W一个元素、放入前面找到的子数l?br />sum[i+1] = max(a[i+1], sum[i] + a[i+1]) 扩展Q?/strong> W二节、Data structures and Algorithm analysis in C 下面l出《Data structures and Algorithm analysis in C》中4U实现?/p> 本章完?/p>
W一节、求子数l的最大和
3.求子数组的最大和
题目描述Q?/strong>
输入一个整形数l,数组里有正数也有负数?br />数组中连l的一个或多个整数l成一个子数组Q每个子数组都有一个和?br />求所有子数组的和的最大倹{要求时间复杂度为O(n)?/p>
int MaxSum(int* A, int n)
{
int maximum = -INF;
int sum=0;
for(int i = 0; i < n; i++)
{
for(int j = i; j < n; j++)
{
for(int k = i; k <= j; k++)
{
sum += A[k];
}
if(sum > maximum)
maximum = sum;
}
return maximum;
}
result = max(result, sum[i])
1、如果数l是二维数组Q同栯你求最大子数组的和?
2、如果是要你求子数组的最大乘U列?
3、如果同时要求输出子D늚开始和l束?
前奏
本章陆箋开始,除了l箋保持原有的字W串、数l等面试题之外,会有意识的间断性节选一些有x字趣呛_而y的面试题目,重在H出思\?#8220;?#8221;Q和“?#8221;。本章亲和数问题之关键字Q?#8220;500?#8221;Q?#8220;U性复杂度”?/p>
W一节、亲和数问题
题目描述Q?br />?00万以内的所有亲和数
如果两个数a和bQa的所有真因数之和{于b,b的所有真因数之和{于a,则称a,b是一对亲和数?br />例如220?84Q?184?210Q?620?924?/p>
分析Q?br /> 首先得明到底是什么是亲和?
亲和数问题最早是由毕辑֓拉斯学派发现和研I的。他们在研究数字的规律的时候发现有以下性质特点的两个数Q?br />220的真因子是:1????0?1?0?2?4?5?10Q?br />284的真因子是:1???1?42?br />而这两个数恰恰等于对方的真因子各自加h的和Qsum[i]表示数i 的各个真因子的和Q,?br />220=1+2+4+71+142=sum[284],
284=1+2+4+5+10+11+20+22+44+55+110=sum[220]?br />?84的真因子之和sum[284]=220Q且220的真因子之和sum[220]=284Q即有sum[220]=sum[sum[284]]=284?/p>
如此Q是否已看出丝毫端?
如上所C,考虑?是每个整数的因子Q把出去整数本n之外的所有因子叫做这个数?#8220;真因?#8221;。如果两个整敎ͼ其中每一个真因子的和都恰好等于另一个数Q那么这两个敎ͼ构成一?#8220;亲和?#8221;Q有关亲和数的更多讨论,可参考这Q?a title="http://en.wikipedia.org/wiki/Amicable_pair" target="_blank" style="color: #336699; text-decoration: initial;">http://t.cn/hesH09Q?/p>
求解Q?/strong> W三节、程序的构造与解释 因ؓ所有数的真因数都包?Q所以,先在各个数的下方全部|?
了解了什么是亲和敎ͼ接下来咱们一步一步来解决上面提出的问题(以下内容大部引自水的原话Q同时水哥有一句原话,“在你真正弄弄懂这个范例之前,你不配说你懂数据l构和算?/strong>”Q?/p>
W二节、伴随数l线性遍?/span>
依据上文中的W?Ҏ\Q编写如下代码:
int main()
{
int i, j;
for (i = 0; i <= 5000000; i++)
sum[i] = 1; //1是所有数的真因数所以全部置1
for (i = 2; i + i <= 5000000; i++)
{
//5000000以下最大的真因数是不超q它的一半的
j = i + i; //因ؓ真因敎ͼ所以不能算本nQ所以从它的2倍开?nbsp;
while (j <= 5000000)
{
//所有i的倍数的位|上加i
sum[j] += i;
j += i;
}
}
for (i = 220; i <= 5000000; i++) //扫描QOQNQ?nbsp;
{
// 一ơ遍历,因ؓ知道最是220?84因此?20开?nbsp;
if (sum[i] > i && sum[i] <= 5000000 && sum[sum[i]] == i)
{
//去重Q不界Q满亲?nbsp;
printf("%d %d/n",i,sum[i]);
}
}
return 0;
}
我再来具体解释下上述E序的原理,okQD个例子,假设是求10以内的亲和数Q求解步骤如下:
1 2 3 4 5 6 7 8 9 10
1 1 1 1 1 1 1 1 1 1
2 2 2 2
3 3
4
5
i对应的数下面的各个真因子加v来得C个和sum[i]Q如果这个和sum[i]==某个i’Q且sum[i‘]=iQ?br />那么q两个数i和i’Q即Z对亲和数?/li>
i=3Qsum[6]+=3Qsum[9]+=3...
......
]]>
作者:JulyQyanshaQzhouzhenren?br /> 致谢Q微?00题实现组Q编E艺术室?br /> 微博Q?a target="_blank" style="color: #336699; text-decoration: initial;">http://weibo.com/julyweibo ?br /> 出处Q?a target="_blank" style="color: #336699; text-decoration: initial;">http://blog.csdn.net/v_JULY_v ?br /> wikiQ?a target="_blank" style="color: #336699; text-decoration: initial;">http://tctop.wikispaces.com/?br />------------------------------
前奏
希望此编E艺术系列能l各位带来的是一U方法,一U创造力Q一UD一反三的能力。本章依然同W四章一P选取比较单的面试题,恭祝各位旅途愉快。同P有Q何问题,Ƣ迎不吝指正。谢谢?/p>
分析Q?/p> ׃试着一步一步解册个问题(注意阐述中数列有序无序的区别Q:
W一节、寻扑֒为定值的两个?/span>
W?4题(数组Q:
题目Q输入一个数l和一个数字,在数l中查找两个敎ͼ使得它们的和正好是输入的那个数字?br />要求旉复杂度是O(n)。如果有多对数字的和{于输入的数字,输出L一对即可?br />例如输入数组1????1?5和数?5。由?+11=15Q因此输??1?/p>
Q?/li>
原始序列Q??2?4?7?1?5 用输入数?5减一下各个数Q得到对应的序列为:
对应序列Q?4?3?1???0
W一个数l以一指针i 从数l最左端开始向x描,W二个数l以一指针j 从数l最右端开始向左扫描,如果下面出现了和上面一L敎ͼ即a[*i]=a[*j]Q就扑ևq俩个数来了。如上,iQj最l在W一个,和第二个序列中找C相同的数4?1Q,所以符合条件的两个敎ͼ即ؓ4+11=15。怎么P两端同时查找Q时间复杂度瞬间~短COQNQ,但却同时需要OQNQ的I间存储W二个数l(@飞羽Q?span style="font-size: 12px;">要达到O(N)的复杂度Q第一个数l以一指针i 从数l最左端开始向x描,W二个数l以一指针j 从数l最右端开始向左扫描,首先初始i指向元素1Qj指向元素0Q谁指的元素,谁先UdQ由?QiQ?gt;0QjQ,所以i不动Qj向左Ud。然后jUd到元?发现大于元素1Q故而停止移动jQ开始移动iQ直到i指向4Q这?i指向的元素与j指向的元素相{,故而判?是满x件的W一个数Q然后同时移动i,j再进行判断,直到它们到达边界
ȝQ?/p>
代码Q?/strong>
okQ在q入W二节之前,׃先来实现思\5Q这里假定数l已l是有序的)Q代码可以如下编写(两段代码实现Q:
扩展Q?/strong>
1、如果在q回扑ֈ的两个数的同Ӟq要求你q回q两个数的位|列?
2、如果把题目中的要你L的两个数改ؓ“多个?#8221;Q或L个数?Q请看下面第二节Q?br />3、二分查找时Q?left <= rightQright = middle - 1;left < rightQright = middle;
//法所操作的区?是左闭右开区间,q是左闭右闭区间,q个区间,需要在循环初始?
//循环体是否终止的判断?以及每次修改left,right区间D三个地方保持一?否则可能出?//二分查找实现一
int search(int array[], int n, int v)
{
int left, right, middle;
left = 0, right = n - 1;
while (left <= right)
{
middle = left + (right-left)/2;
if (array[middle] > v)
{
right = middle - 1;
}
else if (array[middle] < v)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
}//二分查找实现?br />int search(int array[], int n, int v)
{
int left, right, middle;
left = 0, right = n;
while (left < right)
{
middle = left + (right-left)/2;
if (array[middle] > v)
{
right = middle;
}
else if (array[middle] < v)
{
left = middle + 1;
}
else
{
return middle;
}
}
return -1;
}
解法一 解法?/strong> 代码~写如下Q?/p> 扩展Q?/strong> 1、从一列数中筛除尽可能的C得从左往右看Q这些数是从到大再从大到小的(|易Q?/p> 2、有两个序列a,bQ大都为n,序列元素的gQ意整敎ͼ无序Q?br />要求Q通过交换a,b中的元素Q[序列a元素的和]与[序列b元素的和]之间的差最?br />例如: @wellQ[fairywell]: @yanshaQfairywell的程序很赞,旉复杂度O(nlogn)Q这也是我能惛_的时间复杂度最优g。不知能不能辑ֈO(n)?/span> 扩展题第2?/span> 当前数组a和数lb的和之差?br /> A = sum(a) - sum(b) a的第i个元素和b的第j个元素交换后Qa和b的和之差?br /> A' = sum(a) - a[i] + b[j] - Qsum(b) - b[j] + a[i]) 设x = a[i] - b[j]Q得 假设A > 0, 当x ?(0,A)之间Ӟ做这L交换才能使得交换后的a和b的和之差变小Qx接qA/2效果好, 所以算法大概如下: 接上Q@yuanQ?br />a[i]-b[j]要接qA/2Q则可以q样惻I 查找最后一个小于等于a[k]-C的数和第一个大于等于a[k]-C的数Q?br />然后看哪一个与a[k]-C更加接近Q所以T(n) = nlogn?/span> 除此之外Q受本文读?span style="color: #666666; font-family: Arial, Console, Verdana, 'Courier New'; line-height: 14px;">xiafei1987128启示Q?/span>有朋友在stacoverflow上也问过一个类似的题,:-)Q见此:http://stackoverflow.com/questions/9047908/swap-the-elements-of-two-sequences-such-that-the-difference-of-the-element-sums。感兴趣的可以看看?/span> 本章完?/p> E序员面试题狂想?tctopQthe crazy thinking of programersQ的修订wikiQ?a target="_blank" style="color: #336699; text-decoration: initial;">http://tctop.wikispaces.com/Q已建立Q我们急切的想得到读者的反馈Q意见,Q以及更好的思\Q算法,和代码优化的。所以, •如果你发C狂想曲系列中的Q何一题,M一章(http://t.cn/hgVPmHQ中的错误,问题Q与漏洞Q欢q告知给我们Q我们将感激不尽Q同Ӟ免费赠送本blog内的全部博文集锦的CHM文g1期; 版权所有,本hҎblog内所有Q何内容n有版权及著作权。实要{载,请以链接形式注明出处?/strong>
W二节、寻扑֒为定值的多个?/span>
W?1题(数组Q?br />2010q中兴面试题
~程求解Q?br />输入两个整数 n ?mQ从数列1Q?Q?.......n ?随意取几个数,
使其和等?m ,要求其中所有的可能l合列出来?/p>
我想Q稍后给出的E序已经_清楚了,是要注意到放nQ和不放n个区别,卛_Q代码如下:
@zhouzhenrenQ?br />q个问题属于子集和问题(也是背包问题Q。本E序采用 回溯?剪枝
X数组是解向量Qt=∑(1,..,k-1)Wi*Xi, r=∑(k,..,n)Wi
若t+Wk+W(k+1)<=M,则Xk=trueQ递归左儿?X1,X2,..,X(k-1),1)Q否则剪枝;
若t+r-Wk>=M && t+W(k+1)<=M,则置Xk=0Q递归叛_?X1,X2,..,X(k-1),0)Q否则剪枝;
本题中W数组是(1,2,..,n),所以直接用k代替WK倹{?/p>
var a=[100,99,98,1,2, 3];
var b=[1, 2, 3, 4,5,40];Q?a target="_blank" style="color: #336699; text-decoration: initial;">微Y100?/span>W?2题)?/p>
l出扩展问题 1 的一个解法:
1、从一列数中筛除尽可能的C得从左往右看Q这些数是从到大再从大到小的(|易Q?br />双端 LIS 问题Q用 DP 的思想可解Q目标规划函?max{ b[i] + c[i] - 1 }, 其中 b[i] Z左到叻I 0 ~ i 个数之间满递增的数字个敎ͼ c[i] Z叛_左, n-1 ~ i 个数之间满递增的数字个数。最后结果ؓ n - max + 1。其?DP 的时候,可以l护一?inc[] 数组表示递增数字序列Qinc[i] Z到大第 i 大的数字Q然后在计算 b[i] c[i] 的时候用二分查扑֜ inc[] 中找出区?inc[0] ~ inc[i-1] 中小?a[i] 的元素个敎ͼlowQ?br />源代码如下:
= sum(a) - sum(b) - 2 (a[i] - b[j])
= A - 2 (a[i] - b[j])
|A| - |A'| = |A| - |A-2x|
如果找不到在(0,A)之间的xQ则当前的a和b是{案?/span>
在a和b中寻找得x?0,A)之间q且最接近A/2的i和jQ交换相应的i和j元素Q重新计A后,重复前面的步骤直x不到(0,A)之间的x为止?nbsp;
我们可以对于a数组的Q意一个a[k],在数lb中找Za[k]-C最接近的数QC是常数Q也是0.5*AQ?br />q个数要么就是a[k]-CQ要么就是比他稍大,要么比他E小Q所以可以要二分查找?/span>
•如果你能对狂xpd的创作提供Q何徏设性意见,或指|Ƣ迎反馈l我们,q真诚邀h加入到狂x的wiki修订工作中;
•如果你是~程高手Q对狂想曲的M一章有自己更好的思\Q或法Q欢q加入狂x的创作组Q以为千千万万的读者创造更多的价|更好的服务?br />PsQ狂xtctop的wiki修订地址为:http://tctop.wikispaces.com/。欢q围观,更欢q您加入到狂x的创作或wiki修订中?/span>
]]>