文章目录
学习目标
- 掌握线性表的逻辑结构,线性表的顺序存储结构和链式存储结构的描述方法;熟练掌握线性表在顺序存储结构和链式存储结构的结构特点以及相关的查找、插入、删除等基本操作的实现;并能够从时间和空间复杂性的角度综合比较两种存储结构的不同特点
- 掌握栈和队列的结构特性和描述方法,熟练掌握栈和队列的基本操作的实现,并且能够利用栈和队列解决实际应用问题
1 线性表
1.1 线性表的定义
线性表:零个或多个数据元素的有限序列
数学定义:若将线性表记为(a1,…, ai-1, ai, ai+1, …, an),则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。当i=1,2,…,n-1时,ai有且仅有一个直接后继,当i=2,3,…,n时,ai有且仅有一个直接前驱。
注意:线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。
数学模型:
1.2 线性表的抽象数据类型
1.3 线性表的顺序存储结构
用一段地址连续的存储单元依次存储线性表的数据元素。可以用C语言的一维数组来实现顺序存储结构
代码结构:
三个属性:存储空间的起始位置、线性表的最大存储容量、线性表的当前长度
#define max 100
struct LIST{
ElemType elements[max];
int last;
};
存取时间性能 O(1)
插入操作
时间复杂度 O(n)
void Insert(ElemType x,position p,LIST &L){
position q;
if(L.last>=max-1)
cout<<"满";
else if((p>L.last+1)||p<1)
cout<<"位置不存在";
else{
for(q=L.last;q>=p;q--)
L.element[q+1]=L.elements[q];
L.element[p]=x;
L.last+=1;
}
}
删除操作
时间复杂度 O(n)
void Delete(position p,LIST &L){
position q;
if(p>L.last||p<1)
cout<<"位置不存在";
else{
L.last-=1;
for(q=p;q<=L.last;q++)
L.elements[q]=L.elemrnts[q+1];
}
}
其他操作
线性表顺序存储结构的优缺点
优点:
•无须为表示表中元素之间的逻辑关系而增加额外的存储空间
•可以快速地存取表中任一位置的元素
缺点:
•插入和删除操作需要移动大量元素
•当线性表长度变化较大时,难以确定存储空间的容量
•造成存储空间的“碎片”
1.4 线性表的链式存储结构
链表特点:用一组任意的存储单元存储线性表的数据元素
存储数据元素信息的域称为数据域,存储直接后继位置的域称为指针域。
单链表
n个结点(ai的存储映像)链结成一个链表,即为线性表(a1,a2,…,an)的链式存储结构
因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
存储结构特点
- 逻辑次序和物理次序不一定相同
- 元素之间的逻辑关系用指针表示
- 需要额外空间存储元素之间的关系
- 非随机访问存取结构(顺序访问)
存储结构定义
struct cellype{
EleType data;
celltype *next;
}
typedef celltype *LIST ;
typedef celltype *position;
头结点:为了更方便的对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。
头结点的作用:在任意位置的插入或者删除的代码统一;空表和非空表表示统一
单链表的插入–后插
void Insert(Element x,position p,LIST &L){
position q;
q=new celltype;
q->data=x;
q->next=p->next;
p->next=q;
}//时间复杂度 O(1)
单链表的插入–前插
将q插入到p后面
交换pq的数据域信息
复杂度:O(1)
单链表的删除
删除p后面的结点
void Delete(positonn p,LIST &L){
position q;
if(p->next!=NULL){
q=p->next;
p->next=q->next;
delete q;
}
}//复杂度:O(1)
单链表的删除–扩展
设存储单元ai的结点指针为p,要实现将p对应的结点删除的操作
交换pq的数据域信息
将p后面的q删除
复杂度:O(1)
其他操作
为什么是p->next->data? 需要考虑到最后一个结点
单链表的整表创建:尾插法(或者头插法,只是元素顺序相反)
单链表结构与顺序存储结构的优缺点
双向链表
结点有两个指针域,一个指向直接后继,另一个指向直接前驱
代价:插入和删除时,需要更高两个指针变量;每个结点都需要开路凉风指针,空间占用更多
优点:对某个结点的前后结点的操作更加方便,提高时间性能
存储结构
struct dcelltype{
ElemType data;
delltype *next,*prior;
}
插入操作
在带头结点的表中的p位置插入元素x
void Insert(ElemType x,position p,DLIST &L){
s= new dcelltype;
s->data=x;
s-prior=p;
s->next=p->next;
p->next->prior=s;
p-next=s;
}
s的前驱后继、s后继的前驱、s前驱的后继
删除操作
在不带头结点的表中,删除位置p的元素
void Delete(position p,DLIST &L){
if(p->prior!=NULL)
p->prior-next=p->next;
if(p->next!=NULL)
p->next->prior=p->prior;
}
循环链表
定义:将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表
循环链表与单链表的主要差异循环的判断条件:
单链表:p->next == NULL
单循环链表:p->next == 头结点
尾指针
在表左端插入结点
在表右端插入结点
循环链表的应用
约瑟夫环、将两个循环链表合并为一个表
1为删除链表B的头结点
2 栈
2.1 栈的定义与操作
2.2 栈的顺序存储结构
typedef struct{
ElemTpe elements[max];
int top;
}STACK;
栈的操作实现
2.3 栈的链式存储结构
链表顶端为栈顶;链栈需要表头节点,作用与链表相同
sstruct node{
ElemType data;
node *next;
}
栈的操作实现
2.4 栈与递归调用
编译器使用了栈实现递归
递归函数的内部执行过程:建栈、压栈、出栈
3 队列
3.1 队列的定义
3.2 队列的顺序存储
设置队头、队尾两个指针
判断队空队满
队空:front == rear
队满:( (rear+1)%MaxSize == front) 代价浪费一个元素空间,队满时数组中有一个空闲单元;
存储结构
顺序队列的操作
3.3 队列的链式存储
存储结构定义
//结点类型
struct celltype{
ElemType data;
celltype *next;
}
//队列类型
struct QUEUE{
celltype *front;
celltype *rear;
}
链队列的操作
3.4 队列的应用
凡是符合先进先出原则的都可以使用:服务窗口和排号机、打印机的缓冲区、分时系统、树型结构的层次遍历、图的广度优先搜索等
例如:舞伴问题
传送门
上一章:数据结构与算法 第一章 绪论
下一章:数据结构与算法 第三章 树