线性表的链式储存结构的实现主要有单链表,双向链表,循环链表等
,它可以根据结点需要来获取一个空间,不会造成空间浪费
1. 单链表
结点的定义
template <typename T>
struct Node
{
T data;
Node<T> *next; //此处<T>也可以省略
};
带头结点的单链表:
头结点的数据域可以用来储存一些附加信息,指针域用来存储链表中第一个结点的地址
单链表的实现:
template <class T>
class LinkList {
public:
LinkList ( ) {first=new Node<T>; first -> next= NULL ;}//构造带头结点的空单链表
LinkList ( T a[ ], int n ) ;
~LinkList ( ) ;
int Length ( ) ;
T Get ( int i ) ;
int Locate ( T x ) ;
void Insert ( int i, T x ) ;
T Delete ( int i ) ;
void PrintList ( ) ;
private:
Node<T> *first; // 单链表的头指针 , <T>可以省略
};
单链表的带参构造函数
头插法
template <class T>
LinkList<T>:: LinkList(T a[ ], int n) {
first=new Node<T>; //生成头结点
first->next=NULL;//构造空的单链表
Node<T> *s;
for (int i=0; i<n; i++){
s=new Node<T>;
s->data=a[i]; //为每个数组元素建立一个结点
s->next=first->next;//将s插入到头结点后面
first->next=s;//修改头结点的指针域
}
}
尾插法
template <class T>
LinkList<T>:: LinkList(T a[ ], int n) {
Node<T> *r,*s; //s尾指针
first=new Node<T>; //生成头结点
r=first;
for (int i=0; i<n; i++) {
s=new Node<T>;
s->data=a[i]; //为每个数组元素建立一个结点
r->next=s; r=s; //插入到终端结点之后,修改终端结点
}
r->next=NULL; //单链表建立完毕,将终端结点的指针域置空
}
尾插法并不用一直保持终端结点指针域为空,只需在链表构造完成后将它置空
单链表的遍历
template <class T>
LinkList<T>:: PrintList()
{
Node<T> *p;
p=first->next;
while(p)
{
cout<<p->data;
p=p->next;
}
}
补充:
不带头结点的单链表的构造
头插法:
template <class T>
LinkList<T>:: LinkList(T a[ ], int n){
first=NULL;
for(int i=0;i<n;i++) {
s=new node<T>;
s->data=a[i];
s->next=first;
first=s;
}
}
尾插法:
template <class T>
LinkList<T>:: LinkList(T a[ ], int n){
node<T> *r;
first=NULL;
if(n<=0)return;
s=new node<T>;
s->data=a[0];
s->next=first;
first=s;
r=first;
for(int i=1;i<n;i++) {
s=new node<T>;
s->data=a[i];
r->next=s;
r=s;
}
}
单链表中按位置查找
template <class T>
T LinkList<T>::Get(int i) {
Node<T> *p; int j;
p=first->next; j=1; //或p=first; j=0;
while (p && j<i) {
p=p->next; //工作指针p后移
j++;
}
if (!p) throw "位置";
else return p->data;
}
单链表的插入操作(按位置进行插入)
template <class T>
void LinkList<T>::Insert(int i, T x){
Node<T> *p; int j;
p=first ; j=0; //工作指针p初始化
while (p && j<i-1) {
p=p->next; //工作指针p后移
j++;
}
if (!p) throw "位置";
else {
Node<T> *s;
s=new Node<T>;
s->data=x; //向内存申请一个结点s,其数据域为x
s->next=p->next; //将结点s插入到结点p之后
p->next=s;
}
}
补充
不带头结点的单链表中插入结点
Insert(int i, T x){
Node<T> *p; int j;
if(i<=0) throw “位置非法”;
if (i==1 ){ s=new Node<T>;s->next=head;head=s;return}
p=first ; j=1; //工作指针p初始化
while (p && j<i-1) {
p=p->next; //工作指针p后移
j++;
}
if (!p) throw "位置";
else {
Node<T> *s;
s=new Node<T>;
s->data=x; //向内存申请一个结点s,其数据域为x
s->next=p->next; //将结点s插入到结点p之后
p->next=s;
}
}
单链表中节点的删除(删除编号是i的结点)
template <class T>
T LinkList<T>::Delete(int i){
Node<T> *p; int j;
p=first ; j=0; //工作指针p初始化
while (p && j<i-1) { //查找第i-1个结点
p=p->next;
j++;
}
if (!p || !p->next) throw "位置"; //结点p不存在或结点p的后继结点不存在
else {
Node<T> *q; T x;
q=p->next; x=q->data; //暂存被删结点
p->next=q->next; //摘链
delete q;
return x;
}
}
析构函数
template <class T>
LinkList<T>:: ~LinkList()
{
Node<T> *q;
while (first)
{
q=first->next;
delete first;
first=q;
}
}
2.循环链表
循环链表是一种特殊的单链表
将单链表或者双链表的头尾结点链接起来,就是一个循环链表。
特点:
首尾相接的链表。
可以从任一节点出发,访问链表中的所有节点。
判断循环链表中尾结点的特点:
q->next==first
空表的构造
template <class T>
CycleLinkList<T>:: CycleLinkList( )
{
first=new Node<T>; first->next=first;//首尾相连
}
构造非空的时候头插法就从循环链表空表开始,与单链表一模一样;尾插法就将单链表建立完毕,将终端结点的指针域置空
将非循环的单链表改造成循环的单链表
p=first;
while(p->next)
{
p=p->next;
}
p->next=first
3.双链表
双链表的结点结构
template <class T>
struct DNode{
T data;
DNode<T> *llink;
DNode <T>*rlink;
};
双向链表P之后插入结点
q->rlink=p->rlink;
P->rlink=q;
q->llink=p;
if(q->rlink)
q->rlink->llink=q;//p存在后继结点
删除操作
p->llink->rlink=p->rlink;
if(p->rlink)
p->rlink->llink=p->rlink;//p存在后继结点
delete(p);
双向链表的构造-空表的构造
template <class T>
DoubleLink <T>::DoubleLink(){
head=new Node<T>;
head->rlink=NULL;
head->llink=NULL;
}
析构函数
template <class T>
DoubleLink<T>::~DoubleLink(){
Node<T> *p,*q;
p=head;
while(p)
{
q=p->rlink;
delete p;
p=q;
}
}
总结
线性表包括顺序表和链式表,各有各的优点,顺序表不需要多余的空间来表示结点之间的关系,可以任意取其中的元素;链式表插入,删除操作简单;
结论:
1.若线性表的操作主要是进行查找,很少做插入和删除时,宜采用顺序表做存储结构。
2.对于频繁进行插入和删除的线性表, 宜采用链表做存储结构。
3.当线性表的长度变化不大, 易于事先确定其大小时,为了节约存储空间,宜采用顺序表作为存储结构。