程序员编程艺术.docx
《程序员编程艺术.docx》由会员分享,可在线阅读,更多相关《程序员编程艺术.docx(38页珍藏版)》请在冰豆网上搜索。
程序员编程艺术
程序员编程艺术
程序员编程艺术第一二十七章集锦与总结
教你如何编程
作者July编程艺术室
时间comcom
出处httpcomv_JULY_v
声明版权所有侵权定究
本文档制作者有鱼comCEO吴超花明月暗
前言
围绕面试算法编程三个主题的程序员编程艺术系列简称TAOPP系列从
今年4月写第一篇起至今快有一年近1年的创作中写了二十七章共计22篇文章
这是本人的第4大原创作品不过与之前微软面试100题系列红黑树系列及十三个经
典算法研究系列相比编程艺术系列的某些篇文章的作者除了我本人自己或多或少还得到
了不少朋友的支持我把这些朋友组织起来成立了一个工作室它的名字叫做编程艺术室
编程艺术系列最初名为程序员面试题狂想曲即为面试服务后来随着加入与我一起创
作的人越来越多我们逐渐意识到为面试服务不应该成为我们最终或最主要的目的而应
该注重提高广大初学者的编程能力以及如何运用编程技巧和高效的算法解决实际应用问
题这才是计算机科学与编程的本质于是我们便把程序员面试题狂想曲系列更名为程序
员编程艺术系列然后把狂想曲创作组确定为编程艺术室并提出了我们的宗旨即如下
编程艺术室致力于以下三点工作
1针对一个问题不断寻找更高效的算法并予以编程实现
2解决实际中会碰到的应用问题
3经典算法的研究与实现
总体突出一点编程如何高效的编程解决实际问题
刚开始的时候我们是不敢给自己戴艺术这个高帽子的因为艺术的提炼是一个非常非
常艰难的过程且我们全部都是一群庸人但是我们很想也非常乐意接受这个挑战所以
一边带着万分的惶恐一边认真细心的创作每一章等到发布后再对每一章任何一个细节
1
仔细推敲与琢磨反复思考反复修正反复完善绝不轻易放过任何一个问题漏洞和
bug但即便如此仍然冒出了很多的问题幸运的是有广大的读者朋友们对编程艺术系
列和我们给予热心的指导与优化建议更重要的是他们还耐心细致的对编程艺术系列提出了
非常多的且异常宝贵的批评指正与修订完善的意见
没有编程艺术室全部人员的加入创作编程艺术系列将比现在所呈现在大家面前的还要
糟糕至少我个人现在是这么认为的而如果没有众多网友朋友们的修正与完善编程
艺术系列将更显不足从而失去它本身该有的持久动力与明天所以非常感谢所有热心的
朋友给予编程艺术系列所有的指导和意见你们的反馈给了我们的创作很大很大的帮助同
时也感谢本社区编辑的推荐非常感谢最后恳请广大读者对编程艺术系列继续监督
并随时予以批评指正我们不能残留任何一个bug因为编程艺术系列最后可能要写到第
六十章谢谢
ok以下是已经写了的编程艺术系列的前二十七章共21篇文章希望你能从中感受到
编程的技巧与乐趣点击链接即可跳转到相应页面
无私分享造福天下
第一章左旋转字符串4
第二章字符串是否包含及相关问题扩展34
第三章寻找最小的k个数63
第三章续TopK算法问题的实现107
第三章再续快速选择SELECT算法的深入分析与实现148
第三章三续求数组中给定下标区间内的第K小大元素167
第四章现场编写类似strstrstrcpystrpbrk的函数178
第五章寻找满足条件的两个或多个数195
第六章亲和数问题--求解500万以内的亲和数208
第七章求连续子数组的最大和215
第八章从头至尾漫谈虚函数222
第九章闲话链表追赶问题237
第十章如何给107个数据量的磁盘文件排序246
第十一章最长公共子序列LCS问题280
第十二十五章中签概率IP访问次数回文等问题初稿291
第十六第二十章全排列跳台阶奇偶排序第一个只出现一次等问题305
2
第二十一二十二章出现次数超过一半的数字最短摘要的生成322
第二十三四章杨氏矩阵查找倒排索引关键词Hash不重复编码实践340
第二十五章二分查找实现JonBentley90程序员无法正确实现363
第二十六章基于给定的文档生成倒排索引的编码与实践366
第二十七章不改变正负数之间相对顺序重新排列数组时间ON空间O1393
编程艺术室
编程艺术系列已经发布的上二十七章仍有很多很多的问题与不足但永久勘误永久
优化如果读者朋友对编程艺术系列任何一章有任何问题和建议或者发现了以上任何一
章的问题错误漏洞和bug欢迎及时反馈给我们我们将感激不尽当然如果有兴
趣我们也欢迎您加入我们--编程艺术室
1编程能力较强
2有一定的业余时间
3工作经验越长越好能力出众的在读研究生或ACM人员也可以考虑
4愿意分享平时工作中的项目经验或性能优化建议
5热爱算法者优先
符合以上条件的朋友欢迎加入编程艺术室有意者可给我zhoulei0907yahoocn投一
篇稿子稿子主题内容形式不限只要你觉得自己能写出像已有的27章那样的文章便
可投稿审阅之后邀请加入以为大家创造更多的价值更好的服务谢谢
版权所有XX严禁用于任何商业用途违者定究法律责任
特别提醒
本编程艺术系列文档中有任何一处错误bug或漏洞读者朋友一经发现欢迎随时
来信指导或blog内留言评论我将感激不尽
我的联系方式如下
邮箱zhoulei0907yahoocn
微博httpweibocomjulyweibo
Bloghttpcomv_JULY_v
本结构之法算法之道blog若无意外永久更新永久勘误
感谢关注本编程艺术系列的读者朋友们感谢所有编程艺术室内的朋友咱们blog上
第二十八章再见
3
第一章左旋转字符串
作者Julyyansha
时间二零一一年四月十四日
说明狂想曲有三层意思1思绪纷飞行文杂乱无章想到什么记下什么2简单问题深入
化复杂问题精细化不惧汪洋不惧艰深洋洋洒洒纵横千里3依托一道面试题展开来思维放任
不羁逐步深入细致入微反复修正绝不含糊以期给读者一个彻彻底底明明白白的交待原为狂想
曲现在已改为编程艺术系列
微博httpweibocomjulyweibo
出处httpcomv_JULY_v
序
前言
第一节左旋转字符串
第二节两个指针逐步翻转
第三节通过递归转换缩小问题之规模
第四节stlrotate算法的步步深入
第五节总结
序
一个懒散的午夜程序员躺在椅子上静静点上一支烟瞅着屏幕上那一行一行如行云
流水般的代码赏心悦目渐觉困意便慢慢闭上了俩眼养神
而后冥冥中房间里似缓缓响起一首钢琴曲叫不出名字却铿锵有力且清脆无比忽
而激荡忽而平静激荡处如波涛翻滚怒洋咆哮平静处如潺潺流水鸟语花香
半响程序员突然睁开双眼关掉编译器打开记事本信笔由缰急速记录下他那杂
乱无章和奇特跳跃的思绪他怕此刻不赶紧记下来以后风吹云散于是世间就有
了程序员面试题狂想曲一乐章的诞生
此曲终日弹奏绵绵不绝终至广为流传飘进了千万人的耳中余音不去
前言
4
本人整理微软等公司面试100题系列包括原题整理资源上传帖子维护答案整理
勘误修正与优化工作包括后续全新整理的80道总计180道面试题已有半年的时间
了
关于这180道面试题的一切详情请参见横空出世席卷Csdn[评微软等数据结构
算法面试180题]
一直觉得这180道题中的任何一题都值得自己反复思考反复研究不断修正不断
优化之前的答案整理由于时间仓促加之受最开始的认识局限更兼水平有限所以这
180道面试题的答案有很多问题都值得进一步商榷与完善
特此想针对这180道面试题再写一个系列叫做程序员编程艺术系列如你所见
我一般确定要写成一个系列的东西一般都会永久写下去的
他似风儿一般奔跑很多人渐渐的停下来了而只有他一直在飞一直在飞
ok本次程序员编程艺术系列以之前本人最初整理的微软面试100题中的第26题左
旋转字符串为开篇希望就此问题进行彻底而深入的阐述然以下所有任何代码仅仅只是
全部测试正确了而已还有很多的优化工作要做欢迎任何人不吝赐教谢谢
第一节左旋转字符串
题目描述
定义字符串的左旋转操作把字符串前面的若干个字符移动到字符串的尾部
如把字符串abcdef左旋转2位得到字符串cdefab
请实现字符串左旋转的函数要求对长度为n的字符串操作的时间复杂度为
On空间复杂度为O1
编程之美上有这样一个类似的问题咱们先来看一下
设计一个算法把一个含有N个元素的数组循环右移K位要求时间复杂度为ON
且只允许使用两个附加变量
分析
5
我们先试验简单的办法可以每次将数组中的元素右移一位循环K次
abcd1234→4abcd123→34abcd12→234abcd1→1234abcd
RightShiftintarrintNintK
whileK--
inttarr[N-1]
forintiN-1i0i--
arr[i]arr[i-1]
arr[0]t
虽然这个算法可以实现数组的循环右移但是算法复杂度为OKN不符合题目的要
求要继续探索
假如数组为abcd1234循环右移4位的话我们希望到达的状态是1234abcd
不妨设K是一个非负的整数当K为负整数的时候右移K位相当于左移-K位
左移和右移在本质上是一样的
解法一
大家开始可能会有这样的潜在假设KN事实上很多时候也的确是这样的但严格来说
我们不能用这样的惯性思维来思考问题
尤其在编程的时候全面地考虑问题是很重要的K可能是一个远大于N的整数在这个
时候上面的解法是需要改进的
仔细观察循环右移的特点不难发现每个元素右移N位后都会回到自己的位置上因此
如果KN右移K-N之后的数组序列跟右移K位的结果是一样的
进而可得出一条通用的规律
右移K位之后的情形跟右移KKN位之后的情形一样如代码清单2-34所示
代码清单2-34
RightShiftintarrintNintK
KN
whileK--
6
inttarr[N-1]
forintiN-1i0i--
arr[i]arr[i-1]
arr[0]t
可见增加考虑循环右移的特点之后算法复杂度降为ON2这跟K无关与题目
的要求又接近了一步但时间复杂度还不够低接下来让我们继续挖掘循环右移前后数组
之间的关联
解法二
假设原数组序列为abcd1234要求变换成的数组序列为1234abcd即循环右移了4位
比较之后不难看出其中有两段的顺序是不变的1234和abcd可把这两段看成两个整
体右移K位的过程就是把数组的两部分交换一下
变换的过程通过以下步骤完成
逆序排列abcdabcd1234→dcba1234
逆序排列1234dcba1234→dcba4321
全部逆序dcba4321→1234abcd
伪代码可以参考清单2-35
代码清单2-35
Reverseintarrintbinte
forbebe--
inttemparr[e]
arr[e]arr[b]
arr[b]temp
RightShiftintarrintNintk
KN
Reversearr0N–K-1
ReversearrN-KN-1
7
Reversearr0N-1
这样我们就可以在线性时间内实现右移操作了
稍微总结下
编程之美上
限制书中思路的根本原因是题目要求且只允许使用两个附加变量去掉这个限制
思路便可如泉喷涌
1第一个想法是一个字符一个字符的右移所以复杂度为ONK
2后来它改进了通过这条规律右移K位之后的情形跟右移KKN位之后的情
形一样
复杂度为ON2
3直到最后它才提出三次翻转的算法得到线性复杂度
下面你将看到本章里我们的做法是
1三次翻转直接线性
2两个指针逐步翻转线性
3stl的rotate算法线性
好的现在回到咱们的左旋转字符串的问题中来对于这个左旋转字符串的问题咱们可
以如下这样考虑
11思路一
对于这个问题咱们换一个角度可以这么做
将一个字符串分成两部分X和Y两个部分在字符串上定义反转的操作XT即把X的
所有字符反转如X"abc"那么XT"cba"那么我们可以得到下面的结论
XTYTTYX显然我们这就可以转化为字符串的反转的问题了
不是么ok就拿abcdef这个例子来说非常简短的三句请细看一看就懂
1首先分为俩部分XabcYdef
2X-XTabc-cbaY-YTdef-fed
3XTYTTYXcbafed-defabc即整个翻转
我想这下你应该了然了
然后代码可以这么写已测试正确
1Copyright小桥流水July
8
2c代码实现已测试正确
3httpsmallbrcom20110313100E9A298
4_21-E5B7A6E6978BE8BDACE5AD97E7ACA6E4B8B26>html
5Julyupdatedcom
6include
7include
8
9charinvertcharstartcharend
10
11chartmpptmpstart
12whilestartNULLendNULLstartend
13
14tmpstart
15startend
16endtmp
17start
18end--
19
20returnptmp
21
22
23charleftcharsintpospos为要旋转的字符个数或长度下面主函数测试中
pos3
24
25intlenstrlens
26invertsspos-1如上X-XT即abc-cba
27invertsposslen-1如上Y-YT即def-fed
28invertsslen-1如上整个翻转XTYTTYX
即cbafed-defabc
29returns
30
31
32intmain
33
34chars[]"abcdefghij"
35putslefts3
36return0
37
12答案V03版中第26题勘误
之前的答案V03版[第21-40题答案]中第26题贴的答案有误那段代码的问题最
早是被网友Sorehead给指出来的
9
第二十六题
楼主的思路确实很巧妙我真没想到还有这种方法学习了
不过楼主代码中存在问题主要是条件判断部分
函数LeftRotateString中ifnLength0n0nnLength
函数ReverseString中ifpStartNULLpEndNULL
当时以答案整理因时间仓促及最开始考虑问题不够周全为由没有深入细看下去
后来朋友达摩流浪者再次指出了上述代码的问题
26题这句ifnLength0n0nnLength有问题吧
还有一句应该是ifpStartNULLpEndNULL吧
而后修改如下已测试正确
1zhedahht
2Julykupdated
3copyrightcombyJuly
4引用请注明原作者出处
5include
6include
7usingnamespacestd
8
9voidSwapcharacharb特此把交换函数独立抽取出来当然不排除会
有人认为此为多此一举
10
11chartempa
12ab
13btemp
14
15
16ReversethestringbetweenpStartandpEnd
17voidReverseStringcharpStartcharpEnd
18
19ifpStart0pEnd0
20这句也可以是ifpStartNULLpEndNULL
21
22whilepStartpEnd
23
24SwappStartpEnd交换
25
26pStart
27pEnd--
10
28
29
30
31
32Movethefirstncharsinastringtoitsend
33charLeftRotateStringcharpStrunsignedintn
34
35ifpStrNULL
36
37intnLengthstatic_caststrlenpStr
38ifnLength0n0nnLengthn可以0也可以说不该
0
39nLength是整个字符串的长度n是左边的一部分所以应该是
nnLength
40之前上传的答案代码就错在这里最初的为nnLength当然
就是错的了Julykupdated
41
42charpFirstStartpStr
43charpFirstEndpStrn-1
44charpSecondStartpStrn
45charpSecondEndpStrnLength-1
46
47reversethefirstpartofthestring
48ReverseStringpFirstStartpFirstEnd
49reversethesecondpartofthestrint
50ReverseStringpSecondStartpSecondEnd
51reversethewholestring
52ReverseStringpFirstStartpSecondEnd
53
54
55returnpStr
56
57
58intmain
59
60chara[11]"helloJuly"2修正以一个数组实现存储整个字符串
61charpsa
62LeftRotateStringps6
63forps0ps
64coutps
65coutendl
66psNULL代码规范
67return0
68
11
上述修正的俩处错误如下所示
1如上注释中所述
ifnLength0nnLength
nLength是整个字符串的长度吧n是左边的一部分所以应该是nnLength
2至于之前的主函数为什么编写错误请看下面的注释
intmain
charps"helloJuly"本身没错但你不能对ps所指的字符串做任何修改
这句其实等价于constcharps"helloJuly"
LeftShiftStringps4而在这里试图修改ps所指的字符串常量所以将出现错误
putsps
return0
当然上面的解释也不是完全正确的正如ivan所说从编程实践来说不完全对
如果在一个大的工程里面你怎么知道ps指向的是""字符串还是malloc出来的东西
那么如何决定要不要对ps进行delete
不过至少第26题的思路一的代码最终完整修正完全了
13updated
可能你还是感觉上述代码有点不好理解ok下面再给出一段c实现的代码
然后我们可以看到c的高效与简洁
1copyrightyiyibuptJuly
2已测试正确Julyupdatedcom
3不要小看每一段程序July
4include
5include
6
7voidrotatecharstartcharend
8
9whilestartNULLendNULLstartend
10
11chartempstart
12startend
13endtemp
14start
15end--
12
16
17
18
19
20voidleftrotatecharpintm
21
22ifpNULL
23return
24intlenstrlenp
25ifm0mlen
26
27charxfirstxend
28charyfirstyend
29xfirstp
30xendpm-1
31yfirstpm
32yendplen-1
33rotatexfirstxend
34rotateyfirstyend
35rotatepplen-1
36
37
38
39intmainvoid
40
41charstr[]"abcdefghij"
42leftrotatestr3
43printf"sn"str
44return0
45
第二节两指针逐步翻转
先看下网友litaoye的回复26左旋转字符串跟panda所想是一样的即
以abcdef为例
1ab-ba
2cdef-fedc
原字符串变为bafedc
3整个翻转cdefab
只要俩次翻转且时间复杂度也为On
13
21在此本人再奉献另外一种思路即为本思路二
abcdefghi要abc移动至最后
abcdefghi-defabcghi-defghiabc
定义俩指针p1指向ch[0]p2指向ch[m]
一下过程循环m次交换p1和p2所指元素然后p1p2
第一步交换abc和def
abcdefghi-defabcghi
第二步交换abc和ghi