2.1线性表的逻辑结构
线性表的定义:
是零个或多个具有相同类型的数据元素的有限序列。
数据元素的个数定义为线性表的长度。长度等于零时称为空表。
一个非空表通常记为:L(a1,a2,。。。。。。an)
其中的ai(1<=i<=n)称为数据元素
线性表的抽象数据类型定义
ADT List
Data
线性表中的数据元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
InitList
前置条件:线性表不存在
输入:无
功能:线性表的初始化
输出:无
后置条件:一个空的线性表
DestroyList
前置条件:线性表已存在
输入:无
功能:销毁线性表
输出:无
后置条件:释放线性表所占用的存储空间
Length
前置条件:线性表已存在
输入:无
功能:求线性表的长度
输出: 线性表中数据元素的个数
后置条件:线性表不变
Get
前置条件:线性表已存在
输入:元素的序号 i
功能:在线性表中取序号为 i 的数据元素
输出:如果序号合法,返回序号为 i 的元素值,否则抛出异常
后置条件:线性表不变
Locate
前置条件:线性表已存在
输入:数据元素 x
功能:在线性表中查找值等于 x 的元素
输出:如果查找成功,返回元素 x 在表中的序号,否则返回 0
后置条件:线性表不变
Insert
前置条件:线性表已存在
输入:插入位置 i ;待插元素 x
功能:在线性表的第 i 个位置处插入一个新元素 x
输出:若插入不成功,抛出异常
后置条件:若插入成功,表中增加了一个新元素
Delete
前置条件:线性表已存在
输入:删除位置 i
功能:删除线性表中的第 i 个元素
输出:若删除成功,返回被删元素,否则抛出异常
删除成功,表中减少了一个元素
Empty
前置条件:线性表已存在
输入:无
功能:判断线性表是否为空表
输出:若是空表,返回 1 ,否则返回 0
后置条件:线性表不变
PrintList
前置条件:线性表已存在
输入:无
功能:按位置的先后次序依次输出线性表中的元素
输出:线性表的各个数据元素
后置条件:线性表不变
2.2 线性表的顺序存储结构及其实现
2.2.1 线性表的顺序结构-顺序表
特点:线性表的顺序存储是指用一组地址连续的存储的存储单元一次存储线性表中的各个元素
作用:线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系
顺序存储的实现:一维数组存储顺序表中的数据
顺序表的实现:
const int Maxsize=100;
template
class SeqList{
private:
T data[MaxSize]; // 存放数据元素的数组
int length; // 线性表的长度
public:
SeqList ( ) ;// 无参构造函数
SeqList ( T a[ ], int n ) ; // 有参构造函数
~SeqList( ) { } // 析构函数为空
int Length ( ) {return length;} // 求线性表的长度
T Get ( int i ); // 按位查找,取线性表的第 i 个元素
int Locate ( T x ) ; // 按值查找,求线性表中值为 x 的元素序号
void Insert ( int i, T x ) ; // 在线性表中第 i 个位置插入值为 x 的元素
T Delete ( int i ) ; // 删除线性表的第 i 个元素
void PrintList ( ) ; // 遍历线性表,按序号依次输出各元素
};
构造函数:
定义了两个构造函数:
无参构造函数(构造一个空的顺序表)
SeqList ( ) {length=0;}
构造一个非空的顺序表
SeqList ( T a[ ], int n ) ; // 有参构造函数
有参构造函数的实现:
将长度为n的一维数组中的元素依次传入到data中。
有参构造函数
template
SeqList
{
if (n>MaxSize) throw "参数非法";
for (int i=0; i data[i]=a[i]; length=n; } 顺序表的插入算法: 1 如果顺序表已满,抛出上溢异常 2 如果元素插入位置不存在,抛出位置异常 3 将最后一个元素至第i个元素(i为插入位置)向后移动一个位置 4 将元素插入到i位置 5 将顺序表的长度增1 template void SeqList int j; if (length>=MaxSize) throw "上溢"; if (i<1 || i>length+1) throw "位置"; for (j=length; j>=i; j--) data[j]=data[j-1]; data[i-1]=x; length++; } 顺序表的删除算法: 1 如果顺序表已空,抛出下溢异常 2 如果元素删除位置不存在,抛出位置异常 3 取出被删除的元素 4 将下标为i,i+1…n-1的元素一次移到i-1,i,…n-2的位置 5 将顺序表的长度减1,返回被删除的元素 template T SeqList int j; T x; if (length==0) throw "下溢"; if (i<1 || i>length) throw "位置"; x=data[i-1]; for (j=i; j data[j-1]=data[j]; length--; return x; } 顺序表中的查找操作 template T SeqList { if (i<1 && i>length) throw "查找位置非法"; else return data[i-1]; } 按位查找算法的时间复杂度为 O ( 1 ) 。 按值查找 template int SeqList for (int i=0; i if (data[i]==x) return i+1 ; //下标为i的元素等于x,返回其序号i+1 return 0; //退出循环,说明查找失败 } 2.3.1 单链表 带头节点的单链表 头结点:如果链表有头节点,则链式结构中的第一个节点称为头结点:其数据域可以存储一些附加信息,如链表长度;其指针域指向链表中的第一个节点。 单链表的实现: template class LinkList { public: LinkList ( ) {first=new Node 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 }; 头插法: template LinkList first=new Node first->next=NULL; Node for (int i=0; i s=new Node s->data=a[i]; //为每个数组元素建立一个结点 s->next=first->next; first->next=s; } } 尾插法: template LinkList Node first=new Node r=first; for (int i=0; i s=new Node s->data=a[i]; //为每个数组元素建立一个结点 r->next=s; r=s; //插入到终端结点之后 } r->next=NULL; //单链表建立完毕,将终端结点的指针域置空 } 单链表的遍历 template LinkList { Node p=first->next; while(p) { cout< p=p->next; } } 不带头结点的单链表的构造 头插法: 头插法: { first=NULL; for(int i=0;i s=new node s->data=a[i]; s->next=first; first=s; } } 尾插法: node head=NULL; if(n<=0)return; s=new node s->data=a[0]; s->next=head; head=s; r=head; 尾插法: node head=NULL; if(n<=0)return; s=new node s->data=a[0]; s->next=head; head=s; r=head; 单链表中按位置查找: 查找算法: 1 工作指针P初始化,计数器初始化 2 执行下列操作,直到p为空或指向第i个节点 2.1 工作指针后移 2.2 计数器增1 3 若p为空,则第i个元素不存在,抛出位置异常;否则查找成功,返回节点p的数据元素 template T LinkList Node p=first->next; j=1; //或p=first; j=0; while (p && j
p=p->next; //工作指针p后移 j++; } if (!p) throw "位置"; else return p->data; } 单链表的插入: 1 工作指针p初始化,计数器初始化 2 查找第i-1个节点,并使工作指针p指向该节点 3 若查找不成功(P==NULL),说明位置错误,抛出位置异常,否则 3.1 生成一个元素值为x的新节点s 3.2 将s插入到p之后 template void LinkList Node p=first ; j=0; //工作指针p初始化 while (p && j p=p->next; //工作指针p后移 j++; } if (!p) throw "位置"; else { Node s=new Node s->data=x; //向内存申请一个结点s,其数据域为x s->next=p->next; //将结点s插入到结点p之后 p->next=s; } } 不带头结点的单链表中插入节点 Insert(int i, T x){ Node if(i<=0) throw “位置非法”; if (i==1 ){ s=new Node p=first ; j=1; //工作指针p初始化 while (p && j p=p->next; //工作指针p后移 j++; } if (!p) throw "位置"; else { Node s=new Node s->data=x; //向内存申请一个结点s,其数据域为x s->next=p->next; //将结点s插入到结点p之后 p->next=s; } } 删除算法分析 1工作指针p初始化;累加器j清零; 2.查找第i-1个结点并使得工作指针p指向该结点 3.若p不存在或p的后继结点不存在,抛出位置异常 否则3.1 暂存被删结点p的后继结点从链表上摘下 3.2 释放被删结点 3.3返回被删元素值 template T LinkList Node p=first ; j=0; //工作指针p初始化 while (p && j p=p->next; j++; } if (!p || !p->next) throw "位置"; //结点p不存在或结点p的后继结点不存在 else { Node q=p->next; x=q->data; //暂存被删结点 p->next=q->next; //摘链 delete q; return x; } } 2.5线性表的其他存储方法 2.5.1 循环链表 将单链表或者双链表的头尾结点链接起来,就是一个循环链表。 不增加额外存储花销,却给不少操作带来了方便 从循环表中任一结点出发,都能访问到表中其他结点。 特点: 首尾相接的链表。 可以从任一节点出发,访问链表中的所有节点。 判断循环链表中尾结点的特点: q->next==first 循环链表的定义: template struct Node { T data; Node }; template class CycleLinkList{ public: CycleLinkList( ); CycleLinkList(T a[ ], int n); CycleLinkList(T a[ ], int n,int i); ~CycleLinkList(); int Length(); T Get(int i); void Insert(int i, T x); T Delete(int i); void PrintList( ); private: Node }; 尾插法构造循环链表 template CycleLinkList first=new Node Node r=first; //尾指针初始化 for (int i=0; i s=new Node s->data=a[i]; r->next=s; r=s; } r->next=first; //单链表建立完毕,将终端结点的指针域指向头结点 } 头插法构造循环链表 template CycleLinkList { first=new Node first->next=first; Node for (int i=1; i { s=new Node s->data=a[i]; //为每个数组元素建立一个结点 s->next=first->next; first->next=s; } } 2.5.2双链表 特点: 由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。设指针p指向双链表中某一结点,则有下式成立: p->llink->rlink = p = p->rlink->llink 双链表的实现: template class DoubleLink { private: Node public: DoubleLink() ; ~DoubleLink(); void Append(T data); void Display(); void Insert(int locate , T data); T Get(int locate); T Delete(int locate); }; 头插: template void DoubleLink Node s=new Node s->data=data; s->rlink=head->rlink; head->rlink=s; s->llink=head; if (s->rlink) s->rlink->llink=s; return; } 遍历: template void DoubleLink Node p=head->rlink; while(p) { cout< p=p->rlink; } cout< return; }