Upload
stacy
View
83
Download
4
Embed Size (px)
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
Citation preview
Unit 8— 第六章 模板
6.1 模板与数据结构 6.4 模板与类参数
6.3 索引查找与指针数组
6.5 函数指针与指针识别(选读) 6.2 类模板与排序查找
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); }; // 重载下标运算符 []
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;}
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 作了调整 !
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 顺序表类模板设计
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 顺序表类模板设计
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 顺序表类模板设计
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 顺序表类模板设计
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 顺序表类模板设计
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 顺序表类模板设计
单链表类模板设计
回忆结点类: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
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;}
链表类模板设计 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> *); // 删除指定结点};
链表类模板成员函数: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 类模板与数据结构—单链表
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 类模板与数据结构—单链表
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 类模板与数据结构—单链表
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 类模板与数据结构—单链表
测试:
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 )查找类中包含的其他类模板,将类名后加 < 模板参数名表 > ,便于随后的实例化。
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(); // 输出栈内所有数据};
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 类模板与数据结构—栈
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 类模板与数据结构—栈
测试: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 类模板与数据结构—栈
链队类模板设计:
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
链队实现方式
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 类模板与数据结构—队列
template <typename DataType> DataType SLListQueue <DataType> :: DeQue(){ ……}template <typename DataType> DataType SLListQueue <DataType> :: GetFront(){ ……}template <typename DataType> void SLListQueue <DataType> :: MakeEmpty(){ ……}
6.1.2 类模板与数据结构—队列
链队类模板可以继承链表类模板吗?
继承的思想不适应类模板设计;各模板独立设计,但可以使用聚合概念!
查找( search ):按关键字( key word ),在有序的数据集合(顺序表,升序或降序)中,寻找满足条件的数据。算法:对半查找(迭代、递归)
6.2 类模板与排序查找
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 类模板与查找方法
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 类模板与查找方法
【例 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}
有序表基本元素为类 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(); // 无关成员函数省略
};
【例 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;}
【例 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;}
【例 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;}
6.2.2 常用的排序法
排序( sorting ):按照数据元素的可排序数据项(关键字)的大小,排列成升序或降序的元素序列的过程。排序是查找的前提。
算法:插入排序(直接、对半);交换排序(冒泡排序);选择排序(直接选择排序)
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]
将直接插入排序中的顺序查找改为对半查找——对半插入排序,较快!
【例 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;
}//稳定排序}
有序表类 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】升序直接插入排序算法
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 )如果有一趟比较中没有发生交换,则已排好序。
【例 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; //本趟无交换,则终止
算法。 }}
【例 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;}
};
【例 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;
}
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 直接选择排序的过程
【例 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;}
}}
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;
}// 输出运算符”<<” 重载
Unit 8— 第六章 模板
6.1 模板与数据结构 6.4 模板与类参数
6.3 索引查找与指针数组
6.5 函数指针与指针识别(选读) 6.2 类模板与排序查找
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
烦不烦?
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
学号 姓名 性别 年龄 06002808
张伟 男 18
06002804
姚婕 女 17
06002802
王茜 女 18
06002807
朱明 女 18
06002809
沈俊 男 17
06002806
况钟 女 17
06002801
程毅 男 18
06002803
李文 男 19
06002805
胡凤 女 19
下标 012345678
图 6.8 用指针数组进行索引查找实例
6.3 索引查找与指针数组
指针数组下标
对象列表
6.4 模板与类参数
函数模板的深入讨论—用法:函数模板作为类模板的成员函数;以类(或类模板)为类型参数的独立函数模板。
6.4 模板与类参数
函数模板的深入讨论—用法:函数模板作为类模板的成员函数
例子:梯形法求积分的函数模板作为梯形积分法类模板的成员函数。
【例 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);} };
【例 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;}};
【例 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;
}
6.4 模板与类参数
函数模板的深入讨论—用法:以类(类模板)为模板类型的独立函数模板
例子:独立的梯形法求积分函数模板(非成员函数),其形参为被积函数的类对象。
【例 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;
}
6.4 模板与类参数
函数模板用法总结:作为类模板的成员函数,直接访问类模板的私有数据成员。通过在模板类型参数中重载函数和运算符,实现通用算法。作为独立的函数模板 (非成员函数 ) ,处理模板类(或普通类、普通数据)。即以类模板(或类对象、普通数据)为参数,借助模板类型参数中重载的函数或运算符实现通用算法。但调用参数(类)的接口函数间接访问其私有数据成员。
第六章 模板与数据结构
完谢谢!