Upload
fawzi
View
84
Download
0
Embed Size (px)
DESCRIPTION
数据结构. C 语言版. 内 容. 2.1 线性表的定义 2.2 基于抽象数据类型线性表的操作 2.3 线性表的存储结构 2.4 基于顺序存储结构的线性表操作算法 2.5 基于链式存储的线性表操作算法 2.6 循环链表的操作算法 2.7 双向链表的操作算法 2.8 顺序存储线性表与链式存储线性表的比较 2.9 一元多项式的表示及相加. 第 2 章 线性表. 2.1 线性表的定义. 1 、名词术语 · 线性表 --n 个数据元素的有限序列 . - PowerPoint PPT Presentation
Citation preview
数据结构数据结构数据结构数据结构CC 语言版语言版
内 容• 2.1 线性表的定义 • 2.2 基于抽象数据类型线性表的操作 • 2.3 线性表的存储结构 • 2.4 基于顺序存储结构的线性表操作算法 • 2.5 基于链式存储的线性表操作算法 • 2.6 循环链表的操作算法 • 2.7 双向链表的操作算法 • 2.8 顺序存储线性表与链式存储线性表的比较 • 2.9 一元多项式的表示及相加
第 2 章 线性表 2.1 线性表的定义
1 、名词术语
· 线性表 --n 个数据元素的有限序列 .
记为 (a1,a2,……,ai-1,ai,ai+1,…… , an) 。
例如: 26 英文字母表 (A,B,C,……,X,Y,Z) 、一个班级的学生成绩报表等· 表长 -- 线性表中元素的个数
· 直接前驱元素 -- 线性表中 ai-1 领先于 ai ,则 ai-1 是 ai 的直接前驱元素
· 直接后继元素 -- 线性表中 ai 领先于 ai+1 ,则 ai+1 是 ai 的直接后继元素
2 、线性表的抽象数据类型定义
ADT List{
数据对象: D={ai|ai∈ElemSet,i=1,2,……,n,n≥0}
数据关系: R1={<ai-1,ai>|ai-1,ai∈D,i=2,……,n}
基本操作: InitList(&L) 操作结果:构造一个空的线性表 L 。 ……}ADT List
2.2 基于抽象数据类型线性表的操作
1 、建立一个空的线性表 2 、求线性表中元素个数
3 、判断线性表 L 是否为空 4 、取线性表 L 中第 i 个元素
5 、在线性表中插入某元素 6 、在线性表中删除某元素
7 、求线性表中某元素的前驱元素
8 、求线性表中某元素的后继元素
9 、在线性表中查找某元素 10 、两个线性表进行合并
2.3 线性表的存储结构
1 、两种存储结构:
顺序存储 -- 数组
链式存储 -- 链表
2 、线性表的顺序存储结构 示意图:
存储地址 内存状态 数据元素在线性表中的位序
b 1
b+l 2
… …
b+(i-1)l i
… …
b+(n-1)l n
b+nl
空闲…
b+(maxlen-1)l
类型定义:
//--- 线性表的动态分配顺序存储结构 --- #define LIST_INIT_SIZE 100 // 线性表存储空间的初始分配量 #define LISTINCREMENT 10 // 线性表存储空间的分配增量 typedef struct{ ElemType *elem; // 存储空间基址 int length; // 当前长度 int listsize; // 当前分配的存储容量 (以 sizeof(ElemType) 为单位) }SqList;
3 、线性表的链式存储结构 L=(a1,a2,……,an)
示意图:
注: (a) 为非空表, (b) 为空表。
类型定义:
//--- 线性表的单链表存储结构 ---
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
2.4 基于顺序存储结构的线性表操作算法
1 、创建一个空的线性表
Status InitList_Sq(SqList &L){ // 构造一个空的线性表 L 。 L.elem=(ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if (!L.elem) exit(OVERFLOW); // 存储分配失败 L.length=0; // 空表长度为 0 L.listsize=LIST_INIT_SIZE; // 初始存储容量 return OK; }//InitList_Sq
2 、插入元素算法
Status ListInsert_Sq(SqList &L,int i,ElemType e){ // 在顺序线性表 L 中第 i 个位置之前插入新的元素 e, //i 的合法值为 1≤i≤Listlength_Sq(L)+1 if(i<1 || i>L.length+1) return ERROR; //i 的值不合法 if(L.length>=L.listsize){ // 当前存储空间已满,增加分配 newbase=(ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)* sizeof(ElemType)); if(!newbase) exit(OVERFLOW); // 存储分配失败 L.elem=newbase; // 新基址 L.listsize+=LISTINCREMENT; // 增加存储容量 } q=&(L.elem[i-1]); //q 为插入位置 for(p=&(L.elem[L.length-1]); p>=q; --p) *(p+1)=*p; // 插入位置及之后的元素右移 *q=e; // 插入 e ++L.length; // 表长增 1 return OK; } //ListInsert_Sq
3 、删除元素算法
Status ListDelete_Sq(SqList &L,int i,ElemType &e){ // 在顺序线性表 L 中删除第 i 个元素,并用 e 返回其值 //i 的合法值为 1≤i≤Listlength_Sq(L) if((i<1)||(i>L.length)) return ERROR; //i 的值不合法 p=&(L.elem[i-1]); //p 为被删除元素的位置 e=*p; // 被删除元素的值赋给 e q=L.elem+L.length-1; // 表尾元素的位置 for(++p; p<=q; ++p) *(p-1)=*p; // 被删除元素之后的元素左移 --L.length; // 表长减 1 return OK; }//ListDelete_Sq
4 、查找元素算法 int LocateElem_Sq(Sqlist L,ElemType e,Status(*compare)(ElemType,ElemType)){
// 在顺序线性表 L 中查找第 1 个值与 e 满足 compare() 的元素的位序 // 若找到,则返回其在 L 中的位序,否则返回 0 。 i=1; //i 的初值为第 1 个元素的位序 p=L.elem; //p 的初值为第 1 个元素的存储位置 while(i<=L.length &&! (*compare)(*p++,e)) ++i; if(i<=L.length) return i; else return 0; }//LocateElem_Sq
5 、两表合并算法
void MergeList_Sq(SqList La,SqList Lb,SqList &Lc){
// 已知顺序线性表 La 和 Lb 的元素按值非递减排列 // 归并 La 和 Lb 得到新的顺序线性表 Lc,Lc 的元素也按值非递减排列 pa=La.elem; pb=Lb.elem; Lc.listsize=Lc.length=La.length+Lb.length; pc=Lc.elem=(ElemType *)malloc(Lc.listsize * sizeof(ElemType)); if(!Lc.elem) exit(OVERFLOW); // 存储分配失败 pa_last=La.elem+La.length-1; pb_last=Lb.elem+Lb.length-1; while(pa<=pa_last && pb<=pb_last){ // 归并 if(*pa<=*pb) *pc++=*pa++; else *pc++=*pb++; } while(pa<=pa_last) *pc++=*pa++; // 插入 La 的剩余元素 while(pb<=pb_last) *pc++=*pb++; // 插入 Lb 的剩余元素 } //MergeList_Sq
2.5 基于链式存储的线性表操作算法
* 带头结点的单链表(如右: a 图为非空表, b 图为空表)
1 、创建一个带头结点的单链表
void CreateList_L(LinkList &L,int n){
// 逆位序输入 n 个元素的值,建立带表头结点的单链线性表 L 。 L=(LinkList) malloc (sizeof (LNode));
L->next=NULL; // 先建立一个带头结点的单链表 for (i=n; i>0; --i){
p=(LinkList) malloc (sizeof (LNode)); // 生成新结点 scanf(&p->data); // 输入元素值 p->next=L->next; L->next=p; // 插入到表头
}
2 、链表中插入结点算法
Status ListInsert_L(LinkList &L, int i, ElemType e){
// 在带头结点的单链线性表 L 中第 i 个位置之前插入元素e
p=L; j=0;
while (p && j<i-1) {p=p->next; ++j;} // 寻找第 i-1 个结点 if (!p || j>i-1) return ERROR; //i小于 1或者大于表长 s=(LinkList) malloc (sizeof (LNode)); // 生成新结点 s->data=e; s->next=p->next; // 插入 L 中 p->next=s;
return OK;
}//ListInsert_L
3 、链表中删除结点算法
Status ListDelete_L(LinkList &L, int i, ElemType &e){
// 在带头结点的单链线性表 L 中 , 删除第 i 个元素,并由 e 返回其值 p=L; j=0;
while (p->next && j<i-1){ // 寻找第 i 个结点,并令 p指向其前趋 p=p->next; ++j;
}
if (!(p->next) || j>i-1) return ERROR; // 删除位置不合理 q=p->next; p->next=q->next; // 删除并释放结点 e=q->data; free(q);
return OK;
}//ListDelete_L
4 、两个有序链表合并成一个有序链表
void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc){
// 已知单链线性表 La 和 Lb 的元素按值非递减排列 // 归并 La 和 Lb 得到新的单链线性表 Lc , Lc 的元素也按值非递减排列。
pa=La->next; pb=Lb->next;
Lc=pc=La; // 用 La 的头结点作为 Lc 的头结点 while (pa && pb){
if (pa->data<=pb->data){
pc->next=pa; pc=pa; pa=pa->next;
}
else {pc->next=pb; pc=pb; pb=pb->next;}
}
pc->next=pa?pa:pb; // 插入剩余段 free(Lb); //释放 Lb 的头结点
}//MergeList_L
2.6 循环链表的操作算法
* 什么是循环链表?
循环链表是另一种形式的链式存储结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。由此,从表中任一结点出
发均可找到表中其他结点,如下图所示为单链的循环链表。
循环链表的操作与单链表的差别
循环链表的操作与单链表基本一致,差别仅在于算法中的循环条件不是 P 或 P - >netx 是否为空,而是它们是否等于头指针。
2.7 双向链表的操作算法
* 什么是双向链表?
为了克服单链表单向性的缺点,可利用双向链表,在它的结点中有两个指针域,其一指向直接后继,另一指向直接前趋。在 C 语言中可描述如下:
//------- 线性表的双向链表存储结构 --------- typedef struct DuLNode{ ElemType data; struct DuLNode *prior; struct DuLNode *next; }DuLNode, *DuLinkList;
1. 在双向链表中插入节点Status ListInsert_Dul(DuLinkList &L,int i,ElemType e){
// 在带头结点的双链循环线性表 L 中第 i 个位置之前插入元素 e,i 的合法值为 1≤i≤ 表长 +1 。 if (!(p=GetElemp_DuL(L,i))) // 在 L 中确定第 i 个元素的位置指针 p return ERROR; //p=NULL,即第 i 个元素不存在 if(!(s=(DuLinkList) malloc (sizeof (DuLNode)))) return ERROR; s->data=e; s->prior=p->prior; p->prior->next=s; s->next=p; p->prior=s; return OK; }//ListInsert_DuL
2. 在双向链表中删除节点Status ListDelete_Dul(DuLinkList &L,int i,ElemTy
pe &e){ // 删除带头结点的双链循环线性表 L 中第 i 个元素,i 的合法值为 1≤i≤ 表长。 if (!(p=GetElemp_DuL(L,i))) // 在 L 中确定第 i个元素的位置指针 p return ERROR; e=p->data; p->prior->next=p->next; p->next->prior=p->prior; free(p); return OK; }//ListDelete_DuL
2.8 顺序存储线性表与链式存储线性表的比较
* 顺序表的优点:
① 存取数据速度快②占用的存储空间小
* 顺序表的缺点:
① 需占用连续存储空间②插入操作需移动元素
* 链表的优点:
① 不需占用连续存储空间②插入操作不需移动元素
* 链表的缺点:
① 存储数据麻烦、速度慢②占用的存储空间大
2.9 一元多项式的表示及相加
两个多项式的相加操作算法 根据一元多项式相加的运算规则:对于两个一元多项式中所
有指数相同的项,对应指数相加,若其和不为零,则构成 "和多项式 "中的一项;对于两个一元多项式中所有指数不相同的项,则分别复抄到 "和多项式 "中去。 在此, "和多项式 "链表中的结点无需另生成,而应该从两个多项式的链表中摘取。其运算规则如下:假设指针 qa 和 qb分别指向多项式 A 和多项式 B 当前进行比较的某个结点,则比较两个结点中的指数项,有下列三种情况: 1 )指针 qa所指结点的指数值<指针 qb所指结点的指数值,则应摘取指针 qa所指结点插入到 "和多项式 "链表中去; 2 )指针 qa所指结点的指数值>指针 qb所指结点的指数值,则应摘取指针 qb所指结点插入到 "和多项式 "链表中去; 3 )指针 qa所指结点的指数值=指针 qb所指结点的指数值,则将两个结点中的系数相加,若和数不为零,则修改 qa所指结点的系数值,同时释放 qb所指结点;反之,从多项式 A 的链表中删除相应结点,并释放指针 qa 和 qb所指结点。
两个多项式的相加操作算法描述 int cmp(term a,term b);
//依 a 的指数值 <(或 =)(或 >)b 的指数值,分别返回 -1 、 0 和 +1void AddPolyn(polynomial &Pa,polynomial &Pb){
// 多项式加法: Pa=Pa+Pb ,利用两个多项式的结点构成 "和多项式 "。 ha=GetHead(Pa); hb=GetHead(Pb); //ha 和 hb 分别指向 Pa 和 Pb 的头结点 qa=NextPos(ha); qb=NextPos(hb); //qa 和 qb 分别指向 Pa 和 Pb 中当前结点 while(!Empty(Pa)&&!Empty(Pb)){ //Pa 和 Pb均非空 a=GetCurElem(qa); b=GetCurElem(qb); //a 和 b 为两表中当前比较元素 switch(*cmp(a,b)){ case -1: // 多项式 PA 中当前结点的指数值小 ha=qa; qa=NextPos(Pa,qa); break; case 0: // 两者的指数值相等 sum=a.coef+b.coef; if (sum!=0.0){ //修改多项式 PA 中当前结点的系数值 SetCurElem(az,sum); ha=qa;} else{ // 删除多项式 PA 中当前结点 DelFirst(ha,qa); FreeNode(qa);} DelFirst(hb,qb); FreeNode(qb); qb=NextPos(Pb,hb); qa=NextPos(Pa,ha); break; case 1: // 多项式 PB 中当前结点的指数值小 DelFirst(hb,qb); InsFirst(ha,qb); qb=NextPos(Pb,hb); break; }//switch }//while if(!Empty(Pb)) Append(Pa,qa); // 链表 Pb 中剩余结点 FreeNode(hb); //释放Pb 的头结点 }//AddPolyn
实例演示
小结 本章的重点是掌握顺序表和单链表上
实现的各种基本算法及相关的时间性能分析,难点是使用本章所学的基本知识设计有效算法解决与线性表相关的应用问题。
第二章 线性表习题一、基础知识题2.1 试描述头指针、头结点、开始结点的区别、并说明
头指针和头结点的作用。2.2 何时选用顺序表、何时选用链表作为线性表的存储
结构为宜 ?
二、算法设计题2.7 设线性表的 n 个结点定义为 (a0,a1,...an-1) ,重写顺序表上实现的插入和删除算法: InsertList 和 DeleteList.2.8 试分别用顺序表和单链表作为存储结构,实现将线性表 (a0,a1,...an-1) 就地逆置的操作,所谓 " 就地 " 指辅助空间应为 O(1) 。