1、第六章 模板与数据结构,模板是建立通用的与数据类型无关的算法的重要手段,在学习与数据结构相关的表、排序与查找的知识和算法时,要逐步熟悉函数模板和类模板的编程方法。,第六章模板与数据结构,6.1 模板,6.5 函数指针与指针识别(选读),6.4 模板与类参数,6.3 索引查找与指针数组,6.2 排序与查找,6.1 模板,参数化程序设计:通用的代码就必须不受数据类型的限制,可以把数据类型改为一个设计参数。这种类型的程序设计称为参数化(parameterize) 程序设计。这种设计由模板(template) 完成。包括函数模板(function template)和类模板(class templat
2、e)。,6.1.2 类模板与线性表,6.1.1 函数模板及应用,6.1.1 函数模板及应用,(template parameter list)尖括号中不能为空,参数可以有多个,用逗号分开。模板参数主要是模板类型参数。,模板类型参数(template type parameter)代表一种类型,由关键字 class 或 typename(建议用typename ) 后加一个标识符构成,在这里两个关键字的意义相同,它们表示后面的参数名代表一个潜在的内置或用户定义的类型。,函数模板用来创建一个通用函数,支持多种不同类型形参。 函数模板定义: template返回类型 函数名(形式参数表) ;/函数体
3、,6.1.1 函数模板及应用,【例6.1】用函数模板求数组元素中最大值 template Groap max(const Groap *r_array,int size)int i;Groap max_val=r_array0;for (i=1;imax_val) max_val=r_arrayi;return max_val; 类型参数Groap在程序运行中会被各种内置(基本)类型或用户定义类型所置换。模板参数表的使用与函数形式参数表的使用相同,都是位置对应。类型和值的置换过程称为模板实例化 (template instantiation)。 形参size 表示 r_array 数组的长度。
4、,6.1.1 函数模板及应用,模板实参推演: 函数模板根据一组实际类型或(和)值构造出独立的函数的过程通常是隐式发生的,称为模板实参推演(template argument deduction)。 下面完成【例6.1】 int ia5=10,7,14,3,25; double da6=10.2,7.1,14.5,3.2,25.6,16.8; string sa5=“上海“,“北京“,“沈阳“,“广州“,“武汉“; int main() int i=max(ia,5);cout “整数最大值为:“iendl; double d=max(da,6);cout “实数最大值为:“dendl;stri
5、ng s=max(sa,5);cout “字典排序最大为:“sendl;return 0; ,6.1.1 函数模板及应用,第一次调用时,Groap被int取代。第二次调用,Groap 被double取代。第三次Groap 被string取代。为了判断用作模板实参的实际类型,编译器需检查函数调用中提供的函数实参的类型。ia 的类型为int 数组,id的类型为double 数组。都被用来决定每个实例的模板参数。该过程称为模板实参推演。,当然也可以显式指定(explicitly specify),如: i=max(ia,5); d=max(da,6); 这样可读性更好。这里数组名是作为指向数组首元素
6、的指针进行传递,数组长度是由size参数进行传递的。,模板实参推演过程:,6.1.1 函数模板及应用,在main()函数中,由调用函数模板(functron template) 而生成的函数,称为模板函数(template function)。这两个概念须分清楚。,函数模板与模板函数:,【例6.2】矩阵运算:矩阵转置与矩阵相乘函数模板。下标作为参数传递。解决例5.5程序的通用性问题。,6.1.1 函数模板及应用,请注意:与函数声明不同,函数模板的声明必须含变量名。因为两者编译过程不一样,函数模板必须先转换为模板函数。 template void inverse(T1 *mat1, T2 *ma
7、t2,int a,int b); template void multi(T1 *mat1,T2 *mat2,T2 *result,int a,int b,int c); template void output(T *mat,char*s,int a,int b); 注意:mat2和result属同一类型,均为由具有相同元素数量的一维数组所组成的二维数组。本例为mat34和result64。记住数组最高维是不检查边界的。,函数模板的声明:,6.1.2 类模板与线性表,类模板定义: template class 类名 /类定义体 ; /再次指出分号不可少 template 返回类型 类名:成员
8、函数名1(形参表) ;/成员函数定义体 template 返回类型 类名:成员函数名n(形参表) ;/成员函数n定义体 模板参数有两种:模板类型参数和模板非类型参数。,6.1.2 类模板与线性表,模板非类型参数由一个普通的参数声明构成。表示该参数名代表了一个潜在的常量,企图修改这种参数的值是一个错误。如数组类模板,可以有一个数组长度的非类型参数: templateclass arrayT vectori;int size; public:array():size(i)/等效array()size=i;参见4.4.3节. . ;,模板非类型参数:,6.1.2 类模板与线性表,注意: 在类外定义的
9、类模板中的成员函数必须是函数模板。这样的成员函数只有在被调用(或取地址)时才被实例化。成员函数模板定义中,指定成员函数所在类域的类型名后跟的中成员,与类模板的中的类型参数名相同,但不加 typename 或class。,与包含对象成员的构造函数类似,实际是一个模板实例化的类型实参表,所以不加 typename 或class。,6.1.2 类模板与线性表,从通用的类模板定义中生成类的过程称为模板实例化(template instantiation),其格式为: 类名 通常与定义对象同时完成: 类名 对象名;,模板实例化:,例如:标准串类模板basic_string,它提供了复制、查找等典型串操作
10、。它包含在头文件中。文件中包括有: typedef basic_string string; /typedef是类型定义关键字,见5.6.1节 标准串类模板basic_string实例化为上一章讨论的字符串类string。因为字符串中的字符可以是ASCII码,也可以是中文汉字编码,还可以是unicode编码,所以使用类模板是合理的。,6.1.2 类模板与线性表,线性表是数据结构中的概念。数组中除第一个元素外,其他元素有且仅有一个直接前驱,第一个元素没有前驱;除最后一个元素外,其他元素有且仅有一个直接后继,最后一个元素无后继。这样的特性称为线性关系。所议称数组元素在一个线性表中。线性表实际包括顺
11、序表(数组)和链表。,对顺序表的典型操作包括:计算表长度,寻找变量或对象x(其类型与表元素相同)在表中的位置(下标值),判断x是否在表中,删除x,将x插入列表中第i个位置,寻找x的后继,寻找x的前驱,判断表是否空,判断表是否满(表满不能再插入数据,否则会溢出,产生不可预知的错误),取第i个元素的值。,这些操作与数组封装在一起可以定义一个类,考虑到数组元素的类型可以各不相同,所以定义为类模板。,线性表:,6.1.2 类模板与线性表,由编译器编译时分配内存的数组称为静态数组,数组的长度是不可改变的。如果定义了30个元素的数组,后来需要40个元素,是不可能续上10个的,必然产生溢出。因系统不检查数组
12、边界,进而产生不可预知的运行时错误,所以一般采用“大开小用”的方法,即数组定义的较大,而实用较小。为防止不可避免的溢出,应在添加新数据时判断表是否满。,静态数组:,6.1.2 类模板与线性表,当需要在顺序表中删除一个元素时,必须把它后面的元素的数据全部顺序前移一个位置,否则表中前驱后继关系被破坏。图6.1表中有11个数据。删去第i个元素的数据,就是把第i+1个元素拷入第i个元素,把第i+2个元素拷入第i+1个元素,依此类推,直到最后一个元素(下标为10),现在表长度为10。,删除顺序表元素:,6.1.2 类模板与线性表,图6.2 向表中插入一个数据,当需要在顺序表的指定位置i 插入一个数据x时
13、,必须为它腾出这个位置,把从该位置开始向后的所有元素数据,后移一个位置,最后才插入。后移时从最后一个元素开始,参见图6.2。现在长度为12,这样的移动也是不可少的,否则新插入的数据x会冲掉原来的数据,或先移的数据冲掉未移的数据。,添加顺序表元素:,【例6.3】顺序表类模板,template class seqlistT slistsize; /存放顺序表的数组int Maxsize; /最大可容纳项数 int last; /已存表项的最后位置 public:seqlist()last=-1;Maxsize=size; /初始化为空表int Length() constreturn last+1
14、; /计算表长度int Find(T /重载下标运算符,检验主程序,.2 排序与查找,6.2.2 常用的排序法,6.2.1 常用查找方法,排序(sorting): 是最重要的计算应用之一。例如查字典,字典中的词条是按序存放的,我们才能按字母顺序找到要查的字。又如图书馆的藏书也是按书的编号有序排列的。在计算机上数据库里的资料也是有序排列的。 查找(search): 当然也是最常见的运算,就是在数据集合中寻找满足条件的数据对象,找到后进一步给出对象的具体信息,在数据库技术中称为检索(retrieval)。,6.2.1 常用查找方法,顺序查找: 从第一个元素开始,顺序查找直到找到或查到最后一个元素为
15、止。查找是按关键字(key word)进行。可以唯一地把资料区分出来的数据被称为主关键字。如学生的资料(结构体变量): struct student int id ; /学号 char name20; / 姓名 char sex; / 性别 int age; / 年龄 char address60; /家庭地址 float eng, phy,math , electron;/英语,物理,数学和电子学成绩 ; 学号可作为主关键字。 如果关键字小的排在前面,我们称为升序排列,反之为降序排列。这时可以采用对半查找(binary search)。,6.2.1 常用查找方法,首先安排两个指针low和hi
16、gh指向首尾两元素,取mid= (low+ high)/2,如mid指向元素是所查找的,则结束。如果该元素关键字大了,则取low=mid +1, high不变,继续查找;如果该元素关键字小了,则取 high=mid-1,low不变,继续查找。如果查到lowhigh仍未找到,则失败,停止。,对半查找:,6.2.1 常用查找方法对半查找,注意:low=mid+1和high=mid-1非常重要,没有加和减时,可能数据存在而找不到。如在图6.3中,如果找到了仅剩20、21、23这一步,这时取low=mid,则剩下21、23,mid = (low + high)/2,得mid = low ,下一步low
17、 = mid ,还是剩下21、23,mid 永远不能指向23,永远找不到23了。,6.2.1 常用查找方法,【例6.4】对半查找递归算法,作为有序表模板类成员函数。 递归方法易读易懂,但效率低。注意递归的隐式循环代码编写。,【例6.5】对半查找迭代算法。 该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。 *本例中出现的成员函数Binarysearch(T & x)const ,称为 const成员函数,该函数保证只读。相应节点对象重载的比较运算符也必须是const。,6.2.1 常用查找方法,例6.5节点对象重载的比较运算符: class Elementin
18、t key; / 其他域省略 public:bool operator(Element ele)constreturn keyele.key;void putkey(int k)key=k; ; /注意这里加了const,6.2.1 常用查找方法,const成员函数和const对象: const成员函数: 返回类型 函数名(形参表) const; 该函数的this指针所指对象为常量,即它不能修改对象的数据成员,而且在函数体内只能调用const成员函数(它们不会修改对象的数据成员),不能调用其他成员函数。如果编程时不慎修改对象的数据成员,编译器会报错。 const对象: const 类名 对象名
19、; 表示该对象的数据成员均为常量,并仅可访问const成员函数。,6.2.1 常用查找方法,散列查找:散列(hash)查找是最快的查找方法。前文介绍的两种查找方法都是将需查找的关键字值与表中的数据元素的关键字值进行比较而达到查找的目的。如果能找到一个函数 f(key),将关键字经过函数的运算转换为数据表中的位置,直接将数据元素插入在该位置上。在查找中可直接取用该位置的数据元素。这样的组织称为散列,利用散列技术查找的方法称为散列查找,散列查找是一种直接查找。亦用音译称哈希查找。,6.6.2 常用的排序法,排序的概念: 排序(sorting)是数据处理中经常使用的一种重要运算。其功能是将数据元素的
20、无序序列调整为一个有序序列。数据元素中一般有多个数据项,排序可选择其中一个可排序的数据项(可进行比较运算)作为依据,称为排序关键字。 对高考考生的统计表进行排序,可根据考生的准考证号,这样的关键字可以保证排序结果的唯一性,称主关键字。为了便于录取,也可以按高考总分排序,只可称关键字,这样同一分数的人很多,这些人的排名可再取一个次关键字如数学或语文分来排序,以减少重复排名的随意性。从小到大排序称升序,反之为降序。最常见的是插入排序、选择排序和交换排序。,6.2.2 常用的排序法,1插入排序(Insert Sorting) (1)直接插入排序的思想是:(以升序为例)当插入第i(i=1)个元素sli
21、时,前面的元素sl0,sl1,sli-1已经排好序,我们将sli的关键字与sli-1, sli-2,的关键码顺序进行比较,找到第一个比它小的,则sli插到该元素之后。,直接插入排序算法中用了一个临时变量temp,要插入的元素放到temp中,这样插入前各元素后移时允许将该元素冲掉。,6.6.2 常用的排序法,【例6.6】升序直接插入排序算法,*(2)对半插入排序(Binary Insert Sort)是用对半查找的思想取代顺序查找。对半插入排序要快于插入排序。,【例6.7】升序对半插入排序算法,6.2.2 常用的排序法,图6.6从下往上扫描的冒泡程序,2交换排序 交换排序的基本思想是按关键字两两
22、排序对象,如果发生逆序则交换之,直到所有的对象都排好序为止。,冒泡排序基本思想参见图6.6。最左列为最初的情况,最右列为完成后的情况。首先我们从一列数据底部开始,相邻的两数据进行比较,小的数放上面,一趟比较下来,最小的数据冒到了最上面。再缩小区域,按同样方法继续下一趟交换,如果有一趟比较中没有发生交换,则已排好序。图6.6中,第5列就已排好序,若再继续下一趟就不会发生交换。,6.2.2 常用的排序法,3选择排序(Selection Sort) 基本思想是:每一趟从待排序的记录中选出关键字最小的元素,顺序放在已排好序的子序列的后面,直到全部记录排序完成。直接选择排序(Straight Selec
23、tion Sort)是最简单的。此方法的最大优点是易读。缺点是做过的工作和序列的部分有序性利用不上,效率低。选择排序中也有可能利用到以前的工作的方法,如堆排列(Heap Sort),49 38 65 97 76 13 27 4913 38 65 97 76 49 27 4913 27 65 97 76 49 38 4913 27 38 97 76 49 65 4913 27 38 49 76 97 65 4913 27 38 49 49 97 65 7613 27 38 49 49 65 97 7613 27 38 49 49 65 76 97图6.7直接选择排序的过程,6.3 索引查找与指针
24、数组,指针数组: 数据元素为指针的数组称指针数组 。 索引查找: 索引,就象一本书的目录,找到标题,再看一下页号,立即可以翻到。如果每一个查找对象的数据元素很大,比如一个学生的简历,要排序也挺麻烦,去查找也不方便。如果每位同学的简历对应一个指针,构成一个数组,而把学生学号作为数组元素的下标,这样就形成了一个指针数组,找到学号对应元素,其所保存的指针值,即简历的地址,查找起来要方便多了,称索引查找(Index Search)。参见图6.8。,图6.8用指针数组进行索引查找,6.3索引查找与指针数组,6.3索引查找与指针数组,字符型指针数组可以实现字符串数组的功能。这些字符串的长度可以不等;所以用
25、指针数组更方便。如存储每周7天的英文名称,可定义一个char*name7的一维字符指针数组,如图6.9。,指针数组与字符串:,6.4 模板与类参数,模板的通用性: 在前文所讨论的排序与查找中,模板的通用性得到了很好的体现。关键技术是数组的数据元素说明为类,并重载小于运算符,该运算符实际是将元素类对象的比较转化为类对象关键字的比较。因使用标准字符串类string看不见其内部构成,下面用自定义字符串类来进一步阐述这个问题。,【例6.10】冒泡排序算法,关键字为自定义字符串。,【例6.6】中同样可以用mystring代替标准字符串类string,不过那儿有两个层次:元素类和字符串类。运算符的重载可以
26、由对象成员的重载的运算符(或成员)函数一级一级调用生成。在类定义中运算符和函数的重载是面向对象程序设计实现通用性的非常得力的工具。,6.4 模板与类参数,以类对象作为函数的实参,实现被积函数(类对象的成员函数)的传递:,【例6.12】设计梯形法求积分的函数模板。,以模板参数T来引入被积函数类,由该参数调用欲求定积分的函数,【例6.11】设计梯形法求积分的类模板。,求积分的函数为独立的非成员函数。该方法更简洁。,6.4 模板与类参数,函数模板常用方式: (1) 函数模板作为类模板的成员函数,在本类中重载函数和运算符,直接访问私有数据成员,实现通用算法。这是标准的面向对象的方法。 (2) 独立的非
27、成员函数函数模板处理模板类(或普通类,或普通数据),以类模板(或类对象,或普通数据)为参数,借助模板类中重载的函数或运算符,实现通用算法。但间接访问私有数据成员。这也是常见的。,6.5函数指针与指针识别(选读) 6.5.1 函数指针及其应用(选读),函数名对应于该函数执行代码的入口地址。通过取地址运算符“&”也可以取得函数的入口地址。指向函数的指针可以作为函数的参数传递。定义方式如下: 返回类型 (*指针变量名)(参数表)由于一个函数不能以函数作为参数,所以当一个函数需要将函数作为参数时必须借用指向函数的指针(也可以用包含该函数的类对象)。,函数名与函数指针:,6.5.1 函数指针及其应用(选
28、读),【例6.13】梯形法求积分的函数integer()作为通用函数,可求任一函数的定积分。,函数指针的使用方法: 如有函数void scopy(char *q,char *p),函数名scopy代表函数的入口地址,或者说是一个指向该函数的指针,该指针的类型为: void (*)(char *q,char *p) /q与p写与不写是一样的 定义这种函数指针类型的变量pf并用scopy初始化: void(* pf)(char *,char *)=scopy; /或者=&scopy,两者等效 pf将存储scopy函数的入口地址。,6.5.2指向类成员的指针(选读),在类对象中有隐含的this指针,
29、用以正确访问成员函数。所以指向类成员函数的指针有其特殊性。,指向类成员函数的指针:,普通函数指针存储函数的地址,可以直接用来调用指定函数。成员函数指针必须首先被绑定在一个对象或指针上,才能获得被调用对象的this指针,然后才能调用指针所指向的成员函数。虽然两者都被称为指针,但是它们是不同类型的数据。,成员函数有一个非成员函数没有的属性:它所属的类(class)。所以指向成员函数的指针需要三个方面的匹配:参数的类型和个数,返回类型和所属的类类型。,6.5.2指向类成员的指针(选读),成员函数指针的用法(绑定):,CGoods car; (car.*pf)(); /将指针pf与对象car绑定,最终
30、等效调用car. GetPrice ();,上式表示指针pf与对象car绑定,指向了car. GetPrice ()。所以指向成员函数的指针存储的不是成员函数的地址,绑定后才获得地址。,也可以用对象代替类进行初始化,效果一样: CGoods car,motor; float (CGoods:*pf)()=motor.GetPrice; /等效float (CGoods:*pf)()= CGoods:GetPrice; 并未绑定 (car.*pf)(); /将指针pf与对象car绑定,指向类成员函数的指针的说明及初始化:以指向商品类 GetPrice()函数的指针为例: float (CGood
31、s:*pf)()= CGoods:GetPrice;,6.5.3 指针的识别方法(选读),说明中包括多种说明符容易造成阅读和理解的困难。一种理解和构造对象说明的方法是:先撇开标识符,按从右到左的顺序逐个解释每个说明符,如果有括号则改变解释的先后,先解释括号内再解释扩号外。例如: int *arrp5; 按下列顺序理解:五个元素的数组、每个元素是一个指针、指针指向整型,所以 arrp 是一个有五个整型指针作为数组元素的数组。 int (*parr)5; 是一个指针,指针指向一个包含五个元素的数组,每个元素是一个整型,所以 parr 是一个指向五个整型数的数组的指针。,复杂说明阅读和理解的方法:,
32、6.5.3 指针的识别方法(选读),答案: i 是一个整型的变量; ip 是一个指向整型变量的指针,即 ip 中存储的是另一个整型变量的地址; f 是一个返回整型值的函数; fp 是一个返回整型指针的函数,即 fp 返回的是一个指向整 型变量的指针; pf 是一个指向返回整型值的函数的指针;,复杂说明的实例: int i, *ip, f(), *fp(), (*pf)(),6.5.3 指针的识别方法(选读),答案: pfp 是一个指向函数的指针,该函数返回一个整型指针; a 是一个有五个整型元素的数组; ap 是一个指针数组,每个元素是一个指向整型的指针; pa 是一个指向整型数组的指针,该数
33、组有五个整型元素; fap 是一个指向数组的指针,该数组的每个元素都是一个指 向函数的指针,而所指的函数的返回值是整型。,复杂说明的实例: *(*pfp)(), a5, *ap5, (*pa)5, (*(*fap)();,第六章 模板与数据结构,完,谢谢!,【例6.2】矩阵运算,template void inverse(T1 *mat1,T2 *mat2,int a,int b) int i,j;for (i=0;ib;i+) for (j=0;ja;j+)mat2ji=mat1ij;return; ,template void multi(T1 *mat1,T2 *mat2,T2 *res
34、ult,int a,int b,int c)int i,j,k;for(i=0;ia;i+) for(j=0;jc;j+)resultij = 0;for(k=0;kb;k+) resultij+=mat1ik*mat2kj;return;,【例6.2】矩阵运算,template void output(T *mat,char *s,int a,int b)int i,j; coutsendl;for(i=0;ia;i+)for(j=0;jb;j+) coutsetw(4)matij“ “;coutendl; return; ,【例6.2】矩阵运算,int main()int middle63
35、, result64;int matrix136=8,10,12,23,1,3,5,7,9,2,4,6,34,45,56,2,4,6; int matrix234=3,2,1,0,-1,-2,9,8,7,6,5,4;char *s1=“result“;char *s2=“middle“;inverse(matrix1,middle,6,3);/显式:inverse (matrix1,middle,6,3);multi(middle,matrix2,result,6,3,4);/显式:multi (middle,matrix2,result,6,3,4); output(matrix1,“mat
36、rix1“,3,6);output(middle,s2,6,3);output(matrix2,“matrix2“,3,4);output(result,s1,6,4);return 0; ,【例6.3】顺序表类模板,template int seqlist:Find(T /找到,返回下标 ,【例6.3】顺序表类模板,template bool seqlist:IsIn(T ,template T,【例6.3】顺序表类模板,template bool seqlist:Insert(T ,【例6.3】顺序表类模板,template bool seqlist:Remove(T /表中不存在x ,【
37、例6.3】顺序表类模板,template int seqlist:Next(T ,【例6.3】顺序表类模板,int main()seqlist seqlisti;/顺序表对象seqlisti的元素为整型int i,j,k,a10=2,3,5,7,11,13,17,19,23,29;for(j=0;j10;j+) /把素数写入if (!seqlisti.Insert(aj,j)cout“表太大放不下了!“endl; break; j=seqlisti.Length();for(i=0;ij;i+) coutseqlisti.Get(i) ;cout endl ; /打印出seqlisti.sli
38、st-素数表for(j=0;j10;j+) seqlistij=0; /采用下标运算符运算for(j=0;j10;j+) coutseqlistij ;coutendl;for(j=0;j10;j+) seqlistij=aj;,【例6.3】顺序表类模板,seqlisti10=31; /实验能否增加元素for(j=0;j11;j+) coutseqlistij ;coutendl; k=7;if (seqlisti.IsIn(k) cout“素数7在顺序表中“ endl;/因形参为引用,所以实参不可用整数常量7else cout “素数7不在顺序表中“endl;k=17;if (seqlist
39、i.Remove (k) cout“删除素数17“endl; /删除素数17else cout“找不到素数17,无法删除“;j=seqlisti.Length( ) ;for (i=0;ij;i+) coutseqlisti.Get(i) ; /打印剩下的素数coutendl;,【例6.3】顺序表类模板,if (seqlisti.Insert(k,j-1) / 把素数17装回去,成功则打印j=seqlisti.Length ( );for (i=0;ij;i+) coutseqlisti.Get(i) ; coutendl; cout“打印17后一个素数:“seqlisti.Get(seqli
40、sti.Next(k)endl;cout“打印17前一个素数:“seqlisti.Get(seqlisti.Prior(k)endl;cout“素数17在表中位置(下标)为:“seqlisti.Find(k)endl;if(seqlisti.IsEmpty( ) cout“表是空的“endl;else cout“表不空“endl;if (seqlisti.IsFull() cout“表是满的“endl; else cout“表也不满“endl;if (seqlisti.IsIn(k) cout“素数17在表中“endl; return 0; ,【例6.4】对半查找递归算法,【例6.4】对半查找
41、递归算法,作为有序表模板类成员函数。 template int Orderedlist:Binarysearch (const T 未找到但结束了,返回mid不加判断 递归方法易读易懂,但效率低。注意递归的隐式循环代码编写。,有序表和结点类模板定义,基本元素为类Elemen对象 : class Elementint key; / 其他域省略 public:bool operatorclass Orderedlistint maxsize;int last;T slistsize; public:Orderedlist()last=-1;maxsize=size;int Binarysearch
42、(T ,【例6.4】对半查找递归算法,template bool Orderedlist:Insert(T ,【例6.4】对半查找递归算法,int main()const int h=19;int i,k=37;Orderedlist ordlist;int ah=67,61,59,53,47,43,41,37,31,29,23,19,17,13,11,7,5,3,2; /降序Element nh,elem;for(i=0;ih;i+) ni.putkey(ai);for(i=0;ih;i+) ordlist.Insert(ni,0); /始终在0号元素插入,建立升序顺序表elem.putke
43、y(k);ordlist.print();i=ordlist.Binarysearch(elem,0,h-1);cout“整数“k“在表中位置(下标):“iendl;return 0; ,【例6.5】对半查找迭代算法,【例6.5】对半查找迭代算法。 template int Orderedlist:BinarySearch(const T 该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。,【例6.6】升序直接插入排序算法,作为【例6.4】Orderedlist类的成员函数InsertSort(),T为关键字类型。 templatevoid Orderedli
44、st:InsertSort()T temp;int i,j;for (i=1;i0 ,class Elementstring key; / 其他域省略 public:bool operator ordlist;string mslisth=“cat“,“book“,“car“,“zoo“,“fish“,“cab“,“dog“,“cap“,“fox“,“can“;for(i=0;ih;i+) ni.putkey(mslisti);for(i=0;ih;i+) ordlist.Insert(ni,i); /建立顺序表cout“未排序表:“endl;ordlist.print();ordlist.I
45、nsertSort();cout“已排序表:“endl;ordlist.print();return 0;,【例6.7】升序对半插入排序算法,Orderedlist类的成员函数升序对半插入排序算法。当关键字相同时,插入排序原来在前的仍在前,称稳定排序。 template void Orderedlist:BinaryInsertSort()T temp;int low,high,mid,i,j;for (i=1;i=low;j-) slistj+1=slistj;slistlow=temp; ,【例6.8】冒泡排序算法,冒泡排序算法,作为Orderedlist类的成员函数。 template
46、void Orderedlist:BubbleSort()bool noswap;int i,j;T temp;for (i=0;ii;j-) /从下往上冒泡if(slistjslistj-1)temp=slistj;slistj=slistj-1;slistj-1=temp;noswap=false;if(noswap) break; /本趟无交换,则终止算法。 冒泡排序的优点在于可利用原来的数据排列的部分有序性。,【例6.8】冒泡排序算法,学生类为数组元素 class studentint id ; /学号string name; / 姓名char sex; / 性别int age; /
47、年龄string address; /家庭地址float eng, phy, math, electron; /英语,物理,数学和电子学成绩 public:student()student(int,string,char,int,string,float,float,float,float);bool operator(student ele)return idele.id;void show() coutidtnametsextagetaddresstengtphytmathtelectronendl; ;,【例6.8】冒泡排序算法,int main()const int h=4;int i
48、;Orderedlist ordlist;student nh=student(6004327,“张菲“,m,19,“北京路58号“,80,85,90,78),student(6004121,“关雨“,w,19,“天津路64号“,88,75,91,68),student(6004118,“刘蓓“,w,18,“上海路37号“,78,95,81,88),student(6004219,“赵昀“,m,18,“重庆路95号“,78,95,81,88);for(i=0;ih;i+) ordlist.Insert(ni,i); /建立顺序表cout“未排序表:“endl;ordlist.print();ordlist.BubbleSort();cout“已排序表:“endl;ordlist.print();return 0; ,