本章的基本内容是:
Ø 线性表的逻辑结构
Ø 线性表的顺序存储及实现
Ø 线性表的链接存储及实现
Ø 顺序表和单链表的比较
Ø 线性表的其他存储及实现
线性表
线性表的定义
线性表:简称表,是n(n≥0)个具有相同类型的数据元素的有限序列。
线性表的长度:线性表中数据元素的个数。
空表:长度等于零的线性表,记为:L=( )。
非空表记为:L=(a1, a2, …, ai-1, ai , …, an)
其中,ai(1≤i≤n)称为数据元素;
下角标i 表示该元素在线性表中的位置或序号。
线性表的特性
1.有限性:线性表中数据元素的个数是有穷的。
2.相同性:线性表中数据元素的类型是同一的。
3.顺序性:线性表中相邻的数据元素ai-1和ai之间存在序偶关系(ai-1,ai),即ai-1是ai的前驱, ai是ai-1的后继;a1 无前驱,an无后继,其它每个元素有且仅有一个前驱和一个后继。
线性表的抽象数据类型定义
DestroyList
前置条件:表已存在
输入:无
功能:销毁表
输出:无
后置条件:释放表所占用的存储空间
Length
前置条件:表已存在
输入:无
功能:求表的长度
输出:表中数据元素的个数
后置条件:表不变
线性表的抽象数据类型定义
ADT List
Data
线性表中的数据元素具有相同类型,
相邻元素具有前驱和后继关系
Operation
InitList
前置条件:表不存在
输入:无
功能:表的初始化
输出:无
后置条件:建一个空表
线性表的抽象数据类型定义
Get
前置条件:表已存在
输入:元素的序号i
功能:在表中取序号为i的数据元素
输出:若i合法,返回序号为i的元素值,否则抛出异常
后置条件:表不变
Locate
前置条件:表已存在
输入:数据元素x
功能:在线性表中查找值等于x的元素
输出:若查找成功,返回x在表中的序号,否则返回0
后置条件:表不变
Insert
前置条件:表已存在
输入:插入i;待插x
功能:在表的第i个位置处插入一个新元素x
输出:若插入不成功,抛出异常
后置条件:若插入成功,表中增加一个新元素
Delete
前置条件:表已存在
输入:删除位置i
功能:删除表中的第i个元素
输出:若删除成功,返回被删元素,否则抛出异常
后置条件:若删除成功,表中
Empty
前置条件:表已存在
输入:无
功能:判断表是否为空
输出:若是空表,返回1,否则返回0
后置条件:表不变
ADT
进一步说明:
(1)线性表的基本操作根据实际应用是而定;
(2)复杂的操作可以通过基本操作的组合来实现;
(3)对不同的应用,操作的接口可能不同。
存储结构和存取结构
存储结构是数据及其逻辑结构在计算机中的表示;
存取结构是在一个数据结构上对查找操作的时间性能的一种描述。
“顺序表是一种随机存取的存储结构”的含义为:在顺序表这种存储结构上进行的查找操作,其时间性能为O(1)。
顺序表类的声明
const int MaxSize=100;
template <class T> //模板类
class SeqList
{
public:
SeqList( ) ; //构造函数
SeqList(T a[ ], int n);
~SeqList( ) ; //析构函数
int Length( );
T Get(int i);
int Locate(T x );
void Insert(int i, T x);
T Delete(int i);
private:
T data[MaxSize];
int length;
};
顺序表的实现——有参构造函数
template <class T>
SeqList::SeqList(T a[ ], int n)
{
if (n>MaxSize) throw "参数非法";
for (i=0; i<n; i+ +)
data[i]=a[i];
length=n;
}
顺序表的实现——插入
算法描述——伪代码
1. 如果表满了,则抛出上溢异常;
2. 如果元素的插入位置不合理,则抛出位置异常;
3. 将最后一个元素至第i个元素分别向后移动一个位置;
4. 将元素x填入位置i处;
5. 表长加1;
算法描述——C++描述
template <class T>
void SeqList::Insert(int i, T x)
{
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++;
}
时间性能分析
最好情况(i=n+1):
基本语句执行0次,时间复杂度为O(1)。
最坏情况(i=1):
基本语句执行n+1次,时间复杂度为O(n)。
平均情况(1≤i≤n+1):
时间复杂度为O(n)。
顺序表的实现——按值查找
template <class T>
int SeqList::Locate(T x)
{
for (i=0; i<length; i++)
if (data[i]==x) return i+1;
return 0;
}
顺序表的优缺点
Ø 顺序表的优点:
⑵ 无需为表示表中元素之间的逻辑关系而增加额外的存储空间;
⑵随机存取:可以快速地存取表中任一位置的元素。
Ø 顺序表的缺点:
⑴插入和删除操作需要移动大量元素;
⑵表的容量难以确定,表的容量难以扩充;
⑶ 造成存储空间的碎片。
单链表
存储特点:
1. 逻辑次序和物理次序
不一定相同。
2.元素之间的逻辑关系
用指针表示。
单链表类的声明
template <class T>
class LinkList
{
public:
LinkList( );
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;
};
单链表的实现——按位查找
算法描述——伪代码
1. 工作指针p初始化; 累加器j初始化;
2. 循环直到p为空或p指向第i个结点
2.1 工作指针p后移;
2.2 累加器j加1;
3. 若p为空,则第i个元素不存在,抛出查找位置异常;否则查找成功,返回结点p的数据元素;
算法描述——C++描述
template <class T>
T LinkList::Get(int i)
{
p=first->next; j=1;
while (p &&j<i)
{
p=p->next;
j++;
}
if (!p) throw "位置";
else returnp->data;
}
单链表的实现———插入
算法描述——伪代码
1. 工作指针p初始化;累加器j清零;
2. 查找第i-1个结点并使工作指针p指向该结点;
3. 若查找不成功,说明插入位置不合理,抛出插入位置异常;否则,
3.1 生成一个元素值为x的新结点s;
3.2 将新结点s插入到结点p之后;
单链表的实现———删除
算法描述——伪代码
1.工作指针p初始化;累加器j清零;
2. 查找第i-1个结点并使工作指针p指向该结点;
3. 若p不存在或p不存在后继结点,则抛出位置异常;
否则,
3.1 暂存被删结点和被删元素值;
3.2 摘链,将结点p的后继结点从链表上摘下;
3.3 释放被删结点;
3.4 返回被删元素值;
算法描述——C++描述
template <class T>
T LinkList::Delete(int i)
{
p=first ; j=0;
while (p &&j<i-1)
{
p=p->next;
j++;
}
if (!p | | !p->next)
throw “位置”;
else {
q=p->next;
x=q->data;
p->next=q->next;
delete q;
return x;
}
}
单链表的实现———构造函数
template <class T>
LinkList:: LinkList(T a[ ], int n)
{
first=new Node<T>;
first->next=NULL;
for (i=0; i<n; i++)
{
s=new Node<T>;
s->data=a[i];
s->next=first->next;
first->next=s;
}
}
算法设计的一般步骤:
第一步:确定入口(已知条件)、出口(结果);
第二步:根据一个小实例画出示意图;
第三步:① 正向思维:选定一个思考问题的起点,逐步提出问题、解决问题;②逆向思维:从结论出发分析为达到这个结论应该先有什么;③正逆结合;
第四步:写出顶层较抽象算法,分析边界情况;
第五步:验证第四步的算法;
第六步:写出具体算法;
第七步:进一步验证,手工运行。
时间性能比较
时间性能是指实现基于某种存储结构的基本操作(即算法)的时间复杂度。
按位查找:
Ø 顺序表的时间为O(1),是随机存取;
Ø 单链表的时间为O(n),是顺序存取。
插入和删除:
Ø 顺序表需移动表长一半的元素,时间为O(n);
单链表不需要移动元素,在给出某个合适位置的指针后,插入和删除操作所需的时顺序表和单链表的比较⑴若线性表需频繁查找却很少进行插入和删除操作,或其操作和元素在表中的位置密切相关时,宜采用顺序表作为存储结构;若线性表需频繁插入和删除时,则宜采用单链表做存储结构。
⑵当线性表中元素个数变化较大或者未知时,最好使用单链表实现;而如果用户事先知道线性表的大致长度,使用顺序表的空间效率会更高。
总之,线性表的顺序实现和链表实现各有其优缺点,不能笼统地说哪种实现更好,只能根据实际问题的具体需要,并对各方面的优缺点加以综合平衡,才能最终选定比较适宜的实现方法。