Unit 8— 第 六 章 模板

57
Unit 8— 第第第 第第 6.1 第第第第第第第 6.4 第第第第第第 6.3 第第第第第第第第第 6.5 第第第第第第第第第 第第第 () 6.2 第第第第第第第第

description

Unit 8— 第 六 章 模板. 6.1 模板与数据结构. 6.4 模板与类参数. 6.5 函数指针与指针识别(选读). 6.2 类模板与排序查找. 6.3 索引查找与指针数组. 例 6.3 顺序表类模板设计. template < typename T, int size> class seqlist { T slist [size]; // 存放顺序表的数 组 int Maxsize ; // 最大可容纳项数 int last; // 已存表项的最后位置 public : - PowerPoint PPT Presentation

Transcript of Unit 8— 第 六 章 模板

Page 1: Unit 8— 第 六 章 模板

Unit 8— 第六章 模板

6.1 模板与数据结构 6.4 模板与类参数

6.3 索引查找与指针数组

6.5 函数指针与指针识别(选读) 6.2 类模板与排序查找

Page 2: Unit 8— 第 六 章 模板

class seqlist { int slist[size]; // 存放顺序表的数组, size 需定义为常量 int Maxsize; // 最大可容纳项数 int last; // 已存表项的最后位置public: seqlist(){last=-1;Maxsize=size;} // 初始化为空表 int Length() const{return last+1;} // 计算表长度 int Find(int & x)const; // 寻找 x 在表中位置(下标) bool IsIn(int & x); // 判断 x 是否在表中 bool Insert(int & x,int i); //x 插入到列表中第 i个位置处(下标) bool Remove(int & x); // 删除 X int Next(int & x); // 寻找 X 的后续位置 int Prior(int & x); // 寻找 x 的前驱位置 bool IsEmpty(){return last==-1;} // 判断表是否空 bool IsFull(){return last==Maxsize -1;} // 判断表是否满 int Get(int i){return i<0||i>last?NULL:slist[i];} // 取第 i 个元素之值 int& operator[](int i); }; // 重载下标运算符 []

例 6.3 顺序表类模板设计 template <typename T,int size>class seqlist { T slist[size]; // 存放顺序表的数组 int Maxsize; // 最大可容纳项数 int last; // 已存表项的最后位置public: seqlist(){last=-1;Maxsize=size;} // 初始化为空表 int Length() const{return last+1;} // 计算表长度 int Find(T & x)const; // 寻找 x 在表中位置(下标) bool IsIn(T & x); // 判断 x 是否在表中 bool Insert(T & x,int i); //x 插入到列表中第 i 个位置处(下标) bool Remove(T & x); // 删除 X int Next(T & x); // 寻找 X 的后续位置 int Prior(T & x); // 寻找 x 的前驱位置 bool IsEmpty(){return last==-1;} // 判断表是否空 bool IsFull(){return last==Maxsize -1;} // 判断表是否满 T Get(int i){return i<0||i>last?NULL:slist[i];} // 取第 i 个元素之值 T& operator[](int i); }; // 重载下标运算符 []

Page 3: Unit 8— 第 六 章 模板

template <typename T,int size> int seqlist<T,size>::Find(T & x)const {//const 成员函数,表明对象的数据只能读不能写。 int i=0; while(i<=last && slist[i]!=x) i++; // 顺序查找是否有 x if (i>last) return -1; // 未找到,返回 -1 else return i; // 找到,返回下标}

例 6.3 顺序表类模板设计

template <typename T,int size> bool seqlist<T,size>::IsIn(T & x){ int i=0; bool found=0; while(i<=last && !found) // 换了一种方法来查找 if (slist[i]!=x) i++; else found=1; // 找到 return found;}

Page 4: Unit 8— 第 六 章 模板

template <typename T,int size> T& seqlist<T,size>::operator[](int i){ if(i<0||i>=Maxsize){ cout<<" 下标出界! "<<endl; exit(1); } while(i>last) last++; // 数组最大下标增长到 i return slist[i];}

例 6.3 顺序表类模板设计

assert(!(i<0||i>=Maxsize));

有没有其他方式?

对 P194 作了调整 !

对 P194 作了调整 !

Page 5: Unit 8— 第 六 章 模板

template <typename T,int size> bool seqlist<T,size>:: Insert(T & x, int i) { int j; if (i<0||i>last+1||last==Maxsize -1) return false; // 插入位置不合理,不能插入(健壮性) else{ last++; for(j=last;j>i;j--) slist[j]=slist[j-1]; // 从表最后位置向前依次后移,空出指定位置来 slist[i]=x; return true; }}

例 6.3 顺序表类模板设计

Page 6: Unit 8— 第 六 章 模板

template <typename T,int size>bool seqlist<T,size>::Remove(T & x){ int i=Find(x),j; // 先去找 x 在哪个位置 if(i>=0){ last--; // 预先将 last 前移一位 for(j=i;j<=last;j++) slist[j]=slist[j+1]; // 依次前移,保证表连续 return true; } return false; // 表中不存在 x}

例 6.3 顺序表类模板设计

Page 7: Unit 8— 第 六 章 模板

template <typename T,int size> int seqlist<T,size>::Next(T & x){ int i=Find(x); if(i>=0 && i<last) return i+1; //x 后继位置 else return -1; //x 不在表中,或在表末尾}

template <typename T,int size> int seqlist<T,size>::Prior(T & x){ int i=Find(x); if(i>0 && i<=last) return i-1; //x 前驱的位置 else return -1; }

例 6.3 顺序表类模板设计

Page 8: Unit 8— 第 六 章 模板

int main(){ seqlist <int,100> seqlisti; //seqlisti 的元素为整型 int i,j,k,a[10]={2,3,5,7,11,13,17,19,23,29}; for(j=0;j<10;j++) // 把素数写入 if (!seqlisti.Insert(a[j],j)){ cout<<“ 表太小放不下了 !"<<endl; break; } j=seqlisti.Length(); for(i=0;i<j;i++) cout<<seqlisti.Get(i)<<' '; cout << endl ; // 打印出 seqlisti.slist[]- 素数表 for(j=0;j<10;j++) seqlisti[j]=0; // 采用下标运算符运算 for(j=0;j<10;j++) cout<<seqlisti[j]<<' '; cout<<endl; for(j=0;j<10;j++) seqlisti[j]=a[j];

例 6.3 顺序表类模板设计

Page 9: Unit 8— 第 六 章 模板

seqlisti[10]=31; // 检验能否增加元素,正确!! for(j=0;j<11;j++) cout<<seqlisti[j]<<' '; cout<<endl; k=7; if (seqlisti.IsIn(k)) cout<<"7 在表中 "<< endl; // 因形参为引用,所以实参不可用整数常量 7 else cout <<"7 不在表中 "<<endl; k=17; if (seqlisti.Remove (k)) cout<<" 删除17"<<endl; // 删除 17 else cout<<" 找不到 17 ,无法删除 "; j=seqlisti.Length( ) ; for (i=0;i<j;i++) cout<<seqlisti.Get(i) <<' '; // 打印剩下的素数 cout<<endl;

例 6.3 顺序表类模板设计

Page 10: Unit 8— 第 六 章 模板

if (seqlisti.Insert(k,j-1)){ // 把素数 17 装回去 ,成功则打印 j=seqlisti.Length ( ); for (i=0;i<j;i++) cout<<seqlisti.Get(i) <<' '; cout<<endl; } cout<<" 打印 17 后一个素数 : “

<<seqlisti.Get(seqlisti.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.3 顺序表类模板设计

Page 11: Unit 8— 第 六 章 模板

单链表类模板设计

回忆结点类:typedef int DataType;class Node{ DataType info; // 数据域 Node *link; // 指针域public: Node(); // 生成空结点的构造函数 Node(const Datatype &); // 生成一般结点的构造函数 void PrintNode(); // 打印当前结点的信息域 friend class SLList; // 以 SLList 为 Node 友元类,SLList 可直接访问 Node 私有数据,比结构安全};

6.1.2 类模板与数据结构—单链表

结点类模板:template <typename DataType> class SLList;template <typename DataType> class Node{ DataType info; // 数据域 Node <DataType> *link; // 指针域public: Node(); // 生成空结点的构造函数 Node(const Datatype &); // 生成一般结点的构造函数 void PrintNode(); // 打印当前结点的信息域 friend class SLList <DataType>; // 以 SLList 为Node 友元类, SLList 可直接访问 Node 私有数据,比结构安全};

infon-1 ^

info1 info0 ……head

tail

Page 12: Unit 8— 第 六 章 模板

Node::Node(){

link=NULL;}

Node::Node(const DataType & data){info=data;link=NULL;

}

void Node::PrintNode(){cout<<info<<endl;

}

6.1.2 类模板与数据结构—单链表

template <typename DataType> Node<DataType>::Node(){ link=NULL;}

template <typename DataType> Node<DataType>::Node(const DataType & data){

info=data;link=NULL;

}

template <typename DataType> void Node<DataType>:: PrintNode(){

cout<<info<<endl;}

Page 13: Unit 8— 第 六 章 模板

链表类模板设计 class SLList{ Node *head,*tail; // 链表头指针和尾指针public: SLList (); // 构造函数,生成头结点 ( 空链表 ) ~SLList(); // 析构函数 void MakeEmpty(); // 清空链表,只余表头结点 Node* TravFind(DataType); // 搜索数据域与 data 相同的结点,返回该结点的地址 void PrintSLL(); // 打印链表的数据域 void GrowUP(const DataType &); // 链表向前生长 void GrowDN(const DataType &); // 链表向后生长 void RemoveAft(Node*); // 删除结点后的结点 void RemoveCur(Node*); // 删除指定结点};

6.1.2 类模板与数据结构—单链表

template <typename DataType> class SLList{ Node <DataType> *head,*tail; // 链表头指针和尾指针public: SLList (); // 构造函数,生成头结点 ( 空链表 ) ~SLList(); // 析构函数 void MakeEmpty(); // 清空链表,只余表头结点 Node <DataType> * TravFind(DataType); // 搜索数据域与 data 相同的结点,返回该结点的地址 void PrintSLL(); // 打印链表的数据域 void GrowUP(const DataType &); // 链表向前生长 void GrowDN(const DataType &); // 链表向后生长 void RemoveAft(Node <DataType> *); // 删除结点后的结点 void RemoveCur(Node <DataType> *); // 删除指定结点};

Page 14: Unit 8— 第 六 章 模板

链表类模板成员函数:template <typename DataType> SLList<DataType>::SLList(){ head=tail=new Node<DataType> ();}template <typename DataType> SLList<DataType>:: ~SLList(){ MakeEmpty(); delete head; head=NULL;}template <typename DataType> void SLList<DataType>:: MakeEmpty(){ Node<DataType> *p; while(head->link!=NULL) { // 将链表结点从链中脱离 p=head->link; head->link=p->link; delete p; p=NULL; // 删除 ( 释放 ) 脱离下来的结点 } tail=head; // 表头指针与表尾指针均指向表头结点,表示空链}

6.1.2 类模板与数据结构—单链表

Page 15: Unit 8— 第 六 章 模板

template <typename DataType> Node <DataType>* SLList<DataType>:: TravFind(DataType key){ Node<DataType>*p=head->link; while(p!=NULL&&p->info!=key)p=p->link; return p;// 搜索成功返回该结点地址,不成功返回NULL }template <typename DataType> void SLList<DataType>:: PrintSLL() { // 显示链表 Node<DataType>* p=head->link; while(p!=NULL) { cout<<p->info<<'\t'; p=p->link; } cout<<endl;}

6.1.2 类模板与数据结构—单链表

Page 16: Unit 8— 第 六 章 模板

template <typename DataType> void SLList<DataType>:: GrowUP(const DataType& data){

Node<DataType>*p=new Node<DataType> (data);

p->link=head->link;head->link=p; // 新结点始终在头结点之后

}

template <typename DataType> void SLList<DataType>:: GrowDN(const DataType& data){

Node<DataType>* p=new Node<DataType>(data);

tail->link=p;tail=p;tail->link=NULL;

}

6.1.2 类模板与数据结构—单链表

Page 17: Unit 8— 第 六 章 模板

template <typename DataType> void SLList<DataType>:: RemoveAfter(Node<DataType>*p){ Node<DataType>*q; q=p->link; if(q!=NULL){ p->link=q->link; delete q; q=NULL;}}template <typename DataType> void SLList<DataType>:: RemoveCur(Node<DataType>*p){ Node<DataType>*q=head; while(q->link!=NULL && q->link!=p) q=q->link;// 遍历查找 if(q->link==tail) tail=q;// 已经找到末尾 RemoveAfter(q);// 删除 q 后面的结点 p}

6.1.2 类模板与数据结构—单链表

Page 18: Unit 8— 第 六 章 模板

测试:

void int main(){SLList <int>L1; Node<int> n, *tmp;for(int j=0;j<10;j++)

L1.GrowDN(j); // 向后生成链表cout<<" 初始链表 :"; L1.PrintSLL(); // 打

印表L1.GrowUP(20);// 向前插入到头结点后cout<<" 插入结点后的链表 :";

L1.PrintSLL(); // 打印tmp=L1.TravFind(20); // 查找插入的结点n=*tmp;cout<<" 找到结点的信息域 :";

n.PrintNode();L1.RemoveCur(tmp);// 删除插入的结点cout<<" 删除结点后的链表 :";

L1.PrintSLL();// 打印return 0;

}

6.1.2 类模板与数据结构—单链表

类到类模板的设计要点:( 1 )将类中潜在可变的数据类型或者常量,替换为模板参数,设计模板头;( 2 )查找类中包含的其他类模板,将类名后加 < 模板参数名表 > ,便于随后的实例化。

Page 19: Unit 8— 第 六 章 模板

a0

an-2

……

a1

an-1

bottom

压栈

toptop

top

top

top

退栈

顺序栈类模板设计:6.1.2 类模板与数据结构—栈

class Stack{ int top; // 栈顶(下标) DataType *elements; // 指向动态建立的栈 int maxSize; // 栈最大容纳的元素个数public: Stack(const int&size=20); // 构造函数, top= -1 ~Stack(){delete[ ] elements; elements=NULL;} void Push(const DataType &); // 压栈, top++ DataType Pop(); // 弹出, top-- DataType GetElem(int); // 随机取数据, top 不变 void MakeEmpty(){top= -1;} // 清空栈 bool IsEmpty() const{return top== -1;}

// 判栈空 bool IsFull() const{return top==maxSize-1;}

// 判栈满 void PrintStack(); // 输出栈内所有数据};

template <typename DataType, int size> class Stack{ int top; // 栈顶(下标) DataType *elements; // 指向动态建立的栈 int maxSize; // 栈最大容纳的元素个数public: Stack(); // 构造函数 ~Stack(){delete[ ] elements; elements=NULL;} void Push(const DataType &); // 压栈, top++ DataType Pop(); // 弹出, top-- DataType GetElem(int); // 随机取数据, top 不变 void MakeEmpty(){top= -1;} // 清空栈 bool IsEmpty() const{return top== -1;}

// 判栈空 bool IsFull() const{return top==maxSize-1;}

// 判栈满 void PrintStack(); // 输出栈内所有数据};

Page 20: Unit 8— 第 六 章 模板

template <typename DataType , int size > Stack<DataType, size > ::Stack () { maxSize=size; top=-1; elements=new DataType [maxSize]; // 建立栈空间 assert(elements!=NULL); // 假定未悬空,否则分配失败,结束}template <typename DataType , int size > void Stack<DataType,size> :: PrintStack(){ for(int i=0;i<=top;i++) cout<<elements[i]<<'\t'; cout<<endl;}template <typename DataType , int size > void Stack<DataType,size> :: Push(const DataType &data){ assert(!IsFull()); // 栈满则退出程序 elements[++top]=data; // 栈顶下标先加 1 ,元素再进栈}

6.1.2 类模板与数据结构—栈

Page 21: Unit 8— 第 六 章 模板

template <typename DataType , int size > DataType Stack <DataType,size> :: Pop(){ assert(!IsEmpty()); // 栈已空则不能退栈,退出程序 return elements[top--]; // 返回栈顶元素,同时栈顶下标 -1 }

template <typename DataType , int size > DataType Stack <DataType,size> :: GetElem(int i){ assert(i<=top&&i>=0); // 超出栈有效数据区,则退出程序 return elements[i]; // 返回指定元素, top 不变}

6.1.2 类模板与数据结构—栈

Page 22: Unit 8— 第 六 章 模板

测试:int main(){

int i,a[10]={0,1,2,3,4,5,6,7,8,9},b[10];Stack <int, 10>istack();for(i=0;i<10;i++) istack.Push(a[i]); // 压栈if(istack.IsFull()) cout<<" 栈满 "<<endl;istack.PrintStack();for(i=0;i<10;i++) b[i]=istack.Pop();if(istack.IsEmpty()) cout<<" 栈空 "<<endl;for(i=0;i<10;i++) cout<<b[i]<<'\t'; //注意先

进后出cout<<endl;istack.Pop(); // 弹出报错return 0;

}

6.1.2 类模板与数据结构—栈

Page 23: Unit 8— 第 六 章 模板

链队类模板设计:

Node 和 SLList 类模板设置:template <typename DataType> class SLListQueue; template <typename DataType> class Node {

(略) friend class SLListQueue <DataType>;

};

template <typename DataType> class SLList{(略) friend class SLListQueue<DataType>;

};

6.1.2 类模板与数据结构—队列

a0 a1 a2 … an-1…

front rear

head ……^

……

front

a0 a1 an

reartail

链队实现方式

Page 24: Unit 8— 第 六 章 模板

template <typename DataType> class SLListQueue{ SLList <DataType> LstQue; Node <DataType> *front,*rear;public: SLListQueue(){front=rear=LstQue.head->link;} // 空链队 ~SLListQueue(){} // 析构函数 bool IsEmpty(){ return Length()==0;} //队空否? void EnQue(const DataType &); // 进队 DataType DeQue(); // 出队 DataType GetFront(); // 查看队头数据 void MakeEmpty(); // 置空队列};

6.1.2 类模板与数据结构—队列

Page 25: Unit 8— 第 六 章 模板

template <typename DataType> DataType SLListQueue <DataType> :: DeQue(){ ……}template <typename DataType> DataType SLListQueue <DataType> :: GetFront(){ ……}template <typename DataType> void SLListQueue <DataType> :: MakeEmpty(){ ……}

6.1.2 类模板与数据结构—队列

链队类模板可以继承链表类模板吗?

继承的思想不适应类模板设计;各模板独立设计,但可以使用聚合概念!

Page 26: Unit 8— 第 六 章 模板

查找( search ):按关键字( key word ),在有序的数据集合(顺序表,升序或降序)中,寻找满足条件的数据。算法:对半查找(迭代、递归)

6.2 类模板与排序查找

Page 27: Unit 8— 第 六 章 模板

low

8 9 17

13

11

20

7 19

21

23

31

26

29

2 5 37

39

23查找

low mid

high

20

21

29

26

23

31

37

39

mid

highlow

20

21

23

mid

high23

low mid high

成功

图 6.3 查找成功例

以变量 low 和 high 为数据序列的首尾元素的下标,取mid= (low+ high)/2 ,如mid 位置的元素是所查找的,则结束。如果该元素大了,则取 low=mid +1 , high 不变,继续查找;如果该元素小了,则取 high=mid-1 , low 不变,继续查找。如果查到low>=high仍未找到,则失败,停止。

对半查找 :6.2.1 类模板与查找方法

Page 28: Unit 8— 第 六 章 模板

2 5 7 8 11

13

17

9 19

20

23

21

26

29

31

37

10查找 low

39

mid

high

2 5 7 8 11

13

17

9

low mid

high

11

13

17

9

low mid

high

9

low mid high

图 6.4 查找失败例

注意:( 1 )区间收缩过程中, low=mid+1 和high=mid-1非常重要,没有“+1”和“ -1” 时,可能数据存在却找不到。( 2 )对半查找递归算法与迭代算法。

6.2.1 类模板与查找方法

Page 29: Unit 8— 第 六 章 模板

【例 6.4】对半查找递归算法 作为有序表类模板成员函数:template <typename T,int size> int Orderedlist<T,size> :: Binarysearch (const T & x, const int low, const int high){ int mid=-1; if (low<=high) { mid=(low+high)/2; if (slist[mid]<x) mid = Binarysearch(x,mid+1,high);

// 中间点小于 X ,查找右区间,注意mid+1 else if(x<slist[mid]) mid=Binarysearch(x,low,mid-1);

// 中间点大于 X ,查找左区间,注意 mid-1 } return mid;// 找到返回下标 ; 未找到但结束了,返回 -1}

Page 30: Unit 8— 第 六 章 模板

有序表基本元素为类 Element 对象 :class Element{

int key; // 其他域省略public:

bool operator<(Element ele){return key<ele.key;}

void setkey(int k){key=k;} // 初始化元素的主关键字

void show(){cout<<key<<'\t';}}; // 重载了比较运算符 , 元素的比较实际是元素关键字的比较 template <typename T,int size>class Orderedlist{

int maxsize;int last;T slist[size];

public:Orderedlist(){last=-1;maxsize=size;}int Binarysearch(T & x,const int low,const

int high);bool Insert(T & elem,int i);void print(); // 无关成员函数省略

};

Page 31: Unit 8— 第 六 章 模板

【例 6.4】对半查找递归算法template <typename T,int size>bool Orderedlist<T,size>:: Insert(T & elem,int i){ int j ; if (i<0||i>last+1||last==maxsize-1) return false; //” 前驱后继”

else{ last++;for (j=last;j>i;j--) slist[j]=slist[j-1];slist[i]=elem;return true;}

}template <typename T,int size> void Orderedlist<T,size>:: print(){ int i; for(i=0;i<=last;i++){

slist[i].show(); //通用性不好,最好重载输出符”<<”

if(i%5==4) cout<<endl;} cout<<endl;}

Page 32: Unit 8— 第 六 章 模板

【例 6.4】对半查找递归算法int main(){

const int h=19;int i,k=37;Orderedlist<Element,100> ordlist;int

a[h]={67,61,59,53,47,43,41,37,31,29,23, 19,17,13,11,7,5,3,2}; //降序

Element n[h], elem;for(i=0;i<h;i++)

n[i].setkey(a[i]); // 初始化关键字for(i=0;i<h;i++)

ordlist.Insert(n[i],0); // 始终在 0 下标位置插入,建立升序顺序表ordlist.print();elem.setkey(k);

i=ordlist.Binarysearch(elem,0,h-1);cout<<" 整数 "<<k<<" 在表中位

置: "<<i<<endl;return 0;}

Page 33: Unit 8— 第 六 章 模板

【例 6.5】对半查找迭代算法template <typename T,int size > int Orderedlist<T,size>:: BinarySearch(const T & x) const{ int high=last, low=0, mid; // last 当前有序表最大下标 if ( last ==- 1 ) return -1; //避免空表出错 while (low<=high ) { mid = (low+high)/2; if ( x<slist[mid] ) high = mid-1; //左缩查找区间 else if ( slist[mid]<x ) low = mid+1; // 右缩查找区间 else return mid;// 找到,等于 mid } if ( slist[mid] != x ) mid = -1; // 未找到 return mid;}

Page 34: Unit 8— 第 六 章 模板

6.2.2 常用的排序法

排序( sorting ):按照数据元素的可排序数据项(关键字)的大小,排列成升序或降序的元素序列的过程。排序是查找的前提。

算法:插入排序(直接、对半);交换排序(冒泡排序);选择排序(直接选择排序)

Page 35: Unit 8— 第 六 章 模板

6.2.2 常用的排序法——直接插入排序 直接插入排序(升序)的思想 :当取得元素 s[i] ( i>0 )时 , 前面的元素s[0],s[1],…, s[i-1] 已经排好序 ,我们将 s[i] 的关键字与 s[i-1], s[i-2],… 的关键字顺序比较 , 找到首个比它小的 , 则 s[i] 插到该元素后。

i 0 1 2 3 4 5 6 temp

初始序列 [8] 6 7 9 4 5 2 6

1 [6 8] 7 9 4 5 2 7

2 [6 7 8] 9 4 5 2 9

3 [6 7 8 9] 4 5 2 4

4 [4 6 7 8 9] 5 2 5

5 [4 5 6 7 8 9] 2 2

6 [2 4 5 6 7 8 9]

将直接插入排序中的顺序查找改为对半查找——对半插入排序,较快!

Page 36: Unit 8— 第 六 章 模板

【例 6.6】升序直接插入排序算法 作 Orderedlist<T,size> 类成员函数, T 为数组元素类型template<typename T,int size>void Orderedlist<T,size>:: InsertSort(){

T temp;int i,j;for (i=1;i<=last;i++){

temp=slist[i];j=i;while (j>0&&temp<slist[j-1]){

slist[j]=slist[j-1];j--; // 查找与移动同时做

}slist[j]=temp;

}//稳定排序}

Page 37: Unit 8— 第 六 章 模板

有序表类 Orderedlist<T,size> 的基本元素为 Element 类对象( key改为 string ),略。void main(){

const int h=10; Element n[h];int i;Orderedlist<Element,100> ordlist;string

mslist[h]={"cat","book","car","zoo","fish","cab","dog","cap","fox","can"};for(i=0;i<h;i++) n[i].setkey(mslist[i]);for(i=0;i<h;i++)

ordlist.Insert(n[i],i); // 建立顺序表cout<<" 未排序表: "<<endl;ordlist.print();ordlist.InsertSort();cout<<" 已排序表: "<<endl;ordlist.print();

}

【例 6.6】升序直接插入排序算法

Page 38: Unit 8— 第 六 章 模板

6.2.2 常用的排序法——交换排序

图 6.6 从下往上冒泡排序

交换排序基本思想: 按关键字两两排序,如果发生逆序则交换之,直到所有的数据都排好序为止。

49 13 13 13 13 13

38 49 27 27 27 27

65 38 49 38 38 38

97 65 38 49 49 49

76 97 65 49’ 49’ 49’

13 76 97 65 65 65

27 27 76 97 76 76

49’ 49’ 49’ 76 97 97

冒泡排序:( 1 )首先从一列数据底部(下标 last )开始,相邻两数据进行比较,小的放上面,一趟下来,最小的数据冒到最上面;( 2 )缩小区域,按同样方法继续下一趟交换;( 3 )如果有一趟比较中没有发生交换,则已排好序。

Page 39: Unit 8— 第 六 章 模板

【例 6.8】冒泡排序算法template <typename T, int size> void Orderedlist<T,size>:: BubbleSort(){ bool noswap; int i, j; T temp; for ( i=0; i<last; i++ ) { // 最多做last趟 noswap=true; //“ 未交换”标志为真

for ( j=last; j>i; j--) { // 从下往上冒泡if ( slist[ j ] < slist[ j-1 ] ) {

temp = slist[ j ];slist[ j ] = slist[ j-1 ];slist[ j-1 ] = temp;noswap = false;

}}if ( noswap ) break; //本趟无交换,则终止

算法。 }}

Page 40: Unit 8— 第 六 章 模板

【例 6.8】冒泡排序算法student 类对象为数组的元素

class student{int id; //学号 (主关键字 )int age; //年龄string name; // 姓名char sex; // 性别string address; //家庭地址float eng, phy, math, electron; //英语 ,物理 ,数学和电子成绩

public:student(){}

student(int,string,char,int,string,float,float,float,float);bool operator<(student ele){return id<ele.id;} // 比较符重载void show(){ cout<<id<<'\t'<<name<<'\t'<<sex<<'\t' <<age <<'\t' <<address<<'\t'<<eng<<'\t'<<phy<<'\t'<<math<<'\t'<<electron<<endl;}

};

Page 41: Unit 8— 第 六 章 模板

【例 6.8】冒泡排序算法int main(){

const int h=4; int i;Orderedlist<student,100> ordlist;student n[h]={ 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;i<h;i++) ordlist.Insert(n[i],i); // 建立顺序表cout<<" 未排序表: "<<endl;ordlist.print();ordlist.BubbleSort();cout<<" 已排序表: "<<endl;ordlist.print();return 0;

}

Page 42: Unit 8— 第 六 章 模板

6.2.2 常用的排序法——直接选择排序

选择排序( Selection Sort ):每一趟从记录中选出关键字最小的元素,顺序放在已排好序的子序列前面,直到全部记录排序完成。直接选择排序( Straight Selection Sort )是最简单方法。[49 38 65 97 76 13 27 49’]

 13 [38 65 97 76 49 27 49’] 13 27 [65 97 76 49 38 49’] 13 27 38 [97 76 49 65 49’] 13 27 38 49 [76 97 65 49’] 13 27 38 49 49’ [97 65 76] 13 27 38 49 49’ 65 [97 76] 13 27 38 49 49’ 65 76 97

图 6.7 直接选择排序的过程

Page 43: Unit 8— 第 六 章 模板

【例 6.9】直接选择排序template<typename T,int size>void Orderedlist<T,size>:: SelectSort(){ int i, j, k; T temp; for(i=0;i<last;i++){

k=i; temp=slist[i];for(j=i+1;j<=last;j++) //课本 j=i 有误 ,多了

趟自己比 if(slist[j]<temp)

{ k=j; temp=slist[j]; }if ( k != i ){ temp=slist[i]; slist[i]=slist[k]; slist[k]=temp;}

}}

Page 44: Unit 8— 第 六 章 模板

6.2 类模板与排序查找

类模板与排序查找总结:模板的通用性得到了很好的体现。1. 关键技术是数组的数据元素说明为类,并重载小于运算

符,该运算符实际是将元素类对象的比较转化为类对象关键字的比较。

2. 通常还要重载 << 输出运算符,将元素类对象的输出转化为类对象关键字的输出。补充如下:

class Scholar{string NameAsKey;

public:friend ostream&

operator<<(ostream& out,const Scholar& s);//声明友元运算符”<<”

ostream& operator<<(ostream& out,const Scholar& s){

out<<s.NameAsKey;return out;

}// 输出运算符”<<” 重载

Page 45: Unit 8— 第 六 章 模板

Unit 8— 第六章 模板

6.1 模板与数据结构 6.4 模板与类参数

6.3 索引查找与指针数组

6.5 函数指针与指针识别(选读) 6.2 类模板与排序查找

Page 46: Unit 8— 第 六 章 模板

6.3 索引查找与指针数组 “指向数组的指针”与“指针数组”: 指向数组的指针:int Arry[10],* pslst= Array; pslst 是指针,指向了一维数组 Array 。指针数组:int * pA[10];pA 是指针数组,共有 10 个指针元素组成,分别是 pA[0],…,pA[9] 。

回顾查找:

查找元素关键字 6002806 :第一步,排序(插入、交换、选择);第二步,查找(对半)。

6002802

6002807

6002804

6002809

6002806

6002803

6002801

6002808

6002805

烦不烦?

Page 47: Unit 8— 第 六 章 模板

6.3 索引查找与指针数组

索引查找:要查找关键字 6002806 的元素,直接取得 *(pA+6) 或者pA[6] ,即可以访问到对应元素。无需应用排序和查找算法!索引查找的前提:待查找元素的关键字与指针数组下标存在明确映射关系 , 如“下 标 =f(key)” , 查 找 变 为 下 标 访 问pA[f(key)] 。散列( hash )查找 : 采用索引查找技术,速度最快。

pA[3] pA[4]pA[2] pA[5] pA[6] pA[8]pA[7]pA[1] pA[9]

6002802

6002807

6002804

6002809

6002806

6002803

6002801

6002808

6002805

6002802

6002807

6002804

6002809

6002806

6002803

6002801

6002808

6002805

Page 48: Unit 8— 第 六 章 模板

学号 姓名 性别 年龄 06002808

张伟 男 18

06002804

姚婕 女 17

06002802

王茜 女 18

06002807

朱明 女 18

06002809

沈俊 男 17

06002806

况钟 女 17

06002801

程毅 男 18

06002803

李文 男 19

06002805

胡凤 女 19

下标 012345678

图 6.8 用指针数组进行索引查找实例

6.3 索引查找与指针数组

指针数组下标

对象列表

Page 49: Unit 8— 第 六 章 模板

6.4 模板与类参数

函数模板的深入讨论—用法:函数模板作为类模板的成员函数;以类(或类模板)为类型参数的独立函数模板。

Page 50: Unit 8— 第 六 章 模板

6.4 模板与类参数

函数模板的深入讨论—用法:函数模板作为类模板的成员函数

例子:梯形法求积分的函数模板作为梯形积分法类模板的成员函数。

Page 51: Unit 8— 第 六 章 模板

【例 6.11】求积分的类模板 梯形法求积分是一种求函数定积分的近似方法。对函数 f(x) 将积分区间 [a,b] 分成 n 份,每一份看作一个近似梯形,函数在该区间的定积分就是所有近似梯形的面积和。积分步长为 step=(b-a)/n ,面积为: s = step*(f(x0)+f(x1))/2+step*(f(x1)+f(x2))/2+... +step*((f(xn-1)+f(xn))/2 = step*(f(x0)/2+f(x1)+f(x2)+...+f(xn-1)+f(xn)/2)

class F1 { public: double fun(double x){return (1+x+2*x*x);} };class F2 { public: double fun(double x){return (1+x+2*x*x+3*x*x*x);} };class F3 { public: double fun(double x){ return (1+x+2*x*x+3*x*x*x+4*x*x*x*x);} };

Page 52: Unit 8— 第 六 章 模板

【例 6.11】求积分的类模板 template<typename T>class Integer{ double a,b,step,result; int n; // 分区数量 T cf; //被积函数public: integer(double aa=0, double bb=0, int nn=100){ a=aa; b=bb; n=nn; integerate(); } void putlimits(double aa=0, double bb=0, int nn=100){ //修改上下限和分区数 a=aa; b=bb; n=nn; } void integerate(); void print(){cout<<" 定积分值为: "<<result<<endl;}};

Page 53: Unit 8— 第 六 章 模板

【例 6.11】求积分的类模板 template<typename T>void Integer<T>::integerate(){

step=(b-a)/n;result=(cf.fun(a)+cf.fun(b))/2;for (int i=1;i<n;i++)

result+=cf.fun(a+i*step);result*=step;

}

int main(){Integer<F1>

integer1(0.0,3.0,1000);integer1.print();Integer<F2>

integer2(0.0,3.0,1000);integer2.print();Integer<F3>

integer3(0.0,3.0,1000);integer3.print();return 0;

}

Page 54: Unit 8— 第 六 章 模板

6.4 模板与类参数

函数模板的深入讨论—用法:以类(类模板)为模板类型的独立函数模板

例子:独立的梯形法求积分函数模板(非成员函数),其形参为被积函数的类对象。

Page 55: Unit 8— 第 六 章 模板

【例 6.12】求积分的函数模板 template<typename T>double integer (T cf,float a, float b,int n){

double result,step;result=(cf.fun(a)+cf.fun(b))/2;step=(b-a)/n;for (int i=1;i<n;i++)

result+=cf.fun(a+i*step);result*=step;return result;

}int main(){

F1 f1; F2 f2; F3 f3;double fixint1, fixint2, fixint3; int n=1000;fixint1=integer(f1,0.0,3.0,n);//隐式类型指定 ,

仅对函数模板fixint2=integer(f2,0.0,3.0,n);fixint3=integer(f3,0.0,3.0,n);cout<<fixint1<<'\n'<<fixint2<<'\

n'<<fixint3<<'\n';return 0;

}

Page 56: Unit 8— 第 六 章 模板

6.4 模板与类参数

函数模板用法总结:作为类模板的成员函数,直接访问类模板的私有数据成员。通过在模板类型参数中重载函数和运算符,实现通用算法。作为独立的函数模板 (非成员函数 ) ,处理模板类(或普通类、普通数据)。即以类模板(或类对象、普通数据)为参数,借助模板类型参数中重载的函数或运算符实现通用算法。但调用参数(类)的接口函数间接访问其私有数据成员。

Page 57: Unit 8— 第 六 章 模板

第六章 模板与数据结构

完谢谢!