单链表类型模板
单链表类模板,本例作为一个头文件。单链表的结点采用类,与结点有关的基本操作都作为结点类的成员函数。对链表整体的操作则作为链表类的成员函数,包括清空链表、查找数据、计算单链表长度、打印链表数据、向前生成链表、向后生成链表、按升序生成链表等等。
“Ex7_5.h”
#include<iostream>
using namespace std;
//首先看结点组织,采用结点类,凡与结点数据和指针操作有关函数作为成员函数
template<typename T>class List; //前向引用声明
template<typename T>class Node
{
T info; //数据域
Node<T> *link; //指针域,注意结点类格式,尖括号中是参数名表,类模板实例化为类
public:
Node(); //生成头结点的构造函数
Node(const T & data); //生成一般结点的构造函数
void InsertAfter(Node<T>* P); //在当前结点后插入一个结点
Node<T>* RemoveAfter(); //删除当前结点的后继结点,返回该结点备用
friend class List<T>;
//以List为友元类,List可直接访问Node的私有成员,与结构一样方便,但更安全
};
template <typename T> Node<T>::Node(){link=NULL;}
template <typename T> Node<T>::Node(const T & data)
{
info=data;
link=NULL;
}
template<typename T>void Node<T>::InsertAfter(Node<T>* p)
{
p->link=link;
link=p;
}
template<typename T>Node<T>* Node<T>::RemoveAfter()
{
Node<T>* tempP=link;
if(link==NULL) tempP=NULL; //已在链尾,后面无结点
else link=tempP->link;
return tempP;
}
//再定义链表类,选择常用操作:包括建立有序链表、搜索遍历、插入、删除、取数据等
template<typename T>class List
{
Node<T> *head,*tail; //链表头指针和尾指针
public:
List(); //构造函数,生成头结点(空链表)
~List(); //析构函数
void MakeEmpty(); //清空一个链表,只余表头结点
Node<T>* Find(T data); //搜索数据域与data相同的结点,返回该结点的地址
int Length(); //计算单链表长度
void PrintList(); //打印链表的数据域
void InsertFront(Node<T>* p); //可用来向前生成链表,在表头插入一个结点
void InsertRear(Node<T>* p); //可用来向后生成链表,在表尾添加一个结点
void InsertOrder(Node<T> *p); //按升序生成链表
Node<T>*CreatNode(T data); //创建一个结点(孤立结点)
Node<T>*DeleteNode(Node<T>* p); //删除指定结点
};
template<typename T>List<T>::List()
{
head=tail=new Node<T>();
}
template<typename T>List<T>::~List()
{
MakeEmpty();
delete head;
}
template<typename T>void List<T>::MakeEmpty()
{
Node<T> *tempP;
while(head->link!=NULL)
{
tempP=head->link;
head->link=tempP->link; //把头结点后的第一个节点从链中脱离
delete tempP; //删除(释放)脱离下来的结点
}
tail=head; //表头指针与表尾指针均指向表头结点,表示空链
}
template<typename T> Node<T>* List<T>::Find(T data)
{
Node<T> *tempP=head->link;
while(tempP!=NULL&&tempP->info!=data) tempP=tempP->link;
return tempP; //搜索成功返回该结点地址,不成功返回NULL
}
template<typename T>int List<T>::Length()
{
Node<T>* tempP=head->link;
int count=0;
while(tempP!=NULL)
{
tempP=tempP->link;
count++;
}
return count;
}
template<typename T>void List<T>::PrintList()
{
Node<T>* tempP=head->link;
while(tempP!=NULL)
{
cout<<tempP->info<<'\t';
tempP=tempP->link;
}
cout<<endl;
}
template<typename T>void List<T>::InsertFront(Node<T> *p)
{ //链头插入
p->link=head->link;
head->link=p;
if(tail==head) tail=p;
}
template<typename T>void List<T>::InsertRear(Node<T> *p)
{ //链尾插入
p->link=tail->link;
tail->link=p;
tail=p;
}
template<typename T>void List<T>::InsertOrder(Node<T> *p)
{ //升序插入
Node<T> *tempP=head->link,*tempQ=head; //tempQ指向tempP前面的一个节点
while(tempP!=NULL)
{
if(p->info<tempP->info)break; //找第一个比插入结点大的结点,由tempP指向
tempQ=tempP;
tempP=tempP->link;
}
tempQ->InsertAfter(p); //插在tempP指向结点之前,tempQ之后
if(tail==tempQ) tail=tempQ->link;
}
template<typename T>Node<T>* List<T>::CreatNode(T data)
{//建立新节点
Node<T>*tempP=new Node<T>(data);
return tempP;
}
template<typename T>Node<T>* List<T>::DeleteNode(Node<T>* p)
{
Node<T>* tempP=head;
while(tempP->link!=NULL&&tempP->link!=p) tempP=tempP->link;
if(tempP->link==tail) tail=tempP;
return tempP->RemoveAfter(); //本函数所用方法可省一个工作指针,与InsertOrder比较
}
在本例中使用了友元类,这使得List类中的函数全部可以直接访问Node类中的私有数据,而不必使用公有函数NextNode()和Getinfo()。
下面用整型数代替模板中的类型T,来完成一些链表的基本操作。
由键盘输入16个整数,以这些整数作为结点数据,生成两个链表,一个向前生成,一个向后生成,输出两个表。然后给出一个整数在一个链表中查找,找到后删除它,再输出该表。清空该表,再按升序生成链表并输出。
#include "Ex7_5.h" //单链表类模板定义文件
int main()
{
Node<int> * P1;
List<int> list1,list2;
int a[16],i,j;
cout<<"请输入16个整数"<<endl;
for(i=0;i<16;i++)cin>>a[i]; //随机输入16个整数
for(i=0;i<16;i++)
{
P1=list1.CreatNode(a[i]);
list1.InsertFront(P1); //向前生成list1
P1=list2.CreatNode(a[i]);
list2.InsertRear(P1); //向后生成list2
}
list1.PrintList();
cout<<"list1长度:"<<list1.Length()<<endl;
list2.PrintList();
cout<<"请输入一个要求删除的整数"<<endl;
cin>>j;
P1=list1.Find(j);
if(P1!=NULL)
{
P1=list1.DeleteNode(P1);
delete P1;
list1.PrintList();
cout<<"list1长度:"<<list1.Length()<<endl;
}
else cout<<"未找到"<<endl;
list1.MakeEmpty(); //清空list1
for(i=0;i<16;i++)
{
P1=list1.CreatNode(a[i]);
list1.InsertOrder(P1); //升序创建list1
}
list1.PrintList();
return 0;
}
在本例中程序只需调用类模板中的成员函数就可以完成所有链表操作。
以学生类作为链表的数据类,完成学生档案的管理。
#include<string>
using namespace std;
#include "Ex7_6.h" //单链表类模板定义文件
class student
{
int id ; //学号
string name; // 姓名
char sex; // 性别
int age; // 年龄
string address; //家庭地址
float eng, phy, math, electron; //英语,物理,数学和电子学成绩
public:
student(int=0,string="",char='\0',int=0,string="",float=0.0,float=0.0,float=0.0,float=0.0);
bool operator<(student ele){return id<ele.id;}
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;
}
};
student::student(int i,string n,char s,int a,string add,float en,float ph,float ma,float ele)
{
id=i; //学号
name=n; // 姓名
sex=s; // 性别
age=a; // 年龄
address=add; //家庭地址
eng=en; phy=ph; math=ma, electron=ele; //英语,物理,数学和电子学成绩
}
int main()
{
const int h=4;
int i,j;
Node<student> * P1;
List<student> list1,list2;
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++)
{
P1=list1.CreatNode(n[i]);
list1.InsertFront(P1); //向前生成list1
P1=list2.CreatNode(n[i]);
list2.InsertRear(P1); //向后生成list2
}
list1.PrintList();
cout<<"list1长度:"<<list1.Length()<<endl;
list2.PrintList();
cout<<"请输入一个要求删除的学生学号"<<endl;
cin>>j;
P1=list1.Find(j); //学号由构造函数转换为学生类
if(P1!=NULL)
{
P1=list1.DeleteNode(P1);
delete P1;
list1.PrintList();
cout<<"list1长度:"<<list1.Length()<<endl;
}
else cout<<"未找到"<<endl;
list1.MakeEmpty(); //清空list1
for(i=0;i<h;i++)
{
P1=list1.CreatNode(n[i]);
list1.InsertOrder(P1); //升序创建list1
}
list1.PrintList();
return 0;
}
注意,链表类定义中输出函数PrintList()改写为:
template<typename T>void List<T>::PrintList()
{
Node<T>* tempP=head->link;
while(tempP!=NULL)
{
tempP->info.show();
tempP=tempP->link;
}
cout<<endl;
}