2.1 线性表的定义和特点
线性表是具有相同特性的数据元素的一个有限序列
线性表:
由n(>=0)数据元素(结点)a1,a2,...an组成的有限序列。
线性表的例子
例1 分析26个英文字母组成的英文表
例2 分析学生情况登记表![](https://img-blog.csdnimg.cn/adee9314d169414180feb9ef1896d514.png)
同一线性表中的元素必定具有相同特性,数据元素之间的关系是线性关系。
从以上例子可以看出线性表的逻辑特征是:
线性表是一种典型的线性结构
2.2 案例引入
1.一元多项式的运算:实现两个多项式加、减、乘运算
2.系数多项式的运算
顺序存储结构存在问题:
1.存储空间分配不灵活
2.运算的空间复杂度高
3.图书信息管理系统
总结
1.线性表中数据元素的类型可以为简单类型,也可以为复杂类型。
2.许多实际应用问题所涉及的基本操作有很大相似性,不应为每个具体英语单独编写一个程序。
3.从具体应用中抽象出共性的 逻辑结构和基本操作(抽象数据类型),然后实现其存储结构和基本操作。
2.3 线性表的类型定义
抽象数据类型线性表的定义如下:
基本操作(一)
InitList(&L)
操作结果:构造一个空的线性表L。
DestoryList(&L)
初始条件:线性表L已经存在。
操作结果:销毁线性表L。
ClearList(&L)
初始条件:线性表L已经存在。
操作结果:将线性表L重置为空表。
ListEmpty(L)
初始条件:线性表L已经存在。
操作结果:若线性表L为空表,则返回TRUE;否则返回FALSE。
ListLength(L)
初始条件:线性表L已经存在。
操作结果:返回线性表L中的数据元素个数。
GetElem(L,i,&e)
初始条件:线性表L已经存在,1<=i<=ListLength(L)。
操作结果:用e返回线性表L中第i个数据元素的值。
LocateElem(L,e,compare())
初始条件:线性表L已经存在,compare()是数据元素判定函数。
操作结果:返回L中第一个与e满足compare()的数据元素的位序。若这样的数据元素不存在则返回值为0。
PriorElem(L,cur_e,&pre_e)
初始条件:线性表L已经存在。
操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱;否则操作失败,pre_e无意义。
NextElem(L,cur_e,&next_e)
初始条件:线性表L已经存在。
操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的前驱;否则操作失败,next_e无意义。
ListInsert(&L,i,e)
初始条件:线性表L已经存在,1<=i<=ListLength(L)+1。
操作结果:在L的第i个位置之前插入新的数据元素e,L的长度加1。
ListDelete(&L,i,&e)
初始条件:线性表L已经存在,1<=i<=ListLength(L)。
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1。
ListTraverse(&L,visited())
初始条件:线性表L已经存在。
操作结果:对线性表中每个元素调用visited()。
2.4 线性表的顺序表示和实现
线性表的存储结构
在计算机内,线性表有两种基本的存储结构:
顺序存储结构和链式存储结构。
线性表的顺序表示又称为顺序存储结构或顺序映像。
顺序存储定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。
线性表中第一个数据元素a1的存储位置,称作线性表的起始位置或基地址。
线性表顺序存储结构占用一片连续的存储空间。知道某个元素的存储位置 就可以计算其他元素的存储位置。
线性表顺序存储结构的图示:
顺序表的特点:以物理位置相邻表示逻辑关系。
任一元素 均可随机存取。
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
typedef struct{
ElemType elem[LIST_INIT_SIZE] ;
int length; //当前长度
}Sqlist;
多项式的顺序存储结构类型定义
#define MAXSIZE 1000 //多项式可能达到的最大长度
typedef struct{ //多项式非零项的定义
float p; //系数
int e; //指数
}Polynomial;
typedef struct{
Polynomial *elem; //存储空间的基地址
int length; //多项式中当前项的个数
}SqList; //多项式的顺序存储结构类型为Sqlist
顺序表示意图:
补充操作算法中用到的预定义常量和类型
顺序表基本操作的实现
1.线性表L的初始化
Status InitList_Sq(Sqlist &L) //构造一个空的顺序表L
{
L.elem=new ElemType[MAXSIZE]; //为顺序表分配空间
if(!elem) exit(OVERFLOW); //储存分配失败
L.length=0; //空表长度为0
return OK;
}
销毁线性表L
void DestoryList(SqList &L)
{
if(L.elem)
delete L.elem; //释放存储空间
}
清空线性表
void ClearList(SqList &L)
{
L.length=0; //将线性表长度置为0
}
求线性表L长度
void GetLength(Sqlist L)
{
return(L.length);
}
判断线性表L是否为空
int IsEmpty(SqList L)
{
if(L.length==0)
return 1;
else
return 0;
}
2.顺序表的取值(根据位置i获取相应位置数据元素的内容)
int GetElem(SqList L,int i,ElemType &e)
{
if(i<1||i>L.length)
return ERROR;
e=L.elem[i-1]; //第i-1的单元存储着第i个数据
return OK;
}
3.顺序表的查找
在线性表L中查找与指定值e相同的数据元素的位置
从表的一端开始,逐个进行记录的关键字和给定值的比较。找到,返回该元素的位置序号,未找到,返回0。
int LocateElem(Sqlist L,ElemType e)
{
i=0;
while(i<L.length&&l.elem[i]!=e)
i++;
if(i<L.length)
return i+1;
return 0;
}
算法分析:
4.顺序表的插入
Status ListInsert_Sq(SqList &L,int i,ElemType e)
{
if(i<1||i>L.length+1)
return ERROR; //i值不合法
if(L.length==MAXSIZE)
return ERROR; //储存空间已满
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j]; //插入位置及之后的的元素后移
L.elem[i-1]=e; //将新元素e放入第i个位置
L.length++; //表长加一
return OK;
}
算法分析:
5.顺序表的删除
Status ListDelete_Sq(SqList &L,int i)
{
if((i<1)||(i>L.length))
return ERROR;
for (j=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem{j};
L.length--;
return OK;
}
算法分析:
顺序表的特点
顺序表的操作算法分析
优缺点
2.5 线性表的链式表示和实现
链式存储结构
结点在存储器中的位置是任意的,即逻辑上相邻的元素在物理上不一定相邻
线性表的链式表示又称为非顺序映像或链式映像。
用一组物理位置任意的存储单元来存放线性表的数据元素。
这组存储单元既可以是连续的,也可以是不连续的,甚至是零散分布在内存中任意位置上的。
链表中元素的逻辑次序和物理次序不一定相同。
与链式存储有关的术语
1.结点:数据元素的存储映像。由数据域和指针域两部分组成。
2.链表:n个结点有指针链组成一个链表。
它是线性表的链式存储映像,称为线性表的链式存储结构。
3.单链表,双链表,循环链表
结点只有一个指针域的链表,称为单链表或线性链表。
结点有两个指针域的链表,称为双链表。
首尾相接的链表称为循环链表。
4. 头指针,头结点和首元结点:
头指针:是指向链表中第一个结点的指针。
首元结点:是指链表中存储第一个数据元素a1的结点。
头结点:是在链表的首元结点之前附设的一个结点。
两个例子:
讨论1.如何表示空表?
讨论2.在链表中设置头结点 有什么好处?
讨论3.头结点的数据域内装的是什么?
链表(链式存储结构)的特点
2.5.1单链表的定义和表示
带头结点的单链表
单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名,若头指针名是L,则把链表称为表L。
单链表的存储结构
typedef struct Lnode //声明结点的类型和指向结点的指针类型
{
ElemType data; //结点的数据域
struct Lnode *next; //结点的指针域
}Lnode,*Linklist; //Linklist为指向结构体Lnode的指针类型
2.5.2 单链表基本操作的实现
1.单链表的初始化
算法步骤:1.生成新结点作头结点,用头指针L指向头结点
2.将头结点的指针域置空
Status LnitList_L(Linklist &L)
{
L=new Lnode; //或 L=(Linklist)malloc(sizeof(LNode));
L->next=NULL;
return OK;
}
补充单链表几个常用简单算法
1.判断链表是否为空
空表:链表中无元素,头指针和头结点仍然在
算法思路:判断头结点指针域是否为空
int Listempty(Linklist L) //若L为空表,则返回1;否则返回0;
{
if(L->next) //非空
return 0;
else
return 1;
}
2.单链表的销毁
算法思路:从头指针开始,依次释放所有结点
Status Destorylist(Linklist &L)
{
Lnode *p;// Linklist p;
while(L)
{
p=L;
L=L->next;
delete p;
}
return OK;
}
3.清空链表
链表仍然存在,但链表中无元素,成为空链表;
算法思路:依次释放所有结点,并将头结点指针域设置为空
Status clearlist(Linklist &L)
{
Lnode *p,*q; //或 Linklist p,q;
p=L->next;
while(p) //没到表尾
{
q=p->next;
delete p;
p=q;
}
L->next=NULL; //头结点指针域为空
return OK;
}
4.求单链表L的表长
算法思路:从首元结点开始,依次计数所有结点
int Listlength(Linklist L)
{
Linklist p;
p=L->next; //p指向第一个结点
i=0;
while(p) //遍历单链表,统计结点数
{
i++;
p=p->next;
}
}
2.取值——取单链表中第i个元素的内容
从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构。
Status Getelem(Linklist L,int i,ElemType &e)
{
p=L->next; //初始化
j=1;
while(p&&j<i) //向后扫描,直到p指向第i个元素或p为空
{
p=p->next;
++j;
}
if(!p||j>i)
return ERROR; //第i个元素不存在
e=p->next;
return OK; //取第i个元素
}
3.按值查找———根据指定数据获取该数据所在的位置(地址)
Lnode *Locateelem(Linklist L,Elemtype e)
{
p=L->next;
while(p&&p->data!=e)
p=p->next;
return p;
}
3.按值查找变化——根据指定数据获取该数据位置序号
int LocateElem(Linklist L,Elemtype e)
{
p=L->next;
j=1;
while(p&&p->data!=e)
{
p=p->next;
j++;
}
if(p)
return j;
else
return 0;
}
4.插入——在第i个结点前插入值为e的新结点
Status Listinsert(Linklist &L,int i,Elemtype e)
{
p=L;
j=0;
while(p&&j<i-1) //寻找第i-1个结点,p指向第i-1个结点
{
p=p->next;
++j;
}
if(!p||j>i-1) //i大于表长+1或者小于1,插入位置非法
return ERROR;
s=new Lnode; //生成新结点s,将结点s的数据域置为e
s->data=e;
s->next=p->next; //将结点s插入L中
p->next=s;
return OK;
}
5.删除——删除第i个结点
Status ListDelete(Linklist &L,int i,Elemtype &e)
{
p=L;
j=0;
while(p->next&&j<i-1) //寻找第i个结点,并令p指向其前驱
{
p=p->next;
++j;
}
while(p->next||j>i-1) //删除位置不合理
return ERROR;
q=p->next;
p-next=q->next; //临时保存被删结点的地址以备释放
e=q->data; //改变删除结点前驱结点的指针域
delete q; //释放删除结点的空间
return OK;
}
单链表的查找,插入,删除算法的时间效率分析
6.建立单链表:头插法——元素插入在链表头部
1.从一个空表开始,重复读入数据;
2.生成新结点,将读入数据存放到新结点的数据域中
3.从最后一个结点开始,依次将各结点插入到链表的前端
void CreateList(Linklist &L,int n)
{
L=new LNode;
L->next=NULL; //先建立一个带头结点的单链表
for(i=n;i>0;--i)
{
p=new LNode; //生成新结点 p=(LNode*)malloc(sizeof(LNode));
cin>>p->data; //输入元素值 scanf(&p->data);
p->next=L->next; //插入到表头
L-next=p;
}
}
7.建立单链表:尾插法——元素插入在链表尾部
1.从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。
2.初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。
void Createlist(Linklist &L,int n)
{
L=new LNode;
L->next=NULL;
r=L; //尾指针r指向头结点
for(i=0;i<n;++i)
{
p=new LNode;
cin>>p->data; //生成新结点,输入元素值
p->next=NULL;
r->next=p; //插入到表尾
r=p; //r指向新的尾结点
}
}
2.6顺序表和链表的比较