考研_计算机_数据结构_高分笔记.pdf
《考研_计算机_数据结构_高分笔记.pdf》由会员分享,可在线阅读,更多相关《考研_计算机_数据结构_高分笔记.pdf(50页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、完整版:http:/ 针对考研数据结构的C&C+语言基础以及代码书写规范对于考研数据结构,需要C 与 C+语言作为基础,但是又不需要太多,因此此处讲解有针对性。现在我们面临的是研究生考试,要在答题纸上写代码,代码的评判者是阅卷老师,而不是TC,VC6.0等编译器。如果之前你只熟悉在这些编译器下写代码,那你要看看这一部分,这里教你怎么快速的写出能让阅卷老师满意的代码。算法的时间复杂度分析基础为什么要特别注重这一块的讲解?在09 年批阅数据结构算法那道题的时候,由于当时阅卷的标准答案是教育部给出的,并且明确说明以此为标准答案,但是教育部给出的算法时间复杂度太大,对于算法有研究的同学,可以很轻松的写
2、出一个算法,并且时间复杂度远远小于标准答案。教育部就是权威,没有办法,只能按照教育部的答案改,这样就导致了算法牛人写出更完美的算法,却得了最低的分。也许是为了避免这种不公平的再次出现,10年的考试要求终于改了,考生必须对自己写的算法给出时间复杂度和空间复杂度,并以此来作为评分的依据。所以这已经成为数据结构45分里面的必考内容,这一点的考察在图、排序、查找这三章内体现的尤为明显,因此我会在本章先总体讲一下算法时间复杂度分析的基本方法,并在以后章节中以题目的形式讲一些具体分析思路,这样考生逐渐的就会掌握考研要求的算法复杂度分析方法。数据结构和算法的基本概念这一部分介绍一些贯穿于整本书的基本概念。1
3、1 针对考研数据结构的代码书写规范以及C&C+语言基础111 考研综合应用题中算法设计部分的代码书写规范要在答题纸上快速的写出能让阅卷老师满意的代码,是有些技巧的,这与写出能在编译器上编译通过的代码有所不同。为了说明这一点我们首先看一个例题:设将 n(n1)个整数存放到一维数组 R 中。设计一个算法,将 R 中的序列循环左移 P(0Pn)个 位 置,即 将R中 的 数 据 由 X0,X1.Xn-1变 换 为Xp,Xp-1,.,Xn-1,X0,X1.Xp-1。要求:写出本题的算法描述。完整版:http:/ R 中序列循环左移 P 个位置,只需先将 R 中前 P 个元素逆置,再将剩下的元素逆置,最
4、后将R 中所有的元素再整体做一次逆置操作即可。本题算法描述如下:#include/1#defineN 50/2usingnamespacestd;/3voidReverse(intR,intl,intr)/4/5inti,j;/6inttemp;/7for(i=l,j=r;ij;i+,j-)/8/9temp=Ri;/10Ri=Rj;/11Rj=temp;/12/13/14voidRCR(intR,intn,intp)/15/16if(p=n)/17coutERRORL;/30cinn;/31for(i=0;iRi;/33RCR(R,n,L);/34for(i=0;i=n-1;i+)/35cou
5、tRi;/36coutendl;/37return0;/38/39以上程序段,是一段完整的可以在编译器下编译运行的程序,程序比较长,对于考试答卷,完全没有必要这么写。第 1 和 3 句,在我们大学里写的程序中,几乎都要用到,是耳熟能详的,研究生考试这种选拔考试不会用这种东西来区分学生的优劣,因此答题过程中没必要写,去掉。第 2 句,定义了一个常量,如果你的题目中要用一个常量,在你用到的地方加上一句注释,说明某某常量之前已经定义即可。而没必要再跑到前边去补上一句#defineXX XX,因为试卷的答题纸,不是编译器,插入语句不是那么方便,为了考试的时候节省时间且使得试卷整洁,这是最好的解决办法,
6、因此第2 句去掉。第 26 到第 39 是主函数部分,你之前声明的函数(第 4 到第 25 句)在这里调用。在答题中,我们只需要写出自己的函数说明(4 到 25句),写清楚函数的接口(何为接口,下边会细致讲解)即可,答卷老师就知道你已经做好了可以解决这个题目的工具(即函数)并完整版:http:/ 句:RCR(intR,intn,intp)就包含一个接口,它是原材料的一个入口。括号里所描述的就是原材料类型以及名称,是将来函数被调用的时候所要放进去的东西;是在告诉别人,我要三个原材料,第一个是个 int型的数组;第二个是一个 int型的变量;第三个也是一个 int型的变量。第 15 句,coutE
7、RRORdata;一般来说,用结构体变量直接取分量,其操作用”.”,用指向结构体变量的指针来取分量,其操作用”-”。这里再扩展一点,前边我们提到,如果p 是指针(假设已经指向x),*p就是取这个变量的值,a=*p;等价于 a=x;那么对于中的 BT 指针,怎么用”.”来取其 data值呢?类比p,*BT 就是 BT 指向的变量,因此可以写成(*BT).data;((*BT).data;与 BT-data是等价的)。注意*BT 外边要用括号括起来,不要写成*BT.data。在 C 或 C+语言中这是一种好的习惯,在你不知道系统默认的运算符优先级的情况下,你最好依照自己所期望的运算顺序加上括号。有
8、可能这个括号加上是多余的,但是为了减少错误,这种做法是必要的。对于与刚才那句,我所期望的运算顺序是先算*BT,即用”*”先将BT 变成它所指的变量,然后再用”.”取分量值。因此写成(*BT).data。比如这样一个式子 a*b/c,假设你不知道系统会默认先算乘再算除,而你所期望的运算优先顺序是先算乘再算除,为了减少错误,你最好是把它写成(a*b)/c,即便这里的括号是多余的。完整版:http:/ ElemtypeA;的变量定义语句,这里的 Elemtype是什么类型,新来的同学常常会一头雾水。要说明这个问题,我们先来说明一下typedef的用法。一句话,typedef就是用来给现有的数据类型起
9、一个新名字的,我们在结构类型定义时用到过,如 typedefstructTypeA;即为给“struct”起了一个名字 TypeA,就好比你制作了计算机中的整型,给他起了个名字为int。并且如果我想给int型起个新名字 A,就可以这样写 typedefintA;这样的话定义一个整形变量 x 的时候 A x;就等价于 intx;在考研中 typedef用的最多的地方就在结构型的定义过程中,其他的地方几乎不用。你可以这样理解typedef是用来起名字的,新定义的结构型没有名字,因此用 typedef给它起个名字是有必要的,但是对于已有的数据类型,如int,float等已经有了简洁的名字,还有必要给
10、它起个新名字吗?有必要,但不是在考研数据结构中。为什么有必要,有兴趣的同学可以去查下资料,查完你会发现,typedef对程序设计的贡献很大,但是对于考研答卷,用处不大,所以大家对其用法不必深究。说到这里大家就明白了,严奶奶的书上之所以有那么多大家不认识的数据类型,只不过是严奶奶悄悄的给我们认识的数据类型起了新名字而已。2)#define在严奶奶的书上除了我们没见过的数据类型以外,还有一些东西我们也没见过,比如在一个函数中她会写到 returnERROR;returnOK;之类的语句,对于经常在编译器上写代码的同学,乍一看到这种语句会十分的不爽,立马就会想,ERROR 和 OK 这样的东西能编译
11、通过?或者就是怀疑自己语言水平太差,严奶奶写的程序里边有太多自己看不懂的地方了,信心大减。其实不然,和 typedef一样,严奶奶悄悄的用#define语句处理过 ERROR或者 OK之类的词了。其实 ERROR和 OK 就是两个常量,作为函数的返回值,来提示用户函数操作结果的。严奶奶初衷是想把0,1这种常作为函数返回标记的数字定义成ERROR 和OK(一般出错返回0,成功返回1),这样比起数字来更人性化,更容易理解,但结果却适得其反,让新手们更困惑了。#define对于考研数据结构可以说没有什么贡献,我们只要认得它就行,写程序时一般用不到。比如#defineMAX 50 这句,即定义了常量
12、MAX(此时x=50;等价于x=MAX;)。在写程序大题的时候如果你要定义一个数组,如 intAMAX;加上一句注释:“/*MAX为已经定义的常量,其值为50*/”即可。没必要跑到你的程序最前边去加上#defineMAX 50这一句,原因前边已经讲过。严奶奶的书中有很多用自己加工过的代码书写的程序,和编译器上我们习惯的写法有很大出入,所以对于新手较难理解。本书的作用在很大程度上就是做了一个翻译的角色,不过是站在学生的角度把课本上用过于严谨以及专业化的词语描述的思想用通俗易懂的语言表达给你而已。2函数说明:只要是算法设计题,必用到函数,所以其中的一些注意事项这里有必要说一下。(1)用函数来缩短代
13、码如果有一段较长的操作需要在一个函数中反复多次使用,那么你最好把这个操作做成一个函数,在你要用的地方调用它,会节省很多答题空间。比如:voidf()/1/2完整版:http:/ 句组成了一个操作。这个操作在另一个函数(函数名为F)中要多次用到。此时我们就可以把这8 句做成一个函数,当用到的时候调用即可,比如:voidF()f();f();f();从上边可以看出,如果不用 f()函数,就得把 f()中的 8 行写 3 遍,使得 F()函数很长。(2)被传入函数的参数是否会改变inta;voidf(intx)x+;上边声明的函数,它需要一个整型变量作为参数,且在自己的函数体中将参数做自增1的运算。
14、执行完以下程序段之后a 的值是多少呢?a=0;/f(a);/有些同学可能以为a 等于 1。这个答案是错误的,可以这样理解,对于函数f(),在调用他的时候,括号里的变量a 和句中的变量a并不是同一个变量。在执行句的时候,变量a 只是把自己的值赋给了一个在f()的声明过程中已经定义好的整形变量,可以把这个变量想象为上述声明过程中的x,即句的执行过程拆开看来是这样两句:x=a;x+;因此 a 的值在执行完,两句之后不变。如果我想让a 依照 f()函数体中的操作来改变应该怎么写呢,这里就要用到函数的引用型(这种语法是C+中的,C 中没有,C 中是靠传入变量的地址的方法来实现的,写起来比较麻烦且容易出错
15、,因此这里采用C+的语法),其函数声明方法如下:voidf(int&x)x+;这样就相当于 a 取代了 x 的位置,函数 f()就是在对 a 本身进行操作,执行完两完整版:http:/ 的值由0 变为 1。上边讲到的是对针对普通变量的“引用型”,如果传入的变量是指针型变量,且在函数体内要对传入的指针进行改变,则需写成如下形式:voidf(int*&x)/指针型变量在函数体中需要改变的写法。x+;执行完上述函数后,指针x 的值自增1。说明:这种写法很多同学不太熟悉,但是它在树与图的算法中应用广泛,在之后的章节中考生要注意观察其与一般引用型变量的书写差别。上边是单个变量作为函数参数的情况。如果一个
16、数组作为函数的参数,该怎么写呢?传入的数组是不是也有“引用型”一说呢?对于数组作为函数的参数,这里讲两种情况,一维和二维数组。一维数组作为参数的函数声明方法:voidf(intx,intn);对于第一个参数位置上的数组的定义只需写出两个中括号即可,不需要限定数组长度(即不需要写成 f(intx5,intn),即便是你传入的数组真的是长度为 5),对于第二个参数n,是写数组作参数的函数的习惯,用来说明将来要传进函数加工的数组元素的个数,并不是指数组的总长度。二维数组作为参数的函数声明方法:voidf(intxMAX,intn);如果函数的参数是二维数组,数组的第一个中括号内不需要写上数组长度,而
17、第二个中括号内必须写上数组长度(假设MAX 是已经定义的常量)。这里需要注意,你所传入的数组第二维长度也得是MAX,否则出错,比如:voidf(intx5);inta105;intb103;f(a);f(b);/参数正确/参数错误要注意的是,将数组作为参数传入函数,函数就是对传入的数组本身进行操作,即如果函数体内涉及到改变数组数据的操作,传入的数组中的数据就会依照函数的操作来改变。因此,对于数组来说,没有“引用型”和“非引用型”之分,可以理解为只要数组作为参数,都是引用型的。完整版:http:/ int型。如果没有返回值,声明函数的时候用void,前边讲过的函数中已经有所体现。返回值常常用来作
18、为判断函数执行状态(完成还是出错)的标记,或者一个计算的结果。严奶奶的书中出现过类似于下边这样的函数。STATUSf(ELEMTYPEa)if(a=0)returnERROR;elsereturnOK;对于一些基础稍差的同学来说,这个函数麻烦了,STATUS,ELEMTYPE,ERROR,OK这都什么东西,其实严奶奶在离这个函数很远的地方写过这些语句:#defineERROR 1#defineOK 0typedefSTATUS bool/这句中的bool是布尔型,只取两个值/0和 1,其实用bool用 int型代替就可以,/所以对于考研bool用处不大。typedefELEMTYPEint在那
19、个函数前边加上这四句是否看懂了呢?看懂后可以把它翻译一下就能写出以下代码:boolf(inta)/本行可换成intf(inta)if(a=0)return1;elsereturn0;上边这种写法是不是清楚明白了。严奶奶之所以要将程序写的如此个性,原因有两个,一是上边那个自己另起的类型名或者常量名,都有着实际的意义,STATUS 代表状态,OK代表程序执行成功,ERROR 代表出错,这样代码写的就更人性化。二是,如果我们在写一个大工程,对于其中的一个变量,在整个工程中都已经用int型定义过了,但是工程现在要求修改,将所有int型换成char型,这下麻烦就大了。如果你写成上边那种形式,将int型起
20、个新名字ELEMTYPE,在整个程序中凡是类似于intx;的语句都写成ELEMTYPEx;此时如果要统一更换数据类型,只需将typedefELEMTYPEint这一句中的int换成char即可,这无疑是十分方便的事情,这就是typedef对于程序设计的意义所在(#define也能达到类似的目的)。但显然的是,这对考研答卷意义不大。完整版:http:/ 算法的时间复杂度与空间复杂度分析基础121 考研中的算法时间复杂度杂谈对于这部分,要牢记住一句话:将算法中基本操作的执行次数作为算法的时间复杂度。这里我们所讨论的时间复杂度,不是执行完一段程序的总时间,而是其中基本操作的总次数。因此对于一个算法进
21、行时间复杂度分析的要点,无非是明确算法中哪些操作是基本操作,然后计算出基本操作所重复执行的次数。在考试中算法题目里你总能找到一个n,可以称为问题的规模,比如要处理的数组元素的个数为n,而基本操作所执行的次数是n 的一个函数f(n)(这里的函数是数学中的函数的概念,不是 C 或 C+语言中的函数的概念)。对于求其基本操作执行的次数,就是求函数f(n)。求出以后我们就可以取出f(n)中随 n 增大增长最快的项,然后将其系数变为 1 做为时间复杂度的度量,记为 T(n)=O(f(n)中增长最快的项/此 项 的 系 数),比 如 f(n)=2n3+4n2+100,则 其 时 间 复 杂 度 为 为T(
22、n)=O(2n3/2)=O(n3)。其实计算算法的时间复杂度,就是给出相应的数量级,当 f(n)与 n 无关时,时间复杂度 T(n)=O(1);当 f(n)与 n 是线性关系时,T(n)=O(n);是平方关系时,T(n)=O(n2);以此类推。说明:考研中常常要比较各种时间复杂度的大小,常用的比较关系如下:O 1 O log2n O n nlog2n On2 O n3 O nk O 2n通过以上分析我们总结出计算一个算法时间复杂度的步骤如下:(1)确定算法中的基本操作,以及问题的规模。(2)根据基本操作执行情况计算出规模n的函数f(n),并确定时间复杂度为T(n)=O(f(n)中增长最快的项/
23、此项的系数)。注意:有的算法中基本操作执行次数跟初始输入的数据有关。如果题目不做特殊要求,一般我们依照使得基本操作执行次数最多的输入来计算时间复杂度,即将最坏的情况作为算法时间复杂度的度量。122 例题选讲例题 1:求出以下算法的时间复杂度。voidfun(intn)inti=1,j=100;while(in)j+;i+=2;分析:第一步:找出基本操作,确定规模n。找基本操作(所谓基本操作,即其重复执行次数和算法的执行时间成正比的操作,通俗点说,这种操作组成了算法,当它们都执行完的时候算法也结束了,多数情况下我们取最深层循环内的语句所描述的操作作为基本操作),显然题目中j+;与 i+=2;这两
24、行都可以完整版:http:/ 有关,因此参数n就是我们所说的规模n。第二步:计算出n 的函数f(n)。显然,n 确定以后,循环的结束与否与 i有关,i的初值为 1,每次自增 2,假设 i自增 m 次后循环结束,则 i最后的值为 1+2 m,因此有 1+2 m+K=n(其中 K 为一个常数,因为在循环结束时 i的值稍大于 n,为了方便表述和进一步计算,用 K 将 1+2 m 修正成 n。因为K 为常数,所以这样做不会影响最终时间复杂度的计算),解得m=(n-1-K)/2,即f(n)=(n-1-K)/2,可以发现其中增长最快的项为 n/2,因此时间复杂度 T(n)=O(n)。例题 2:分析以下算法
25、的时间复杂度。voidfun(intn)inti,j,x=0;for(i=1;in;i+)for(j=i+1;j=n;j+)x+;分析:x+;处于最内层循环,因此取 x+;做为基本操作。显然 n 为规模。可以算出 x+;的执行次数为 f(n)=n(n-1)/2,变化最快的项为 n2,因此时间复杂度为 T(n)=O(n2)。例题 3:分析以下算法的时间复杂度。voidfun(intn)inti=0;s=0;while(sn)i+;s=s+i;分析:显然 n 为规模,基本操作为 i+;s=s+i;i与 s 都从 0 开始,假设循环执行 m 次结束,则有 s1=1,s2=1+2=3,s3=1+2+3
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 考研 计算机 数据结构 高分 笔记
限制150内