Coco是个聪明的孩子,而且又是学的计算机软件专业,所以她说要让我教她写程序时,我很高兴的答应了。教了一段后,感觉真的是赚到了一个聪明学生。
海顿曾说,“我以为会因为教过这个年青人而感到骄傲的。”后来,这个叫贝多芬的年青人真的做出了大事业。
不过,到底Coco在这之前,几乎从没有摸过真正的计算机,加上我只能通过网络教她,所以还是闹了一些笑话。当然,比起我当年,她学得快多啦。
我给Coco开了一个很另类的学习规划:C++&Python。初期以Python为主,往后慢慢加C++,然后看是不是再加些Java(我总觉得要是前面两样学好了,Java,C#什么的自学也能很快上手)。当然,我自己最熟悉的是数据库系统,所以以后肯定会教她学数据库方面的知识。暂时嘛,先学好眼前这些再说。主要是培养程序设计与编码的风格,打打基础。我毕竟不是她的任课教师,很多课本上的知识在课堂上学效果会更好些。
Coco的英语不错,所以,装上Python内核以及Pythonwin(我最习惯的Python IDE)后,我给了她一个星期去阅读Python的文档,自学Python的基本知识。作为一个只在课本上学过C语言的在校学生,她第一个星期的进展还是很让我满意的,除了个别使用问题,需要我的帮助,其他我没怎么管。现在最基本的语法已经完全掌握了,也懂得使用Pythonwin的一些简单功能。老鸟们也不要笑,对于一个刚刚拥有自己的计算机一个月的学生,这个进度,不算慢。现在,我准备让她写一些小程序来练习。就从她的必修课--《算法与数据结构》开始吧。反正Python有现成的容器,链表字典一应俱全,直接从基础的算法入手更简单。so,今天的QQ上,我们从排序开始:
我:Coco,你们现在正在学算法吧。
Coco:是啊,不过课本是C++的,老师讲的全是C。
我:对于这种课程,用什么语言实现不是最重要的--其实也挺重要,我个人还是感觉C++实现最好:)--最要是实现了什么。从你上次发给我的C程序来看……我认为你还是从Python开始学吧。
Coco:为什么?那个程序最后不是可以运行吗?
我:是啊,但是连缩进全没有,这种代码,拿给别人根本没法看,更不要说调试和改进。用Python练一段,强迫你学会良好的书写风格再说吧。再说什么指针引用的,要是和算法搅在一起,更学不清了。Python的语法简单,不用费心。
Coco:这么说我那个C程序一无是处喽……
我:倒也不是,下面这几行还可以保留:
#include <stdlio.c>
int main()
{
return 0;
}
Coco:555……
(Coco暴怒中,中断课程五分钟,^_^)
我:好,现在我们开始上课,首先,你要知道Python的线性容器--链表。
Coco:嗯,我知道,最简单的方法,Array = [] 就可以声明一个链表。也可以在这里给它赋初值。
我:OK,那么有了这个容器,我们就可以开始学习最基本的排序算法了。如果给你一些扑克,你会怎么样把它排序?
Coco:当然最简单的办法就是,先拿出一张放在桌面上,然后拿出第二张,与第一张比较,把它放在合适的位置。然后是第三张,第四张……视情况将其插入在队列的某一端或队列中的合适位置。
我:完全正确,我们对数据的排序,也有类似的方法,我们称之为直接插入排序。
Coco:直接插入排序似乎是这样的过程,先把这些数据全摆在这里,然后一个一个找出顺序不对的数据,把它们插入到合适的位置。
我:是的,我们在程序中,就用采用这种方法,现在给你这个链表 Array=[6,16,10,9,15,5,11,1,19,4,14,18,0,13,3,17,12,2,8,7] ,我们写出对它排序的函数代码吧。我们先约定好这个函数的声明,就叫它DrtInsSort(theArray)吧,给出测试代码:
Array=[6,16,10,9,15,5,11,1,19,4,14,18,0,13,3,17,12,2,8,7]
print Array
DrtInsSort(Array)
print Array
第一步,我们先找出这里面哪些位置出现了逆序,这个程序你能写出来吗?
Coco:我试试……
def DrtInsSort(theArray):
for i in range(1, len(theArray)):
if theArray[i] < theArray[i-1]:
print “i :”, i, “theArray[i-1] :”, theArray[i-1], “theArray[i] :”, theArray[i]
return
我:很好,这个程序运行的不错,代码也很清楚,如果要把它变成真正能对数组进行排序的代码,你知道怎么改进吗?
Coco:应该把“print”行改成移动元素的代码即可,让我写写看,这个代码应该改成……
def DrtInsSort(theArray):
for i in range(1, len(theArray)):
if theArray[i] < theArray[i-1]:
tmp = theArray[i]
del theArray[i]
for j in range(i):
if theArray[j] > tmp:
theArray.insert(j, tmp)
break
return
我:很好,我注意到你的代码中,查找插入点的循环是 for j in range(i),可以说说为什么不是for j in range(len(theArray)) 吗?还有,下次记得不同功能块的代码,最好是写上注释或用空行分开,提高可读性:)。
Coco:这是因为,搜索逆序点的代码是从链表起点开始搜的,如果搜到i-1和i 存在逆序时,说明前面的元素都已处理完毕,已经是良序的。但是序号大于i的元素还没有进行比较,不能保证。所以,我们应该只在已确定的良序区间寻找插入点。
我:不错,那么你说说这个算法还有没有改进的地方呢?
Coco:老师讲搜索算法时提过,这种顺序搜索的时间复杂度是线性的,那么随着良序区间的不断增长,搜索插入点所用的时间就会越来越长。如果改成更有效的搜索算法,比如折半搜索,就可以快一些。
我:想法不错,那么当你改进它的时候,有几个地方要注意:
1、你以前的代码全都放在一个函数体里,从来没有分过子函数,这不是好习惯。排序中的插入代码明显可以从整个排序中分离出来,而且可以有多种组合方案,应该把搜索插入的代码分离成一个子函数;
2、按照书本上的理论演示,折半插入是一直递归到底的。不过这样的话,就要在每一次递归时判断边界条件,比较麻烦,反而会降低效率,所以实用的作法是当搜索被缩减到一定程度,就直接使用顺序搜索。所以,你的顺序搜索代码也仍是有用的(不过这个你应该已经明白了吧);
3、为了调较性能方便,我希望改折半为顺序搜索的判断条件,即进行折半搜索的间区最小长度,应该可以很方便的修改。这样,你把它做为搜索函数的参数之一,这样我改起来也方便。
Coco:好的,我试试看(要求这么多@_@)……
(Coco几经波折,终于拿出了这样一个版本)
#Direct Insert
def drtIns(theArray, n, e):
for j in range(n):
if theArray[j] > e:
theArray.insert(j, e)
break
return
#Half Search.
def hlfIns(theArray, low, top, e, lmt):
if (top - low) < lmt:
drtIns(theArray, top, e)
return
#This variable is the marker insert Point.
insP = (low+top)/2
if theArray[insP-1] < e < theArray[insP]:
theArray.insert(insP, e)
return
elif e < theArray[insP-1]:
hlfInsRe(theArray, low, insP, e, lmt)
return
elif theArray[insP] < e:
hlfInsRe(theArray, insP, top, e, lmt)
return
else:
return
#Direct Sort method.
def DrtInsSort(theArray):
#The variable tell the half serach insert medoth
#use half serach in how long area.
lowlmt = 8
for i in range(1, len(theArray)):
if theArray[i] < theArray[i-1]:
tmp = theArray[i]
del theArray[i]
#You can choice a medoth you like in iterative or recursively, even choice the direct insert modth.
hlfIns(theArray, 0, i, tmp, lowlmt)
return
我:不错,值得鼓励!我想问一下,为什么你的顺序搜索插入函数drtIns要声明成def drtIns(theArray, n, e) ?这样子的话,每次搜索时,还是会从theArray[0]开始,我们的目的可基本上达不到啊。
Coco:我明白了,这时应该对drtIns做改进,让它的起点也可调……这就去改……
我:还有,你现在写的是一个递归算法,递归当然有它的好处,比如代码简洁。但是如果递归次数过多,会占用大量资源,还有可能造成堆栈溢出,现在除了递归算法,你还要再写一个迭代版本。
Coco:好(累啊)……
(下面是Coco的最终代码)
#Direct Inert to the list. If it is just directly used in the
#sort, you can use the sample version :
#def drtIns(theArray, top, e):
# for j in range(top):
# ...
def drtIns(theArray, low, top, e):
for j in range(low, top):
if theArray[j] > e:
theArray.insert(j, e)
break
return
#Half search insert method, it is the recursively version.
def hlfInsRe(theArray, low, top, e, lmt):
if (top - low) < lmt:
drtIns(theArray, low, top, e)
return
#This variable is the marker insert Point.
insP = (low+top)/2
if theArray[insP-1] < e < theArray[insP]:
theArray.insert(insP, e)
return
elif e < theArray[insP-1]:
hlfInsRe(theArray, low, insP, e, lmt)
return
elif theArray[insP] < e:
hlfInsRe(theArray, insP, top, e, lmt)
return
else:
return
#Half search insert method, it is the iterative version
def hlfInsIt(theArray, low, top, e, lmt):
if (top - low) < lmt:
drtIns(theArray, low, top, e)
return
ilow = low
itop = top
#This variable is the marker of insert Point.
insP = (ilow+itop)/2
while (e < theArray[insP-1]) or (theArray[insP] > e):
if e < theArray[insP-1]:
itop = insP
else:
ilow = insP
if itop - ilow < lmt:
drtIns(theArray, low, top, e)
return
insP = (ilow+itop)/2
else:
theArray.insert(insP, e)
return
#Direct Sort method.
def DrtInsSort(theArray):
#The variable tell the half search insert medoth
#use half search in how long area.
lowlmt = 8
for i in range(1, len(theArray)):
if theArray[i] < theArray[i-1]:
tmp = theArray[i]
del theArray[i]
#You can choice a medoth you like in iterative
#or recursively, even choice the direct insert modth.
hlfInsIt(theArray, 0, i, tmp, lowlmt)
return
#Follow is the demo of direct sort.
Array=[6,16,10,9,15,5,11,1,19,4,14,18,0,13,3,17,12,2,8,7]
print Array
DrtInsSort(Array)
print Array
我:好啊好啊,干得漂亮。
Coco:我有一个问题,我们这里做的各种操作,都是针对函数传进来的theArray参数,为什么修必的结果会直接反映在Array中呢?
我:所谓的“传值参数”和“引用参数”,你听说过吧。
Coco:知道。
我:在python中,简单类型的数据,比如整数,实数,在函数调用中,都是传值调用的,而比较复杂的类型,比如链表,就是引用调用,这是出于效率方面的考虑吧。
Coco:明白了……
我:你今天学得很快,还不错。这样吧,为了表示鼓励,你马上打车过来,我请你吃饭。
Coco:好几千里呢,算了吧~,我还是自己到食堂买点好吃的……
PS:写了几年的程序,总感觉学习新技术时,从入门到领悟,是一个很艰难的过程。python的入门,我很喜欢python自带的文档,还有恶魔吹着笛子来在《程序员》上发表的系列文章(我就是读了那些文章才决定学python的)。可是再进一步的指导,就比较不好找了。我试着以这种对话的题材,写下我个人的学习笔记,算是抛砖引玉吧,其实我对python所学还是太浅,还请各位高手拿出自己的心得,多多指教。
Coco是我虚拟的一个角色,整个故事纯属虚构,如有雷同……也不一定纯属巧合,这里面有一些小花絮,倒确是我身边发生过的,希望真实故事的主人公们,不要来打我就好,呵呵。有些时候感觉两个人对话像是自言自语,这很正常,因为本来就是我在自说自话……
如果文中因折行问题导致有代码出错,我在此表示歉意。这个HTML的排版实在是让我无可奈何……为了能排得更好看些,我不得不放弃了OpenOffice,开始用Word……
如果再出现排版混乱,以后的代码,我只好贴图啦……