??xml version="1.0" encoding="utf-8" standalone="yes"?>中文字幕乱码人妻无码久久,亚洲精品国产美女久久久,久久久久久噜噜精品免费直播http://m.shnenglu.com/mysileng/archive/2013/05/28/200661.html鑫龙鑫龙Tue, 28 May 2013 14:18:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/28/200661.htmlhttp://m.shnenglu.com/mysileng/comments/200661.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/28/200661.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200661.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200661.html阅读全文

鑫龙 2013-05-28 22:18 发表评论
]]>
E序员编E艺?----W二十七?----不改变正负数相对序重新排列数组http://m.shnenglu.com/mysileng/archive/2013/05/28/200652.html鑫龙鑫龙Tue, 28 May 2013 09:50:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/28/200652.htmlhttp://m.shnenglu.com/mysileng/comments/200652.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/28/200652.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200652.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200652.html

W二十七章:不改变正负数之间相对序重新排列数组.旉O(N)Q空间O(1)


前言

    在这文章:九月腾讯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>


重新排列使负数排在正数前?/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:

  1. 最单的Q如果不考虑旉复杂度,最单的思\是从头扫描这个数l,每碰C个正数时Q拿个数字,q把位于q个数字后面的所有数字往前挪动一位。挪完之后在数组的末有一个空位,q时把该正数攑օq个IZ。由于碰C个正Q需要移动O(n)个数字,因此ȝ旉复杂度是O(nQ??/li>
  2. 既然题目要求的是把负数放在数l的前半部分Q正数放在数l的后半部分Q因此所有的负数应该位于正数的前面。也是说我们在扫描q个数组的时候,如果发现有正数出现在负数的前面,我们可以交换他们的顺序,交换之后q合要求了?br />因此我们可以l护两个指针Q第一个指针初始化为数l的W一个数字,它只向后UdQ第二个指针初始化ؓ数组的最后一个数字,它只向前Ud。在两个指针盔R之前Q第一个指针L位于W二个指针的前面。如果第一个指针指向的数字是正而第二个指针指向的数字是负数Q我们就交换q两个数字?br />但遗憄是上q方法改变了原来正负C间的相对序。所以,׃得另寻良{?span style="font-family: SimSun; line-height: 25px; font-size: 14px;">?/span>
  3. 首先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>

  4. 我们可以q样Q?/span>讄一个v始点j, 一个翻转点k,一个终止点LQ从右侧P起始点在W一个出现的负数, {点在起始点后W一个出现的正数,l止点在{点后出现的第一个负?或结??/span>

    如果无翻转点, 则不操作Q如果有{? 则待l止点出现后, 做翻? 即ab => ba q样的操作。翻转后, 负数串一定在左侧, 然后从负C的右侧开始记录v始点, l箋往下找下一个翻转点?/p>

    例子中的是(下划U代表要交换序的两个数?Q?/p>

    1, 7, -5, 9-12, 15  
    W一ơ翻? 1, 7, -5, -12,9, 15   =>  1, -12, -5, 7, 9, 15
    W二ơ翻? -5, -12, 1, 7, 9, 15

    此思\2果真解决了么?NOQ用下面q个例子试一下,我们p立马看出了漏z:
    1, 7, -5, -6Q?nbsp;9-12, 15Q此U情冉|能处理)
    7 -5 -6 -12 9 15
    -12 -5 -6 7 9 15
    -6 -12 -5 1 7 9 15   (此时Q正负数之间的相寚w序已l改变,本应该是-5Q?6Q?12Q而现在是-6 -12 -5)
  5. 看来q个问题的确有点ȝQ不q我们最l貌D是找C另外一U解军_法,正如朋友越所说的Q从后往前扫描,遇到负数Q开始记录负数区_然后遇到正数Q记录前面的正数区间Q然后把整个负数区间与前面的正数区间q行交换Q交换区间但保序的算法类|a,bc->bc,aQ的字符串原地翻转算法?/span>交换完之后要l箋向前一直扫描下去,每次到负数区间在正数区间后面,q转区间。下面,详l阐q此思\4?/span>

思\5之区间翻?/span>

    其实上述思\5非常单,既然单个{无法解决问题Q那么咱们可以区间翻转阿。什么叫区间{?不知读者朋友们是否q记得本blog之前曄整理q这样一道题Q微软面?00题系列第10题,如下Q?/span>

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
1 7 -5 -6 -12 9 15
-12 -6 -5 7 1 9 15   (借用单词{的方法,先逐个数字{Q后正负数整体原地翻?
-5 -6 -12 1 7 9 15


思\5再次被质?/span>

    但是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>



鑫龙 2013-05-28 17:50 发表评论
]]>
E序员编E艺?----W二十五?----二分查找实现QJon BentleyQ?0%E序员无法正实玎ͼhttp://m.shnenglu.com/mysileng/archive/2013/05/28/200651.html鑫龙鑫龙Tue, 28 May 2013 08:41:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/28/200651.htmlhttp://m.shnenglu.com/mysileng/comments/200651.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/28/200651.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200651.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200651.html

W二十五章:二分查找实现QJon BentleyQ?0%E序员无法正实玎ͼ

作者:July
出处Q结构之法算法之?/p>

引言

    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;
多数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>

    你能正确无误的写Z分查找代码么Q不妨一试?/p>

二分查找代码

     二分查找的原理想必不用多解释了,不过有一点必L醒读者的是,二分查找是针对的排好序的数组。OKQ纸上读来终觉浅Q觉知此事要w行。我先来写一份,下面是我写的一份二分查扄实现Q之前去某一家公叔R试也曾被叫当场实C分查找,不过l果可能跟你一P当时未能完整无误写出)Q有M问题或错误,恌不吝指正Q?br />

//二分查找V0.1实现?nbsp; 
//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;  
}  




鑫龙 2013-05-28 16:41 发表评论
]]>
E序员编E艺?----W二十三 ~ 二十四章-----杨氏矩阵、不重复Hash~码http://m.shnenglu.com/mysileng/archive/2013/05/28/200650.html鑫龙鑫龙Tue, 28 May 2013 08:39:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/28/200650.htmlhttp://m.shnenglu.com/mysileng/comments/200650.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/28/200650.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200650.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200650.html阅读全文

鑫龙 2013-05-28 16:39 发表评论
]]>
E序员编E艺?----W十?~ 二十?----全排列、蟩台阶、奇偶、第一个出Cơ字W、一致性hashhttp://m.shnenglu.com/mysileng/archive/2013/05/16/200324.html鑫龙鑫龙Thu, 16 May 2013 08:42:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/16/200324.htmlhttp://m.shnenglu.com/mysileng/comments/200324.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/16/200324.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200324.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200324.html阅读全文

鑫龙 2013-05-16 16:42 发表评论
]]>
E序员编E艺?----W十一 ~ 十四?----量整数处理、蓄水池抽样、回?http://m.shnenglu.com/mysileng/archive/2013/05/15/200299.html鑫龙鑫龙Wed, 15 May 2013 13:37:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/15/200299.htmlhttp://m.shnenglu.com/mysileng/comments/200299.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/15/200299.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200299.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200299.html阅读全文

鑫龙 2013-05-15 21:37 发表评论
]]>
E序员编E艺?----W十?----最长公共子序列(LCS)问题http://m.shnenglu.com/mysileng/archive/2013/05/14/200265.html鑫龙鑫龙Tue, 14 May 2013 14:05:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/14/200265.htmlhttp://m.shnenglu.com/mysileng/comments/200265.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/14/200265.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200265.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200265.html

  E序员编E艺术第十一章:最长公共子序列(LCS)问题

0、前a

    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>

W一节、问题描q?/span>

    什么是最长公共子序列?好比一个数?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>

W二节、LCS问题的解x\

  • ID?nbsp;  

    解最长公共子序列问题时最Ҏ惛_的算法是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>

  • 动态规划算?/strong>

    事实上,最长公共子序列问题也有最优子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>

W三节、动态规划算法解LCS问题

3.1、最长公共子序列的结?/strong>

    最长公共子序列的结构有如下表示Q?/p>

    讑ֺ列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>Q则Q?/p>

  1. 若xm=ynQ则zk=xm=yn且Zk-1是Xm-1和Yn-1的最长公共子序列Q?/li>
  2. 若xm≠yn且zk≠xm Q?/sub>则Z是Xm-1和Y的最长公共子序列Q?/li>
  3. 若xm≠yn且zk≠yn Q则Z是X和Yn-1的最长公共子序列?/li>

    其中Xm-1=<x1, x2, …, xm-1>QYn-1=<y1, y2, …, yn-1>QZk-1=<z1, z2, …, zk-1>?/p>

3?.子问题的递归l构

    由最长公共子序列问题的最优子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 />

3?.计算最优?/h4>

    直接利用上节节末的递归式,我们很Ҏ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>

  1. Procedure LCS_LENGTH(X,Y);  
  2. begin  
  3.   m:=length[X];  
  4.   n:=length[Y];  
  5.   for i:=1 to m do c[i,0]:=0;  
  6.   for j:=1 to n do c[0,j]:=0;  
  7.   for i:=1 to m do  
  8.     for j:=1 to n do  
  9.       if x[i]=y[j] then  
  10.         begin  
  11.           c[i,j]:=c[i-1,j-1]+1;  
  12.           b[i,j]:="↖";  
  13.         end  
  14.       else if c[i-1,j]≥c[i,j-1] then  
  15.         begin  
  16.           c[i,j]:=c[i-1,j];  
  17.           b[i,j]:="↑";  
  18.         end  
  19.       else  
  20.         begin  
  21.           c[i,j]:=c[i,j-1];  
  22.           b[i,j]:="←"  
  23.         end;  
  24.   return(c,b);  
  25. end;   

    q法LCS_LENGTH计算得到的数lb可用于快速构造序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最长公共子序列。首先从b[m,n]开始,沿着其中的箭头所指的方向在数lb中搜索?/p>

  • 当b[i,j]中遇?↖"Ӟ意味着xi=yi是LCS的一个元?/em>Q,表示Xi与Yj的最长公共子序列是由Xi-1与Yj-1的最长公共子序列在尾部加上xi得到的子序列Q?/li>
  • 当b[i,j]中遇?↑"Ӟ表示Xi与Yj的最长公共子序列和Xi-1与Yj的最长公共子序列相同Q?/li>
  • 当b[i,j]中遇?←"Ӟ表示Xi与Yj的最长公共子序列和Xi与Yj-1的最长公共子序列相同?/li>

    q种Ҏ是按照反序来找LCS的每一个元素的。由于每个数l单元的计算耗费Ο(1)旉Q算法LCS_LENGTH耗时Ο(mn)?/p>

3?.构造最长公共子序列

    下面的算法LCS(b,X,i,j)实现Ҏb的内Ҏ印出Xi与Yj的最长公共子序列。通过法的调用LCS(b,X,length[X],length[Y])Q便可打印出序列X和Y的最长公共子序列?/p>

  1. Procedure LCS(b,X,i,j);  
  2. begin  
  3.   if i=0 or j=0 then return;  
  4.   if b[i,j]="↖" then  
  5.     begin  
  6.       LCS(b,X,i-1,j-1);  
  7.       print(x[i]); {打印x[i]}  
  8.     end  
  9.   else if b[i,j]="↑" then LCS(b,X,i-1,j)   
  10.                       else LCS(b,X,i,j-1);  
  11. end;   

在算法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>

3?.法的改q?/h4>

    对于一个具体问题,按照一般的法设计{略设计出的法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>

W四节、编码实现LCS问题

    动态规划的一个计最长公共子序列的方法如下,以两个序?nbsp;X?em>Y Z子:

设有二维数组 f[i][j] 表示 X ?nbsp;i 位和 Y ?nbsp;j 位之前的最长公共子序列的长度,则有Q?/p>

f[1][1] = same(1,1)
f[i][j] = max{f[i − 1][j − 1] +same(i,j)f[i − 1][j] ,f[i][j − 1]}

其中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>

  1.    
  2. import java.util.Random;  
  3.    
  4. public class LCS{  
  5.     public static void main(String[] args){  
  6.    
  7.         //讄字符串长?/span>  
  8.         int substringLength1 = 20;  
  9.         int substringLength2 = 20;  //具体大小可自行设|?/span>  
  10.    
  11.         // 随机生成字符?/span>  
  12.         String x = GetRandomStrings(substringLength1);  
  13.         String y = GetRandomStrings(substringLength2);  
  14.    
  15.         Long startTime = System.nanoTime();  
  16.         // 构造二l数l记录子问题x[i]和y[i]的LCS的长?/span>  
  17.         int[][] opt = new int[substringLength1 + 1][substringLength2 + 1];  
  18.    
  19.         // 动态规划计所有子问题  
  20.         for (int i = substringLength1 - 1; i >= 0; i--){  
  21.             for (int j = substringLength2 - 1; j >= 0; j--){  
  22.                 if (x.charAt(i) == y.charAt(j))  
  23.                     opt[i][j] = opt[i + 1][j + 1] + 1;                                 //参考上文我l的公式?/span>  
  24.                 else  
  25.                     opt[i][j] = Math.max(opt[i + 1][j], opt[i][j + 1]);        //参考上文我l的公式?/span>  
  26.             }  
  27.         }  
  28.    
  29.         -------------------------------------------------------------------------------------  
  30.    
  31.         理解上段Q参考上文我l的公式Q?nbsp; 
  32.    
  33.         Ҏ上述l论Q可得到以下公式Q?nbsp; 
  34.    
  35.         如果我们记字W串Xi和Yj的LCS的长度ؓc[i,j]Q我们可以递归地求c[i,j]Q?nbsp; 
  36.    
  37.                   /      0                               if i<0 or j<0  
  38.         c[i,j]=          c[i-1,j-1]+1                    if i,j>=0 and xi=xj  
  39.                  /       max(c[i,j-1],c[i-1,j]           if i,j>=0 and xi≠xj  
  40.    
  41.         -------------------------------------------------------------------------------------  
  42.    
  43.         System.out.println("substring1:"+x);  
  44.         System.out.println("substring2:"+y);  
  45.         System.out.print("LCS:");  
  46.    
  47.         int i = 0, j = 0;  
  48.         while (i < substringLength1 && j < substringLength2){  
  49.             if (x.charAt(i) == y.charAt(j)){  
  50.                 System.out.print(x.charAt(i));  
  51.                 i++;  
  52.                 j++;  
  53.             } else if (opt[i + 1][j] >= opt[i][j + 1])  
  54.                 i++;  
  55.             else  
  56.                 j++;  
  57.         }  
  58.         Long endTime = System.nanoTime();  
  59.         System.out.println(" Totle time is " + (endTime - startTime) + " ns");  
  60.     }  
  61.    
  62.     //取得定长随机字符?/span>  
  63.     public static String GetRandomStrings(int length){  
  64.         StringBuffer buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyz");  
  65.         StringBuffer sb = new StringBuffer();  
  66.         Random r = new Random();  
  67.         int range = buffer.length();  
  68.         for (int i = 0; i < length; i++){  
  69.             sb.append(buffer.charAt(r.nextInt(range)));  
  70.         }  
  71.         return sb.toString();  
  72.     }  
  73. }  

W五节、改q的法

    下面׃来了解一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>

  • 定义 1 子序?Subsequence)Q给定字W串A=A[1]A[2]…A[m]Q?A[i]是A 的第i 个字母,A[i]∈字符?#931;Ql<= i<m = A Q?A 表示字符串A 的长?Q字W串B 是A 的子序列是指B=A[ 1 i ]A[ 2 i ]…A[ k i ],其中1 i < 2 i <…< k i 且k<=m.
  • 定义2 公共子序?Common Subsequence)Q给定字W串A、B、CQC UCؓA 和B 的公共子序列是指C 既是A 的子序列Q又是B 的子序列?/li>
  • 定义3 最长公共子序列(Longest Common Subsequence ULCS)Q给定字W串A、B、CQC UCؓA 和B 的最长公共子序列是指C 是A 和B 的公共子序列Q且对于A 和B 的Q意公共子序列DQ都有D <= C 。给定字W串A 和BQA =mQB =nQ不妨设m<=nQLCS 问题是要求出A 和B 的LCS?/li>
  • 定义4 l定字符串A=A[1]A[2]…A[m]和字W串B=B[1]B[2]…[n]QA( 1:i)表示A 的连l子序列A[1]A[2]…A[i]Q同样B(1:j)表示B 的连l子序列B[1]B[2]…[j]。Li(k)表示所有与字符串A(1:i) 有长度ؓk 的LCS 的字W串B(l:j) 中j 的最倹{用公式表示是Li(k)=Minj(LCS(A(1:i)QB(l:j))=k) [3]?br />

    定理1 ∀ i∈[1Qm]Q有Li(l)<Li(2)<Li(3)<…<Li(m) .
    定理2 ∀i∈[lQm-1]Q?#8704;k∈[lQm]Q有i 1 L + (k)<= i L (k).
    定理3 ∀ i∈[lQm-1]Q?∀ k∈[lQm-l]Q有i L (k)< i 1 L + (k+l).
    以上三个定理都不考虑Li(k)无定义的情况?/span>
    定理4[3] i 1 L + (k)如果存在Q那么它的取值必? i 1 L + (k)=Min(j, i L (k))。这里j 是满以下条件的最整?A[i+l]=B[j]且j> i L (k-1)?br />

     矩阵中元素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

    1. L(A,B,L){//字符串AQBQ所求矩阵L  
    2.   for(k=1;k<=m;k++){ //m 为A 的长?/span>  
    3.     for(i=1;i<=m;i++){  
    4.       if(i<k) L[k][i]=N;//i<k ?L(k,i)=nullQN 代表无穷?/span>  
    5.       if(L[k][i]==k)//L(k,i)=k ?L(k,i+1)=L(k,i+2)=…L(k,m)=k  
    6.       for(l=i+1;l<=m;l++)  
    7.        { L[k][l]=k;  
    8.          Break;}  
    9.       for(j=1;j<=n;j++){//定理4 的实?/span>  
    10.        if(A[i+1]==B[j]&&j>L[k-1][i]){  
    11.         L[k][i+1]=(j<L[k][i]?j:L[k][i]);  
    12.         break;  
    13.       }  
    14.       if(L[k][i+1]==0)  
    15.         L[k][i]=N;  
    16.      }  
    17.      if(L[k][m]==N)  
    18.       {p=k-1;break;}  
    19.   }  
    20.   p=k-1;  
    21. }  

    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>

 



鑫龙 2013-05-14 22:05 发表评论
]]>E序员编E艺?----W九?----闲话链表q赶问题http://m.shnenglu.com/mysileng/archive/2013/05/14/200264.html鑫龙鑫龙Tue, 14 May 2013 13:35:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/14/200264.htmlhttp://m.shnenglu.com/mysileng/comments/200264.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/14/200264.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200264.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200264.html作者:July、狂x创作l?br />出处Q?a target="_blank" style="color: #336699; text-decoration: initial;">http://blog.csdn.net/v_JULY_v ?/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>


W一节、求链表倒数Wk个结?/span>
W?3题、题目描qͼ
输入一个单向链表,输出该链表中倒数Wk个结?
链表的倒数W?个结点ؓ链表的尾指针?/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
{
    char data;
    ListNode* next;
};
ListNode* head,*p,*q;
ListNode *pone,*ptwo;

//@heyaming, W一?求链表倒数Wk个结点应该考虑k大于链表长度的case?br />ListNode* fun(ListNode *head,int k)
{
 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?/strong>
q是针对链表单项链表查找其中倒数Wk个结炏V试问,如果链表是双向的Q且可能存在环呢?LW二节、编E判断两个链表是否相交?br />


W二节、编E判断两个链表是否相?/span>
题目描述Q给Z个单向链表的头指针(如下图所C)

比如h1、h2Q判断这两个链表是否怺。这里ؓ了简化问题,我们假设两个链表均不带环?/p>

分析Q?/strong>q是来自~程之美上的微Y亚院的一道面试题目。请跟着我的思\步步深入Q部分文字引自编E之)Q?/p>

  1. 直接循环判断W一个链表的每个节点是否在第二个链表中。但Q这U方法的旉复杂度ؓO(Length(h1) * Length(h2))。显Ӟ我们得找CU更为有效的ҎQ至不能是OQN^2Q的复杂度?/li>
  2. 针对W一个链表直接构造hash表,然后查询hash表,判断W二个链表的每个l点是否在hash表出玎ͼ如果所有的W二个链表的l点都能在hash表中扑ֈQ即说明W二个链表与W一个链表有相同的结炏V时间复杂度ZؓU性:O(Length(h1) + Length(h2))Q同时ؓ了存储第一个链表的所有节点,I间复杂度ؓO(Length(h1))。是否还有更好的Ҏ呢,既能够以U性时间复杂度解决问题Q又能减存储空_
  3. q一步考虑“如果两个没有环的链表怺于某一节点Q那么在q个节点之后的所有节炚w是两个链表共有的”q个特点Q我们可以知道,如果它们怺Q则最后一个节点一定是共有的。而我们很Ҏ能得到链表的最后一个节点,所以这成了我们化解法的一个主要突破口。那么,我们只要判断俩个链表的尾指针是否相等。相{,则链表相交;否则Q链表不怺?br />所以,先遍历第一个链表,C最后一个节炏V然后遍历第二个链表Q到最后一个节Ҏ和第一个链表的最后一个节点做比较Q如果相同,则相交,否则Q不怺。这h们就得到了一个时间复杂度Q它为O((Length(h1) + Length(h2))Q而且只用了一个额外的指针来存储最后一个节炏V这个方法时间复杂度为线性OQNQ,I间复杂度ؓOQ?Q,昄比解法三更胜一{V?/li>
  4. 上面的问题都是针寚w表无环的Q?strong>那么如果现在Q链表是有环的呢?q能扑ֈ最后一个结点进行判断么?上面的方法还同样有效?昄Q这个问题的本质已经转化为判断链表是否有环。那么,如何来判断链表是否有环呢?

ȝQ?/strong>
所以,事实上,q个判断两个链表是否怺的问题就转化成了Q?br />1.先判断带不带?br />2.如果都不带环Q就判断节Ҏ否相{?br />3.如果都带环,判断一链表上俩指针盔R的那个节点,在不在另一条链表上?br />如果在,则相交,如果不在Q则不相交?/p>

    1?/strong>那么Q如何编写代码来判断链表是否有环?因ؓ很多的时候,你给Z问题的思\后,面试官可能还要追加你的代码,okQ如下(讄两个指针(p1, p2)Q初始值都指向_p1每次前进一步,p2每次前进二步Q如果链表存在环Q则p2先进入环Qp1后进入环Q两个指针在环中走动Q必定相遇)Q?/p>

 

  1. //copyright@ KurtWang  
  2. //July?011.05.27?/span>  
  3. struct Node  
  4. {  
  5.     int value;  
  6.     Node * next;  
  7. };  
  8.   
  9. //1.先判断带不带?/span>  
  10. //判断是否有环Q返回boolQ如果有环,q回环里的节?/span>  
  11. //思\Q用两个指针Q一个指针步长ؓ1Q一个指针步长ؓ2Q判断链表是否有?/span>  
  12. bool isCircle(Node * head, Node *& circleNode, Node *& lastNode)  
  13. {  
  14.     Node * fast = head->next;  
  15.     Node * slow = head;  
  16.     while(fast != slow && fast && slow)  
  17.     {  
  18.         if(fast->next != NULL)  
  19.             fast = fast->next;  
  20.           
  21.         if(fast->next == NULL)  
  22.             lastNode = fast;  
  23.         if(slow->next == NULL)  
  24.             lastNode = slow;  
  25.           
  26.         fast = fast->next;  
  27.         slow = slow->next;  
  28.           
  29.     }  
  30.     if(fast == slow && fast && slow)  
  31.     {  
  32.         circleNode = fast;  
  33.         return true;  
  34.     }  
  35.     else  
  36.         return false;  
  37. }  

 

    2&3?/strong>如果都不带环Q就判断节Ҏ否相{,如果都带环,判断一链表上俩指针盔R的那个节点,在不在另一条链表上。下面是l合解决q个问题的代码:

 

  1. //判断带环不带环时链表是否怺  
  2. //2.如果都不带环Q就判断节Ҏ否相{?/span>  
  3. //3.如果都带环,判断一链表上俩指针盔R的那个节点,在不在另一条链表上?/span>  
  4. bool detect(Node * head1, Node * head2)  
  5. {  
  6.     Node * circleNode1;  
  7.     Node * circleNode2;  
  8.     Node * lastNode1;  
  9.     Node * lastNode2;  
  10.       
  11.     bool isCircle1 = isCircle(head1,circleNode1, lastNode1);  
  12.     bool isCircle2 = isCircle(head2,circleNode2, lastNode2);  
  13.       
  14.     //一个有环,一个无?/span>  
  15.     if(isCircle1 != isCircle2)  
  16.         return false;  
  17.     //两个都无环,判断最后一个节Ҏ否相{?/span>  
  18.     else if(!isCircle1 && !isCircle2)  
  19.     {  
  20.         return lastNode1 == lastNode2;  
  21.     }  
  22.     //两个都有环,判断环里的节Ҏ否能到达另一个链表环里的节点  
  23.     else  
  24.     {  
  25.         Node * temp = circleNode1->next;  //updatedQ多谢苍?nbsp;and hyy?/span>  
  26.         while(temp != circleNode1)    
  27.         {  
  28.             if(temp == circleNode2)  
  29.                 return true;  
  30.             temp = temp->next;  
  31.         }  
  32.         return false;  
  33.     }  
  34.       
  35.     return false;  
  36. }  

 

扩展2Q求两个链表怺的第一个节?/strong>
思\Q在判断是否怺的过E中要分别遍历两个链表,同时记录下各自长度?/p>

    @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读《程序员~程艺术》,补充代码2012q??8?nbsp;周三下午10:15
    发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?/p>

  1. ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2)  
  2. {  
  3.       // Get the length of two lists  
  4.       unsigned int nLength1 = ListLength(pHead1);  
  5.       unsigned int nLength2 = ListLength(pHead2);  
  6.       int nLengthDif = nLength1 - nLength2;  
  7.   
  8.       // Get the longer list  
  9.       ListNode *pListHeadLong = pHead1;  
  10.       ListNode *pListHeadShort = pHead2;  
  11.       if(nLength2 > nLength1)  
  12.       {  
  13.             pListHeadLong = pHead2;  
  14.             pListHeadShort = pHead1;  
  15.             nLengthDif = nLength2 - nLength1;  
  16.       }  
  17.    
  18.       // Move on the longer list  
  19.       for(int i = 0; i < nLengthDif; ++ i)  
  20.             pListHeadLong = pListHeadLong->m_pNext;  
  21.    
  22.       // Move on both lists  
  23.       while((pListHeadLong != NULL) && (pListHeadShort != NULL) && (pListHeadLong != pListHeadShort))  
  24.       {  
  25.             pListHeadLong = pListHeadLong->m_pNext;  
  26.             pListHeadShort = pListHeadShort->m_pNext;  
  27.       }  
  28.    
  29.       // Get the first common node in two lists  
  30.       ListNode *pFisrtCommonNode = NULL;  
  31.       if(pListHeadLong == pListHeadShort)  
  32.             pFisrtCommonNode = pListHeadLong;  
  33.    
  34.       return pFisrtCommonNode;  
  35. }  
  36.   
  37. unsigned int ListLength(ListNode* pHead)  
  38. {  
  39.       unsigned int nLength = 0;  
  40.       ListNode* pNode = pHead;  
  41.       while(pNode != NULL)  
  42.       {  
  43.             ++ nLength;  
  44.             pNode = pNode->m_pNext;  
  45.       }  
  46.       return nLength;  
  47. }  

    关于判断单链表是否相交的问题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>
    在一条左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>

分析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:
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

再简单解释下上面的伪代码Q@bigQ:
A------------B
|                  |
在A到达B点前Q两者都只有W一条if为真Q即以相同的速度向右UdQ在A到达B后,A只满第二个ifQ即以两倍的速度向右UdQB依然只满第一个ifQ则速度保持不变Q经q|AB|/move_right个单位时_A可以追上B?/p>

 

     2?/strong>有个l节又出CQ正如ivan所_

if(at the position other robots have reached)
    move_right
    move_right

上面q个分支不一定能提速的。why?因ؓ如果if条gq旉很少Q而move指o发的旉很大Q实际很可能是这PQ那么两个机器h的速度q是基本是一L?/p>

那作如何修改?:

start:
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

-------

q样改后QA的速度应该比B快了?/p>

      3?/strong>然要是说每个指o处理速度都很快,AB岂不是一直以相同的速度右移?那到底该作何修改?LQ?/p>

go_step()
{
   向右
   向左
   向右
}
--------
三个旉单位才向右一?/p>

go_2step()
{
   向右
}
------

    一个时间单向右一步向左和向右q旉是同LQƈ且会占用一定时间?如果条g判定指o旉比移令花的时间较的话,应该上面两种步法Q后者比前者快。至此,׃的问题已l得到解冟?/p>



鑫龙 2013-05-14 21:35 发表评论
]]>
E序员编E艺?----W八?----从头臛_漫谈虚函?/title><link>http://m.shnenglu.com/mysileng/archive/2013/05/13/200230.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Mon, 13 May 2013 10:51:00 GMT</pubDate><guid>http://m.shnenglu.com/mysileng/archive/2013/05/13/200230.html</guid><wfw:comment>http://m.shnenglu.com/mysileng/comments/200230.html</wfw:comment><comments>http://m.shnenglu.com/mysileng/archive/2013/05/13/200230.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/mysileng/comments/commentRss/200230.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/mysileng/services/trackbacks/200230.html</trackback:ping><description><![CDATA[<p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">作者:July?br />出处Q?a target="_blank" style="color: #336699; text-decoration: initial;"><strong><span style="color: #002d93;">http://blog.csdn.net/v_JULY_v</span> </strong></a>?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"> </p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><span style="color: #800000;"><span style="font-size: 16px;">前奏</span></span></p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">    有关虚函数的问题层出不穷Q有兌函数的文章千一律,那ؓ何还要写q一有兌函数的文章呢?看完本文后,怿能懂其意义之所在。同Ӟ?span style="text-decoration: line-through;">狂想曲系?/span>已经更名?span style="text-decoration: underline;"><strong>E序员编E艺术系?/strong></span>Q因Z再只专注?#8220;面试”Q而在“~程”之上了。okQ如果有不正之处Q望不吝赐教。谢谢?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><br /><span style="font-size: 16px;"><span style="color: #800000;">W一节、一道简单的虚函数的面试?/span></span><br />题目要求Q写Z面程序的q行l果?<br /></p><div bg_cpp"="" style="border: 1px dashed #999999; background-color: #f5f5f5; width: 687.0499877929688px;"><ol start="1" style="margin-bottom: 0px; margin-left: 0px; padding-top: 5px; padding-bottom: 5px; position: relative;"><li style="border-left-style: none; line-height: 13px;"><span style="color: #999999;">//谢谢董天喆提供的q道癑ֺ的面试题 </span>  </li><li style="border-left-style: none; line-height: 13px;">#include <iostream>  </li><li style="border-left-style: none; line-height: 13px;"><span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std;  </li><li style="border-left-style: none; line-height: 13px;"><span style="color: #0000ff;">class</span> A{  </li><li style="border-left-style: none; line-height: 13px;">  <span style="color: #0000ff;">public</span>:<span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> p()   </li><li style="border-left-style: none; line-height: 13px;">  {   </li><li style="border-left-style: none; line-height: 13px;">    cout << <span style="color: #009900;">"A"</span> << endl;   </li><li style="border-left-style: none; line-height: 13px;">  }  </li><li style="border-left-style: none; line-height: 13px;">};  </li><li style="border-left-style: none; line-height: 13px;">  </li><li style="border-left-style: none; line-height: 13px;"><span style="color: #0000ff;">class</span> B : <span style="color: #0000ff;">public</span> A  </li><li style="border-left-style: none; line-height: 13px;">{  </li><li style="border-left-style: none; line-height: 13px;">  <span style="color: #0000ff;">public</span>:<span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> p()   </li><li style="border-left-style: none; line-height: 13px;">  { cout << <span style="color: #009900;">"B"</span> << endl;  </li><li style="border-left-style: none; line-height: 13px;">  }  </li><li style="border-left-style: none; line-height: 13px;">};  </li><li style="border-left-style: none; line-height: 13px;">  </li><li style="border-left-style: none; line-height: 13px;"><span style="color: #2e8b57; font-weight: bold;">int</span> main()   </li><li style="border-left-style: none; line-height: 13px;">{  </li><li style="border-left-style: none; line-height: 13px;">  A * a = <span style="color: #0000ff;">new</span> A;  </li><li style="border-left-style: none; line-height: 13px;">  A * b = <span style="color: #0000ff;">new</span> B;  </li><li style="border-left-style: none; line-height: 13px;">  a->p();  </li><li style="border-left-style: none; line-height: 13px;">  b->p();  </li><li style="border-left-style: none; line-height: 13px;">  <span style="color: #0000ff;">delete</span> a;  </li><li style="border-left-style: none; line-height: 13px;">  <span style="color: #0000ff;">delete</span> b;      </li><li style="border-left-style: none; line-height: 13px;">  <span style="color: #0000ff;">return</span> 0;  </li><li style="border-left-style: none; line-height: 13px;">}  </li></ol></div><p> </p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">    我想Q这道面试题应该是考察虚函数相关知识的相对单的一道题目了。然后,希望你碰到此cL兌函数的面试题Q不论其隑ֺ是难是易Q都能够举一反三Q那么本章的目的也就辑ֈ了。okQ请跟着我的思\Q咱们步步深入(上面E序的输出结果ؓA BQ?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"> </p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"> </p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><span style="font-size: 16px;"><span style="color: #800000;">W二节、有无虚函数的区?/span></span><br />      <strong>1?/strong>当上q程序中的函数p()不是虚函敎ͼ那么E序的运行结果是如何?卛_下代码所C:</p><blockquote style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><p>class A<br />{<br />public:<br /> void p() <br /> { <br />  cout << "A" << endl; <br /> }<br /> <br />};</p><p>class B : public A<br />{<br />public:<br /> void p() <br /> { <br />  cout << "B" << endl;<br /> }<br />};</p></blockquote><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">对的Q程序此时将输出两个AQA。ؓ什?<br />我们知道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><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">      <strong>2?/strong>那如果加上虚函数?卛_最开始的那段E序那样Q程序的输出l果Q将是什?<br />在此之前Q我们还得明以下两点:<br />    a、通过基类引用或指针调用基cM定义的函数时Q我们ƈ不知道执行函数的对象的确切类型,执行函数的对象可能是基类cd的,也可能是zcd的?br />    b、如果调用非虚函敎ͼ则无论实际对象是什么类型,都执行基cȝ型所定义的函敎ͼ如上q第1Ҏqͼ。如果调用虚函数Q则直到q行时才能确定调用哪个函敎ͼq行的虚函数是引用所l定的或指针所指向的对象所属类型定义的版本?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">Ҏ上述b的观点,我们知道Q如果加上虚函数Q如上面q道面试题,</p><blockquote style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><p>class A<br />{<br />public:<br /> virtual void p() <br /> { <br />  cout << "A" << endl; <br /> }<br /> <br />};</p><p>class B : public A<br />{<br />public:<br /> virtual void p() <br /> { <br />  cout << "B" << endl;<br /> }<br />};</p><p>int main() <br />{<br /> A * a = new A;<br /> A * b = new B;<br /> a->p();<br /> b->p();<br /> delete a;<br /> delete b;<br />    return 0;<br />}</p><p> </p></blockquote><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">那么E序的输出结果将是A B?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">所以,xQ咱们的q道面试题已l解冟뀂但虚函数的问题Q还没有解决?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><br /><span style="color: #800000;"><span style="font-size: 16px;">W三节、虚函数的原理与本质</span></span><br />    我们已经知道Q虚QvirtualQ函数的一般实现模型是Q每一个类QclassQ有一个虚表(virtual tableQ,内含该class之中有作用的虚(virtualQ函数的地址Q然后每个对象有一个vptrQ指向虚表(virtual tableQ的所在?/p><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">请允许我援引自深度探索c++对象模型一书上的一个例子:</p><blockquote style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><p>class Point { <br />public: <br />   virtual ~Point();  </p><p>   virtual Point& mult( float ) = 0; </p><p>   float x() const { return _x; }     //非虚函数Q不作存?br />   virtual float y() const { return 0; }  <br />   virtual float z() const { return 0; }  <br />   // ...</p><p>protected: <br />   Point( float x = 0.0 ); <br />   float _x; <br />};</p></blockquote><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">      <strong>1?/strong>在Point的对象pt中,有两个东西,一个是数据成员_xQ一个是_vptr_Point。其中_vptr_Point指向着virtual table pointQ而virtual tableQ虚表)point中存储着以下东西Q?/p><ul style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><li>virtual ~Point()被赋值slot 1Q?/li><li>mult() 被赋值slot 2.</li><li>y() is 被赋值slot 3</li><li>z() 被赋值slot 4.</li></ul><blockquote style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><p>class Point2d : public Point { <br />public: <br />   Point2d( float x = 0.0, float y = 0.0 )  <br />      : Point( x ), _y( y ) {} <br />   ~Point2d();   //1</p><p>   //改写base class virtual functions <br />   Point2d& mult( float );  //2<br />   float y() const { return _y; }  //3</p><p>protected: <br />   float _y; <br />};</p></blockquote><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">      <strong>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><blockquote style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><p>class Point3d: public Point2d { <br />public: <br />   Point3d( float x = 0.0, <br />            float y = 0.0, float z = 0.0 ) <br />      : Point2d( x, y ), _z( z ) {} <br />   ~Point3d();</p><p>   // overridden base class virtual functions <br />   Point3d& mult( float ); <br />   float z() const { return _z; }</p><p>   // ... other operations ... <br />protected: <br />   float _z; <br />};</p></blockquote><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">      <strong>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><p style="color: #333333; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;">okQ上q???所有情늚详情Q请参考下图?br /><img src="http://m.shnenglu.com/images/cppblog_com/mysileng/QQ截图20130513184606.jpg" width="532" height="571" alt="" /><br /><p style="font-size: 14px;">本文Q日后可能会酌情考虑增补有关内容。okQ更多,可参考深度探索c++对象模型一书第四章?br />最q几章难度都比较,是考虑到狂x有深有浅的原则,后箋章节会逐步恢复到相应难度?/p><p style="font-size: 14px;"> </p><p style="font-size: 14px;"><span style="color: #800000;"><span style="font-size: 16px;">W四节、虚函数的布局与汇~层面的考察</span></span></p><p style="font-size: 14px;">      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;"><span style="color: #002d93;">http://blog.csdn.net/linyt/archive/2011/04/20/6336762.aspx</span></a>?/p><p style="font-size: 14px;"> </p><p style="font-size: 14px;"><span style="color: #800000;"><span style="font-size: 16px;">W五节、虚函数表的详解</span></span></p><p style="font-size: 14px;">    本节全部内容来自淄博的共享,非常感谢。注@molixiaogemaoQ?span style="font-family: Arial, Helvetica, sans-serif; line-height: 20px;"><strong>只有发生l承的时候且父类子类都有virtual的时候才会出现虚函数指针Q请不要忘了虚函数出现的目的是ؓ了实现多?/strong>?/span><br /> </p><blockquote style="font-size: 14px; background-color: #ffffff;"><p><span style="font-size: 13px;"><strong> 一般承(无虚函数覆盖Q?/strong><br /> 下面Q再让我们来看看l承时的虚函数表是什么样的。假设有如下所C的一个承关p:<br /><img src="http://m.shnenglu.com/images/cppblog_com/mysileng/QQ截图20130513184726.jpg" width="195" height="208" alt="" /><br /></span><p style="font-size: 14px;"><span style="font-size: 13px;">h意,在这个承关pMQ子cL有重载Q何父cȝ函数。那么,在派生类的实例中Q?/span></p><p style="font-size: 14px;"><span style="font-size: 13px;"> 对于实例QDerive d; 的虚函数表如下:</span></p><span style="font-size: 13px;"><img src="http://m.shnenglu.com/images/cppblog_com/mysileng/QQ截图20130513184808.jpg" width="561" height="137" alt="" /><br /></span><p style="font-size: 14px;"><span style="font-size: 13px;">我们从表中可以看C面几点,<br /> 1Q覆盖的f()函数被放C虚表中原来父c虚函数的位|?br /> 2Q没有被覆盖的函C旧?br /> <br /> q样Q我们就可以看到对于下面q样的程序,<br /> Base *b = new Derive();</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">b->f();</span></p><p style="font-size: 14px;"><strong><span style="font-size: 13px;">由b所指的内存中的虚函数表的f()的位|已l被Derive::f()函数地址所取代Q?br />于是在实际调用发生时Q是Derive::f()被调用了。这实C多态?/span></strong></p><p style="font-size: 14px;"><br /><strong><span style="font-size: 13px;">多重l承Q无虚函数覆盖)</span></strong></p><p style="font-size: 14px;"><span style="font-size: 13px;">下面Q再让我们来看看多重l承中的情况Q假设有下面q样一个类的承关p(注意Q子cdƈ没有覆盖父类的函敎ͼQ?/span></p><span style="font-size: 13px;"><img src="http://m.shnenglu.com/images/cppblog_com/mysileng/QQ截图20130513184842.jpg" width="530" height="456" alt="" /><br /></span><p style="font-size: 14px;"><span style="font-size: 13px;">我们可以看到Q?br />1Q?每个父类都有自己的虚表?br />2Q?子类的成员函数被攑ֈ了第一个父cȝ表中。(所谓的W一个父cL按照声明序来判断的Q?/span></p><p style="font-size: 14px;"><span style="font-size: 13px;">q样做就是ؓ了解决不同的父类cd的指针指向同一个子cd例,而能够调用到实际的函数?/span></p><p style="font-size: 14px;"><br /><span style="font-size: 13px;"><strong>多重l承Q有虚函数覆盖)</strong><br />下面我们再来看看Q如果发生虚函数覆盖的情c?br />下图中,我们在子cM覆盖了父cȝf()函数?/span></p><span style="font-size: 13px;"><img src="http://m.shnenglu.com/images/cppblog_com/mysileng/QQ截图20130513184926.jpg" width="455" height="442" alt="" /><br /></span><p style="font-size: 14px;"><span style="font-size: 13px;">我们可以看见Q三个父c虚函数表中的f()的位|被替换成了子类的函数指针?br />q样Q我们就可以M静态类型的父类来指向子c,q调用子cȝf()了。如Q?/span></p><p style="font-size: 14px;"><span style="font-size: 13px;">Derive d;<br />Base1 *b1 = &d;<br />Base2 *b2 = &d;<br />Base3 *b3 = &d;<br />b1->f(); //Derive::f()<br />b2->f(); //Derive::f()<br />b3->f(); //Derive::f()<br />b1->g(); //Base1::g()<br />b2->g(); //Base2::g()<br />b3->g(); //Base3::g()</span></p><p style="font-size: 14px;"><span style="font-size: 13px;"> </span></p><p style="font-size: 14px;"><span style="font-size: 13px;"><strong>安全?/strong><br />每次写C++的文章,d不了要批判一下C++?br />q篇文章也不例外。通过上面的讲qͼ怿我们对虚函数表有一个比较细致的了解了?br />水可载舟Q亦可覆舟。下面,让我们来看看我们可以用虚函数表来q点什么坏事吧?/span></p><p style="font-size: 14px;"><span style="font-size: 13px;"><strong>一、通过父类型的指针讉K子类自己的虚函数</strong><br />我们知道Q子cL有重载父cȝ虚函数是一件毫无意义的事情。因为多态也是要Z函数重蝲的?br />虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数Q但我们Ҏ不可能用下面的语句来调用子cȝ自有虚函敎ͼ</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">Base1 *b1 = new Derive();<br />b1->g1(); //~译出错</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">M妄图使用父类指针惌用子cM的未覆盖父类的成员函数的行ؓ都会被编译器视ؓ非法Q?span style="text-decoration: underline;">卛_cL针不能调用子c自己定义的成员函数?/span>所以,q样的程序根本无法编译通过?br />但在q行Ӟ我们可以通过指针的方式访问虚函数表来辑ֈq反C++语义的行为?br />Q关于这斚w的尝试,通过阅读后面附录的代码,怿你可以做到这一点)</span></p><p style="font-size: 14px;"><span style="font-size: 13px;"><strong>二、访问non-public的虚函数</strong><br />另外Q如果父cȝ虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,<br />所以,我们同样可以使用讉K虚函数表的方式来讉Kq些non-public的虚函数Q这是很Ҏ做到的?br />如:</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">class Base {<br />private: <br /> virtual void f() { cout << "Base::f" << endl; } <br />};</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">class Derive : public Base{ <br />};<br />typedef void(*Fun)(void);<br />void main() {<br /> Derive d;<br /><strong> Fun pFun = (Fun)*((int*)*(int*)(&d)+0);</strong><br /> pFun(); <br />}</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">对上面粗体部分的解释Q@a && x</span><span style="font-size: 13px;">Q:</span></p><p style="font-size: 14px;"><span style="font-size: 13px;">1. (int*)(&d)取vptr地址Q该地址存储的是指向vtbl的指?br />2. (int*)*(int*)(&d)取vtbl地址Q该地址存储的是虚函数表数组<br />3. (Fun)*((int*)*(int*)(&d) +0)Q取vtbl数组的第一个元素,即Base中第一个虚函数f的地址<br />4. (Fun)*((int*)*(int*)(&d) +1)Q取vtbl数组的第二个元素Q这W?点,如下图所C)?/span></p><p style="font-size: 14px;"><span style="font-size: 13px;">下图也能很清晰的说明一些东西(@5Q:</span></p><span style="font-size: 13px;"><img src="http://m.shnenglu.com/images/cppblog_com/mysileng/QQ截图20130513185001.jpg" width="631" height="461" alt="" /><br /></span><blockquote style="font-size: 14px; background-color: #ffffff;"><p><span style="font-size: 13px;">okQ再来看一个问题,如果一个子c重载的虚拟函数为priveteQ那么通过父类的指针可以访问到它吗Q?/span></p><p><span style="font-size: 13px;">#include <IOSTREAM>   <br />class B   <br />{    <br />public:    <br />    virtual void fun()      <br />    {     <br />        std::cout << "base fun called";     <br />    };    <br />};  </span></p><p><span style="font-size: 13px;">class D : public B    <br />{    <br />private:   <br />    virtual void fun()      <br />    {     <br />        std::cout << "driver fun called";    <br />    };    <br />};  </span></p><p><span style="font-size: 13px;">int main(int argc, char* argv[])   <br />{       <br />    B* p = new D();    <br />    p->fun();    <br />    return 0;    <br />}  </span><span style="font-size: 13px;"><br /><strong>q行时会输出 driver fun called</strong></span></p><p><span style="font-size: 13px;">从这个实验,可以更深入的了解虚拟函数~译时的一些特?<br />在编译虚拟函数调用的时候,例如p->fun(); 只是按其静态类型来处理? 在这里p的类型就是BQ不会考虑其实际指向的cdQ动态类型)?/span><br /><span style="font-size: 13px;">    也就是说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></p><p><br /><span style="font-size: 13px;">Zq一步的实验Q可以将B里的fun改ؓprivate的,D里的改ؓpublic的,则编译就会出错?br />C++的注意条ƾ中有一? l不重新定义l承而来的缺省参数? <br />QEffective C++ Item37Q?never redefine a function's inherited default parameter value) 也是同样的道理?/span></p><p><span style="font-size: 13px;"><strong>可以再做个实?/strong><br />class B   <br />{    <br />public:   <br />    virtual void fun(int i = 1)      <br />    {     <br />        std::cout << "base fun called, " << i;     <br />    };    <br />};  </span></p><p><span style="font-size: 13px;">class D : public B    <br />{    <br />private:    <br />    virtual void fun(int i = 2)      <br />    {     <br />        std::cout << "driver fun called, " << i;     <br />    };    <br />}; </span></p><p> </p><p> </p><strong><span style="font-size: 13px;">则运行会输出driver fun called, 1</span></strong><p> </p><p><span style="font-size: 13px;">关于q一点,Effective上讲的很清楚“virtual 函数pd态绑定, 而缺省参数却是静态绑?#8221;Q?br />也就是说在编译的时候已l按照p的静态类型处理其默认参数?转换成了(*p->vptr[1])(p, 1)q样的方式?/span><span style="font-size: 13px;"> </span></p><p><strong><span style="font-size: 13px;">补遗</span></strong></p><p><span style="font-size: 13px;">   一个类如果有虚函数Q不是几个虚函敎ͼ都会个类声明一个虚函数表,q个虚表是一个含有虚函数的类的,不是说是cd象的。一个含有虚函数的类Q不有多少个数据成员,每个对象实例都有一个虚指针Q在内存中,存放每个cd象的内存区,在内存区的头部都是先存放q个指针变量的(准确的说Q应该是Q视~译器具体情况而定Q,从第nQn视实际情况而定Q个字节才是q个对象自己的东ѝ?/span></p><p> </p><p><span style="font-size: 13px;">下面再说下通过基类指针Q调用虚函数所发生的一切:<br />One *p;<br />p->disp();</span></p><p><span style="font-size: 13px;">1、上来要取得cȝ虚表的指针,是要得刎ͼ虚表的地址。存攄对象的内存区的前四个字节其实是用来存放虚表的地址的?br />2、得到虚表的地址后,从虚表那知道你调用的那个函数的入口地址。根据虚表提供的你要扄函数的地址。ƈ调用函数Q你要知道,那个虚表是一个存放指针变量的数组Qƈ不是_那个虚表中就是存攄虚函数的实体?/span></p></blockquote><p style="font-size: 14px;">本章完?/p></p></blockquote><br /><br /></p><img src ="http://m.shnenglu.com/mysileng/aggbug/200230.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/mysileng/" target="_blank">鑫龙</a> 2013-05-13 18:51 <a href="http://m.shnenglu.com/mysileng/archive/2013/05/13/200230.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>E序员编E艺?----W七?----求连l子数组的最大和http://m.shnenglu.com/mysileng/archive/2013/05/13/200229.html鑫龙鑫龙Mon, 13 May 2013 10:44:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/13/200229.htmlhttp://m.shnenglu.com/mysileng/comments/200229.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/13/200229.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200229.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200229.html E序员编E艺术:W七章、求q箋子数l的最大和 

作者:July?br />出处Q?a target="_blank" style="color: #336699; text-decoration: initial;">http://blog.csdn.net/v_JULY_v ?/p>


前奏

  • 希望更多的h能和我一P把本狂想曲系列中的Q何一道面试题当做一道简单的~程题或一个实质性的问题来看待,在阅L狂想曲系列的q程中,希望你能量暂时放下所有有关面试的一切包袱,潜心d每一?#8220;~程?#8221;Q在解决~程题的q程中,好好享受~程带来的无限乐,与思考带来的无限Ȁ情?-By@July_____?/li>
  • 原狂xpd已更名ؓQ?a target="_blank" style="color: #336699; text-decoration: initial;">E序员编E艺术系?/strong>。原狂想曲创作组更名?span style="text-decoration: underline;">~程艺术?/span>。编E艺术室致力于以下三点工作:1、针对一个问题,不断L更高效的法Qƈ予以~程实现?、解军_际中会碰到的应用问题Q如W十章、如何给10^7个数据量的磁盘文件排?/strong>?、经典算法的研究与实现。MH出一点:~程Q如何高效的~程解决实际问题。欢q有志者加入?/li>


W一节、求子数l的最大和
3.求子数组的最大和
题目描述Q?/strong>
输入一个整形数l,数组里有正数也有负数?br />数组中连l的一个或多个整数l成一个子数组Q每个子数组都有一个和?br />求所有子数组的和的最大倹{要求时间复杂度为O(n)?/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:

//本段代码引自~程之美
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;

   sum=0;   //q里要记得清Ӟ否则的话sum最l存攄是所有子数组的和。也是~程之美上所说的bug。多谢苍狹{?br />  }
 }
 return maximum;

      2?/strong>其实q个问题Q在我之前上传的微Y100题,{案V0.2版[W?-20题答案]Q便直接l出了以下OQNQ的法Q?/p>

  1. //copyright@ July 2010/10/18  
  2. //updatedQ?011.05.25.  
  3. #include <iostream.h>  
  4.   
  5. int maxSum(int* a, int n)  
  6. {  
  7.     int sum=0;  
  8.     //其实要处理全是负数的情况Q很单,如稍后下面第3Ҏ见,直接把这句改成:"int sum=a[0]"卛_  
  9.     //也可以不改,当全是负数的情况Q直接返?Q也不见得不行?/span>  
  10.     int b=0;  
  11.       
  12.     for(int i=0; i<n; i++)  
  13.     {  
  14.         if(b<0)           //...  
  15.             b=a[i];  
  16.         else  
  17.             b+=a[i];  
  18.         if(sum<b)  
  19.             sum=b;  
  20.     }  
  21.     return sum;  
  22. }  
  23.   
  24. int main()  
  25. {  
  26.     int a[10]={1, -2, 3, 10, -4, 7, 2, -5};  
  27.     //int a[]={-1,-2,-3,-4};  //试全是负数的用?/span>  
  28.     cout<<maxSum(a,8)<<endl;  
  29.     return 0;  
  30. }  
  31.   
  32. /*------------------------------------- 
  33. 解释下: 
  34. 例如输入的数lؓ1, -2, 3, 10, -4, 7, 2, -5Q?/span> 
  35. 那么最大的子数lؓ3, 10, -4, 7, 2Q?/span> 
  36. 因此输出子数l的?8?/span> 
  37.  
  38. 所有的东西都在以下俩行Q?/span> 
  39. 卻I 
  40. b  Q?nbsp; 0  1  -1  3  13   9  16  18  13   
  41. sumQ?nbsp; 0  1   1  3  13  13  16  18  18 
  42.    
  43. 其实法很简单,当前面的几个敎ͼ加v来后Qb<0后, 
  44. 把b重新赋||ؓ下一个元素,b=a[i]?/span> 
  45. 当b>sumQ则更新sum=b; 
  46. 若b<sumQ则sum保持原|不更新。。July?0/31?/span> 
  47. ----------------------------------*/  

 

      3?/strong>不少朋友看到上面的答案之后,认ؓ上述思\2的代码,没有处理全是负数的情况,当全是负数的情况Ӟ我们可以让程序返?Q也可以让其q回最大的那个负数Q下面便是前几日重写的,修改后的处理全是负数情况Q返回最大的负数Q的代码Q?/p>

  1. //copyright@ July  
  2. //July、updatedQ?011.05.25?/span>  
  3. #include <iostream.h>  
  4. #define n 4           //多定义了一个变?nbsp; 
  5.   
  6. int maxsum(int a[n])    
  7. //于此处,你能看到上述思\2代码Q指针)的优?/span>  
  8. {  
  9.     int max=a[0];       //全负情况Q返回最大数  
  10.     int sum=0;  
  11.     for(int j=0;j<n;j++)  
  12.     {  
  13.         if(sum>=0)     //如果加上某个元素Qsum>=0的话Q就?/span>  
  14.             sum+=a[j];  
  15.         else     
  16.             sum=a[j];  //如果加上某个元素Qsum<0了,׃?/span>  
  17.         if(sum>max)  
  18.             max=sum;  
  19.     }  
  20.     return max;  
  21. }  
  22.   
  23. int main()  
  24. {  
  25.     int a[]={-1,-2,-3,-4};  
  26.     cout<<maxsum(a)<<endl;  
  27.     return 0;  
  28. }  

 

      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])
result = max(result, sum[i])
 

扩展Q?/strong>
1、如果数l是二维数组Q同栯你求最大子数组的和?
2、如果是要你求子数组的最大乘U列?
3、如果同时要求输出子D늚开始和l束?

 

W二节、Data structures and Algorithm analysis in C

下面l出《Data structures and Algorithm analysis in C》中4U实现?/p>

  1. //感谢|友firo  
  2. //July?010.06.05?/span>  
  3.   
  4. //Algorithm 1:旉效率为O(n*n*n)  
  5. int MaxSubsequenceSum1(const int A[],int N)  
  6. {  
  7.     int ThisSum=0 ,MaxSum=0,i,j,k;  
  8.     for(i=0;i<N;i++)  
  9.         for(j=i;j<N;j++)  
  10.         {  
  11.             ThisSum=0;  
  12.             for(k=i;k<j;k++)  
  13.                 ThisSum+=A[k];  
  14.               
  15.             if(ThisSum>MaxSum)  
  16.                 MaxSum=ThisSum;  
  17.         }  
  18.         return MaxSum;  
  19. }  
  20.   
  21. //Algorithm 2:旉效率为O(n*n)  
  22. int MaxSubsequenceSum2(const int A[],int N)  
  23. {  
  24.     int ThisSum=0,MaxSum=0,i,j,k;  
  25.     for(i=0;i<N;i++)  
  26.     {  
  27.         ThisSum=0;  
  28.         for(j=i;j<N;j++)  
  29.         {  
  30.             ThisSum+=A[j];  
  31.             if(ThisSum>MaxSum)  
  32.                 MaxSum=ThisSum;  
  33.         }  
  34.     }  
  35.     return MaxSum;  
  36. }  
  37.   
  38. //Algorithm 3:旉效率为O(n*log n)  
  39. //法3的主要思想Q采用二分策略,序列分成左右两份?/span>  
  40. //那么最长子序列有三U可能出现的情况Q即  
  41. //?】只出现在左部分.  
  42. //?】只出现在右部分?/span>  
  43. //?】出现在中间Q同时涉及到左右两部分?/span>  
  44. //分情况讨Z?/span>  
  45. static int MaxSubSum(const int A[],int Left,int Right)  
  46. {  
  47.     int MaxLeftSum,MaxRightSum;              //左、右部分最大连l子序列倹{对应情c?】、??/span>  
  48.     int MaxLeftBorderSum,MaxRightBorderSum;  //从中间分别到左右两侧的最大连l子序列|对应case?】?/span>  
  49.     int LeftBorderSum,RightBorderSum;  
  50.     int Center,i;  
  51.     if(Left == Right)Base Case  
  52.         if(A[Left]>0)  
  53.             return A[Left];  
  54.         else  
  55.             return 0;  
  56.         Center=(Left+Right)/2;  
  57.         MaxLeftSum=MaxSubSum(A,Left,Center);  
  58.         MaxRightSum=MaxSubSum(A,Center+1,Right);  
  59.         MaxLeftBorderSum=0;  
  60.         LeftBorderSum=0;  
  61.         for(i=Center;i>=Left;i--)  
  62.         {  
  63.             LeftBorderSum+=A[i];  
  64.             if(LeftBorderSum>MaxLeftBorderSum)  
  65.                 MaxLeftBorderSum=LeftBorderSum;  
  66.         }  
  67.         MaxRightBorderSum=0;  
  68.         RightBorderSum=0;  
  69.         for(i=Center+1;i<=Right;i++)  
  70.         {  
  71.             RightBorderSum+=A[i];  
  72.             if(RightBorderSum>MaxRightBorderSum)  
  73.                 MaxRightBorderSum=RightBorderSum;  
  74.         }  
  75.         int max1=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum;  
  76.         int max2=MaxLeftBorderSum+MaxRightBorderSum;  
  77.         return max1>max2?max1:max2;  
  78. }  
  79.   
  80. //Algorithm 4:旉效率为O(n)  
  81. //同上q第一节中的思\3、和4?/span>  
  82. int MaxSubsequenceSum(const int A[],int N)  
  83. {  
  84.     int ThisSum,MaxSum,j;  
  85.     ThisSum=MaxSum=0;  
  86.     for(j=0;j<N;j++)  
  87.     {  
  88.         ThisSum+=A[j];  
  89.         if(ThisSum>MaxSum)  
  90.             MaxSum=ThisSum;  
  91.         else if(ThisSum<0)  
  92.             ThisSum=0;  
  93.     }  
  94.     return MaxSum;  
  95. }   
  

 

本章完?/p>

鑫龙 2013-05-13 18:44 发表评论
]]>
E序员编E艺?----W六?----求解500万以内的亲和?素数、完?http://m.shnenglu.com/mysileng/archive/2013/05/13/200226.html鑫龙鑫龙Mon, 13 May 2013 09:43:00 GMThttp://m.shnenglu.com/mysileng/archive/2013/05/13/200226.htmlhttp://m.shnenglu.com/mysileng/comments/200226.htmlhttp://m.shnenglu.com/mysileng/archive/2013/05/13/200226.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/200226.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/200226.html作者:上善若水、July、yansha?br />出处Q?a target="_blank" style="color: #336699; text-decoration: initial;">http://blog.csdn.net/v_JULY_v ?/p>


前奏
    本章陆箋开始,除了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>
    了解了什么是亲和敎ͼ接下来咱们一步一步来解决上面提出的问题(以下内容大部引自水的原话Q同时水哥有一句原话,“在你真正弄弄懂这个范例之前,你不配说你懂数据l构和算?/strong>”Q?/p>

  1. 看到q个问题后,W一x是什么?模拟搜烦+剪枝Q回溯?旉复杂度有多大Q其中bn为an的伪亲和敎ͼ即bn是an的真因数之和大约是多?臛_?0^13Q@iicupQN^1.5 对于5*10^6 , ơ数大致 10^10 而不?10^13.Q的数量U的。那么对于每U千万次q算的计机来说Q大概在1000多天也就?q内可以搞定了Qiicup的计? 10^13 / 10^7 =1000000(U? 大约 278 时. Q。如果是Zq个基数在优化,你无法在一天内得到l果的?/li>
  2. 一个不错的法应该在半时之内搞定q个问题Q当然这L法有很多。节U时间的做法是可以生成伴随数l,也就是空间换旉Q但是那PI间代h太大Q因为数据规模庞大?/li>
  3. 在稍后的法中,依然使用的伴随数l,只不q,因ؓ题目的特D性,只是它方便和巧妙地利用了下标作ؓ伴随数组Q来节约旉。同Ӟ回溯的思想换成递推的思想Q预处理数组的时间复杂度为logNQ调和敎ͼ*NQ扫描数l的旉复杂度ؓU性OQNQ。所以,ȝ旉复杂度ؓOQN*logN+NQ(其中logN和敎ͼ  Q?/li>


W二节、伴随数l线性遍?/span>
依据上文中的W?Ҏ\Q编写如下代码:

int sum[5000010];   //为防界  
  
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;  
}  

W三节、程序的构造与解释
    我再来具体解释下上述E序的原理,okQD个例子,假设是求10以内的亲和数Q求解步骤如下:

因ؓ所有数的真因数都包?Q所以,先在各个数的下方全部|?

  1. 然后取i=2,3,4,5Qi<=10/2Q,j依次对应的位|ؓj=Q????0Q,Q??Q?Q?Q?Q?0Q各数所对应的位|?/li>
  2. 依据j所扑ֈ的位|,在j所指的各个数的下面加上各个真因子iQi=2???Q?br />整个q程Q即如下图所C(如sum[6]=1+2+3=6Qsum[10]=1+2+5=8.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
  3. 然后一ơ遍历i?20开始到5000000Qi每遍历一个数后,
    i对应的数下面的各个真因子加v来得C个和sum[i]Q如果这个和sum[i]==某个i’Q且sum[i‘]=iQ?br />那么q两个数i和i’Q即Z对亲和数?/li>
  4. i=2Qsum[4]+=2Qsum[6]+=2Qsum[8]+=2Qsum[10]+=2Qsum[12]+=2...
    i=3Qsum[6]+=3Qsum[9]+=3...
    ......
  5. i=220Ӟsum[220]=284Qi=284Ӟsum[284]=220Q即sum[220]=sum[sum[284]]=284Q?br />得出220?84是一对亲和数。所以,最l输?20?84Q?..





鑫龙 2013-05-13 17:43 发表评论
]]>
E序员编E艺?----W五?----L满和ؓ定值的两个或多个数http://m.shnenglu.com/mysileng/archive/2012/11/30/195849.html鑫龙鑫龙Fri, 30 Nov 2012 13:09:00 GMThttp://m.shnenglu.com/mysileng/archive/2012/11/30/195849.htmlhttp://m.shnenglu.com/mysileng/comments/195849.htmlhttp://m.shnenglu.com/mysileng/archive/2012/11/30/195849.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/195849.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/195849.html                    E序员编E艺术:W五章、寻扑֒为定值的两个或多个数
 

    作者: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>


W一节、寻扑֒为定值的两个?/span>
W?4题(数组Q:
题目Q输入一个数l和一个数字,在数l中查找两个敎ͼ使得它们的和正好是输入的那个数字?br />要求旉复杂度是O(n)。如果有多对数字的和{于输入的数字,输出L一对即可?br />例如输入数组1????1?5和数?5。由?+11=15Q因此输??1?/p>

分析Q?/p>

׃试着一步一步解册个问题(注意阐述中数列有序无序的区别Q:

  1. 直接IDQ从数组中Q意选取两个敎ͼ判定它们的和是否入的那个数字。此丑֤杂度为OQN^2Q。很昄Q我们要L效率更高的解法?/li>
  2. 题目相当于,Ҏ个a[i]Q然后查扑ֈ断sum-a[i]是否也在原始序列中,每一ơ要查找的时间都要花费ؓOQNQ,q样下来Q最l找C个数q是需要OQN^2Q的复杂度。那如何提高查找判断的速度?对了Q二分查找,原来OQNQ的查找旉提高到OQlogNQ,q样对于N个a[i]Q都要花logN的时间去查找相对应的sum-a[i]是否在原始序列中Qȝ旉复杂度已降ؓOQN*logNQ,且空间复杂度为OQ?Q。(如果有序Q直接二分OQN*logNQ,如果无序Q先排序后二分,复杂度同样ؓOQN*logN+N*logNQ?OQN*logNQ,I间MؓOQ?Q)?/strong>
  3. 有没有更好的办法?׃可以依据上述思\2的思想Qa[i]在序列中Q如果a[i]+a[k]=sum的话Q那么sum-a[i]Qa[k]Q也必然在序列中Q,举个例子Q如下:
    原始序列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?/li>
  4. 当然Q你q可以构造hash表,正如~程之美上的所qͼl定一个数字,Ҏhash映射查找另一个数字是否也在数l中Q只需用OQ?Q的旉Q这L话,M的算法通上q思\3 一P也能降到OQNQ,但有个缺P是构造hash额外增加了OQNQ的I间Q此点同上述思\ 3。不q,I间换时_仍不׃ؓ在时间要求较严格的情况下的一U好办法?/li>
  5. 如果数组是无序的Q先排序Qn*lognQ,然后用两个指针iQjQ各自指向数l的首尾两端Qoi=0Qj=n-1Q然后i++Qj--Q逐次判断a[i]+a[j]?=sumQ如果某一刻a[i]+a[j]>sumQ则要想办法让sum的值减,所以此刻i不动Qj--Q如果某一刻a[i]+a[j]<sumQ则要想办法让sum的值增大,所以此刻i++Qj不动。所以,数组无序的时候,旉复杂度最lؓOQn*logn+nQ?OQn*lognQ,若原数组是有序的Q则不需要事先的排序Q直接OQnQ搞定,且空间复杂度q是OQ?Q,此思\是相对于上述所有思\的一U改q?strong>。(如果有序Q直接两个指针两端扫描,旉OQNQ,如果无序Q先排序后两端扫描,旉OQN*logN+NQ?OQN*logNQ,I间始终都ؓOQ?Q)。(与上q思\2相比Q排序后的时间开销׃前的二分的n*logn降到了扫描的OQNQ?strong>Q?/strong>

ȝQ?/p>

  • 不论原序列是有序q是无序Q解册c题有以下三U办法:1、二分(若无序,先排序后二分Q,旉复杂度MؓOQn*lognQ,I间复杂度ؓOQ?Q;2、扫描一遍X-S[i]  映射C个数l或构造hash表,旉复杂度ؓOQnQ,I间复杂度ؓOQnQ;3、两个指针两端扫描(若无序,先排序后扫描Q,旉复杂度最后ؓQ有序OQnQ,无序OQn*logn+nQ?OQn*lognQ,I间复杂度都为OQ?Q?/li>
  • 所以,要想辑ֈ旉OQNQ,I间OQ?Q的目标Q除非原数组是有序的Q指针扫描法Q,不然Q当数组无序的话Q就只能先排序,后指针扫描法或二分(旉n*lognQ空间OQ?Q)Q或映射或hashQ时间OQnQ,I间OQnQ)。时间或I间Q必ȝ牲一个,自个权衡吧?/li>
  • lgQ若是数l?strong>有序的情况下Q优先考虑两个指针两端扫描法,以达到最佳的ӞOQNQ)Q空QOQ?Q)效应。否则,如果要排序的话,旉复杂度最快当然是只能辑ֈN*logNQ空间OQ?Q则是不在话下?/li>

代码Q?/strong>

okQ在q入W二节之前,׃先来实现思\5Q这里假定数l已l是有序的)Q代码可以如下编写(两段代码实现Q:

  1. //代码一  
  2. //OQNQ?/span>  
  3. Pair findSum(int *s,int n,int x)     
  4. {     
  5.     //sort(s,s+n);   如果数组非有序的Q那׃先排好序OQN*logNQ?nbsp;    
  6.       
  7.     int *begin=s;     
  8.     int *end=s+n-1;     
  9.       
  10.     while(begin<end)    //俩头多w|或称两个指针两端扫描法,很经典的ҎQOQNQ?nbsp;   
  11.     {     
  12.         if(*begin+*end>x)     
  13.         {     
  14.             --end;     
  15.         }     
  16.         else if(*begin+*end<x)     
  17.         {     
  18.             ++begin;     
  19.         }     
  20.         else    
  21.         {     
  22.             return Pair(*begin,*end);     
  23.         }     
  24.     }     
  25.       
  26.     return Pair(-1,-1);     
  27. }     
  28.   
  29. //或者如下编写,  
  30. //代码?/span>  
  31. //copyright@ zhedahht && yansha  
  32. //July、updatedQ?011.05.14?/span>  
  33. bool find_num(int data[], unsigned int length, int sum, int& first_num, int& second_num)  
  34. {     
  35.     if(length < 1)  
  36.         return true;  
  37.       
  38.     int begin = 0;  
  39.     int end = length - 1;  
  40.       
  41.     while(end > begin)  
  42.     {  
  43.         long current_sum = data[begin] + data[end];  
  44.           
  45.         if(current_sum == sum)  
  46.         {  
  47.             first_num = data[begin];  
  48.             second_num = data[end];  
  49.             return true;  
  50.         }  
  51.         else if(current_sum > sum)  
  52.             end--;  
  53.         else  
  54.             begin++;  
  55.     }  
  56.     return false;  
  57. }  

 

 

扩展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;
}


W二节、寻扑֒为定值的多个?/span>
W?1题(数组Q?br />2010q中兴面试题
~程求解Q?br />输入两个整数 n ?mQ从数列1Q?Q?.......n ?随意取几个数,
使其和等?m ,要求其中所有的可能l合列出来?/p>

解法一
我想Q稍后给出的E序已经_清楚了,是要注意到放nQ和不放n个区别,卛_Q代码如下:

  1. // 21题递归Ҏ  
  2. //copyright@ July && yansha  
  3. //July、yanshaQupdated?/span>  
  4. #include<list>  
  5. #include<iostream>  
  6. using namespace std;  
  7.   
  8. list<int>list1;  
  9. void find_factor(int sum, int n)   
  10. {  
  11.     // 递归出口  
  12.     if(n <= 0 || sum <= 0)  
  13.         return;  
  14.       
  15.     // 输出扑ֈ的结?/span>  
  16.     if(sum == n)  
  17.     {  
  18.         // 反{list  
  19.         list1.reverse();  
  20.         for(list<int>::iterator iter = list1.begin(); iter != list1.end(); iter++)  
  21.             cout << *iter << " + ";  
  22.         cout << n << endl;  
  23.         list1.reverse();      
  24.     }  
  25.       
  26.     list1.push_front(n);      //典型?1背包问题  
  27.     find_factor(sum-n, n-1);   //放nQn-1个数填满sum-n  
  28.     list1.pop_front();  
  29.     find_factor(sum, n-1);     //不放nQn-1个数填满sum   
  30. }  
  31.   
  32. int main()  
  33. {  
  34.     int sum, n;  
  35.     cout << "误入你要等于多的数值sum:" << endl;  
  36.     cin >> sum;  
  37.     cout << "误入你要从1.....n数列中取值的nQ? << endl;  
  38.     cin >> n;  
  39.     cout << "所有可能的序列Q如下:" << endl;  
  40.     find_factor(sum,n);  
  41.     return 0;  
  42. }  

 

解法?/strong>
@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>

代码~写如下Q?/p>

  1. //copyright@ 2011 zhouzhenren  
  2.   
  3. //输入两个整数 n ?nbsp;mQ从数列1Q?Q?.......n ?nbsp;随意取几个数,  
  4. //使其和等?nbsp;m ,要求其中所有的可能l合列出来?/span>  
  5.   
  6. #include <stdio.h>  
  7. #include <stdlib.h>  
  8. #include <memory.h>  
  9.   
  10. /**  
  11.  * 输入tQ?nbsp;rQ?nbsp;试Wk 
  12.  */  
  13. void sumofsub(int t, int k ,int r, int& M, bool& flag, bool* X)  
  14. {  
  15.     X[k] = true;   // 选第k个数  
  16.     if (t + k == M) // 若找C个和为MQ则讄解向量的标志位,输出?/span>  
  17.     {  
  18.         flag = true;  
  19.         for (int i = 1; i <= k; ++i)  
  20.         {  
  21.             if (X[i] == 1)  
  22.             {  
  23.                 printf("%d ", i);  
  24.             }  
  25.         }  
  26.         printf("/n");  
  27.     }  
  28.     else  
  29.     {   // 若第k+1个数满条gQ则递归左子?/span>  
  30.         if (t + k + (k+1) <= M)  
  31.         {  
  32.             sumofsub(t + k, k + 1, r - k, M, flag, X);  
  33.         }  
  34.         // 若不选第k个数Q选第k+1个数满条gQ则递归叛_?/span>  
  35.         if ((t + r - k >= M) && (t + (k+1) <= M))  
  36.         {  
  37.             X[k] = false;  
  38.             sumofsub(t, k + 1, r - k, M, flag, X);  
  39.         }  
  40.     }  
  41. }  
  42.   
  43. void search(int& N, int& M)  
  44. {  
  45.     // 初始化解I间  
  46.     bool* X = (bool*)malloc(sizeof(bool) * (N+1));  
  47.     memset(X, falsesizeof(bool) * (N+1));  
  48.     int sum = (N + 1) * N * 0.5f;  
  49.     if (1 > M || sum < M) // 预先排除无解情况  
  50.     {  
  51.         printf("not found/n");  
  52.         return;  
  53.     }  
  54.     bool f = false;  
  55.     sumofsub(0, 1, sum, M, f, X);  
  56.     if (!f)  
  57.     {  
  58.         printf("not found/n");  
  59.     }  
  60.     free(X);  
  61. }  
  62.   
  63. int main()  
  64. {  
  65.     int N, M;  
  66.     printf("误入整数N和M/n");  
  67.     scanf("%d%d", &N, &M);  
  68.     search(N, M);  
  69.     return 0;  
  70. }  

 

扩展Q?/strong>

1、从一列数中筛除尽可能的C得从左往右看Q这些数是从到大再从大到小的(|易Q?/p>

2、有两个序列a,bQ大都为n,序列元素的gQ意整敎ͼ无序Q?br />要求Q通过交换a,b中的元素Q[序列a元素的和]与[序列b元素的和]之间的差最?br />例如:  
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>

    @wellQ[fairywell]:
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 />源代码如下:

  1. /** 
  2. * The problem: 
  3. * 从一列数中筛除尽可能的C得从左往右看Q这些数是从到大再从大到小的(|易Q?/span> 
  4. * use binary search, perhaps you should compile it with -std=c99 
  5. * fairywell 2011 
  6. */  
  7. #include <stdio.h>  
  8.   
  9. #define MAX_NUM    (1U<<31)  
  10.   
  11. int  
  12. main()  
  13. {  
  14.     int i, n, low, high, mid, max;  
  15.       
  16.     printf("Input how many numbers there are: ");  
  17.     scanf("%d/n", &n);  
  18.     /* a[] holds the numbers, b[i] holds the number of increasing numbers 
  19.     * from a[0] to a[i], c[i] holds the number of increasing numbers 
  20.     * from a[n-1] to a[i] 
  21.     * inc[] holds the increasing numbers 
  22.     * VLA needs c99 features, compile with -stc=c99 
  23.     */  
  24.     double a[n], b[n], c[n], inc[n];  
  25.       
  26.     printf("Please input the numbers:/n");  
  27.     for (i = 0; i < n; ++i) scanf("%lf", &a[i]);  
  28.       
  29.     // update array b from left to right  
  30.     for (i = 0; i < n; ++i) inc[i] = (unsigned) MAX_NUM;  
  31.     //b[0] = 0;  
  32.     for (i = 0; i < n; ++i) {  
  33.         low = 0; high = i;  
  34.         while (low < high) {  
  35.             mid = low + (high-low)*0.5;  
  36.             if (inc[mid] < a[i]) low = mid + 1;  
  37.             else high = mid;  
  38.         }  
  39.         b[i] = low + 1;  
  40.         inc[low] = a[i];  
  41.     }  
  42.       
  43.     // update array c from right to left  
  44.     for (i = 0; i < n; ++i) inc[i] = (unsigned) MAX_NUM;  
  45.     //c[0] = 0;  
  46.     for (i = n-1; i >= 0; --i) {  
  47.         low = 0; high = i;  
  48.         while (low < high) {  
  49.             mid = low + (high-low)*0.5;  
  50.             if (inc[mid] < a[i]) low = mid + 1;  
  51.             else high = mid;  
  52.         }  
  53.         c[i] = low + 1;  
  54.         inc[low] = a[i];  
  55.     }  
  56.       
  57.     max = 0;  
  58.     for (i = 0; i < n; ++i )  
  59.         if (b[i]+c[i] > max) max = b[i] + c[i];  
  60.         printf("%d number(s) should be erased at least./n", n+1-max);  
  61.         return 0;  
  62. }  

 

@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])
           = sum(a) - sum(b) - 2 (a[i] - b[j])
           = A - 2 (a[i] - b[j])

设x = a[i] - b[j]Q得
    |A| - |A'| = |A| - |A-2x|

    假设A > 0,

    当x ?(0,A)之间Ӟ做这L交换才能使得交换后的a和b的和之差变小Qx接qA/2效果好,
    如果找不到在(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;

接上Q@yuanQ?br />a[i]-b[j]要接qA/2Q则可以q样惻I
我们可以对于a数组的Q意一个a[k],在数lb中找Za[k]-C最接近的数QC是常数Q也是0.5*AQ?br />q个数要么就是a[k]-CQ要么就是比他稍大,要么比他E小Q所以可以要二分查找?/span>

查找最后一个小于等于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期;
•如果你能对狂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> 

版权所有,本hҎblog内所有Q何内容n有版权及著作权。实要{载,请以链接形式注明出处?/strong>



鑫龙 2012-11-30 21:09 发表评论
]]>E序员编E艺?----W四?----现场~写cMstrstr/strcpy/strpbrk的函?/title><link>http://m.shnenglu.com/mysileng/archive/2012/11/21/195477.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Wed, 21 Nov 2012 11:04:00 GMT</pubDate><guid>http://m.shnenglu.com/mysileng/archive/2012/11/21/195477.html</guid><wfw:comment>http://m.shnenglu.com/mysileng/comments/195477.html</wfw:comment><comments>http://m.shnenglu.com/mysileng/archive/2012/11/21/195477.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/mysileng/comments/commentRss/195477.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/mysileng/services/trackbacks/195477.html</trackback:ping><description><![CDATA[     摘要:                W四章、现场编写类似strstr/strcpy/strpbrk的函?nbsp;   作者:July?nbsp;   说明Q?nbsp;如果在博客中代码使用了\nQcsdn blogpȝ会自动回给我变?n。据后箋验证Q可能是原来旧bl...  <a href='http://m.shnenglu.com/mysileng/archive/2012/11/21/195477.html'>阅读全文</a><img src ="http://m.shnenglu.com/mysileng/aggbug/195477.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/mysileng/" target="_blank">鑫龙</a> 2012-11-21 19:04 <a href="http://m.shnenglu.com/mysileng/archive/2012/11/21/195477.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>E序员编E艺?----W三章箋-----Top K法问题的实?/title><link>http://m.shnenglu.com/mysileng/archive/2012/11/21/195473.html</link><dc:creator>鑫龙</dc:creator><author>鑫龙</author><pubDate>Wed, 21 Nov 2012 09:21:00 GMT</pubDate><guid>http://m.shnenglu.com/mysileng/archive/2012/11/21/195473.html</guid><wfw:comment>http://m.shnenglu.com/mysileng/comments/195473.html</wfw:comment><comments>http://m.shnenglu.com/mysileng/archive/2012/11/21/195473.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/mysileng/comments/commentRss/195473.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/mysileng/services/trackbacks/195473.html</trackback:ping><description><![CDATA[     摘要:                      E序员编E艺术:W三章箋、Top K法问题的实?nbsp;   作者:JulyQzhouzhenrenQyansha?nbsp;   致谢Q微?00题实现组Q狂x创作l?nbsp;&nb...  <a href='http://m.shnenglu.com/mysileng/archive/2012/11/21/195473.html'>阅读全文</a><img src ="http://m.shnenglu.com/mysileng/aggbug/195473.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/mysileng/" target="_blank">鑫龙</a> 2012-11-21 17:21 <a href="http://m.shnenglu.com/mysileng/archive/2012/11/21/195473.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>E序员编E艺?----W三?----L最的k个数http://m.shnenglu.com/mysileng/archive/2012/11/20/195436.html鑫龙鑫龙Tue, 20 Nov 2012 13:56:00 GMThttp://m.shnenglu.com/mysileng/archive/2012/11/20/195436.htmlhttp://m.shnenglu.com/mysileng/comments/195436.htmlhttp://m.shnenglu.com/mysileng/archive/2012/11/20/195436.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/195436.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/195436.html阅读全文

鑫龙 2012-11-20 21:56 发表评论
]]>
E序员编E艺?----W二?----字符串是否包含及匚w/查找/转换/拯问题http://m.shnenglu.com/mysileng/archive/2012/11/19/195347.html鑫龙鑫龙Mon, 19 Nov 2012 03:43:00 GMThttp://m.shnenglu.com/mysileng/archive/2012/11/19/195347.htmlhttp://m.shnenglu.com/mysileng/comments/195347.htmlhttp://m.shnenglu.com/mysileng/archive/2012/11/19/195347.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/195347.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/195347.html阅读全文

鑫龙 2012-11-19 11:43 发表评论
]]>
E序员编E艺?----W一?----左旋转字W串http://m.shnenglu.com/mysileng/archive/2012/11/18/195323.html鑫龙鑫龙Sun, 18 Nov 2012 08:39:00 GMThttp://m.shnenglu.com/mysileng/archive/2012/11/18/195323.htmlhttp://m.shnenglu.com/mysileng/comments/195323.htmlhttp://m.shnenglu.com/mysileng/archive/2012/11/18/195323.html#Feedback0http://m.shnenglu.com/mysileng/comments/commentRss/195323.htmlhttp://m.shnenglu.com/mysileng/services/trackbacks/195323.html阅读全文

鑫龙 2012-11-18 16:39 发表评论
]]>
þþƷ| һaƬþëƬ| Ʒþþþ| þ99ֻƵƷ8| 7777þþùƷ| ˾þ| þwww˳ɾƷ㽶| þۺϾþۺϾþۺ| 91ƷۺϾþþþþ| þѵľƷV| þӰԺۺϾƷ| þԭavapp| þþþþþĻ| һɫþۺϺݺ| ݺɫþþۺƵպ | þҹӰ| þùƷ| ˾þóۺӰԺ | vaþþþͬ| ˼˼þþƷ| Ʒþþþ| һһþaaۺϾƷ| þ66͵Ʒ9| þۺϾɫۺϾ99| 99þþƷֻоƷ| ˾þóۺӰԺ | ˾þô߽| ޾Ʒþþþþ| Ʒһþ| þseƷһƷ| þþþþþۺۺϺݺ| Ļ˾þ| ٸƷþþһ| ˸ŮѲžþþ| þþƷ99þ㽶| AVþþƷ | ݺɫþۺ| Ʒþþþþ| þþþþùaѹۿɫƬ | þþƷ}Ů| þԾƷ|