330 likes | 558 Views
数据结构. C 语言版. 内 容. 2.1 线性表的定义 2.2 基于抽象数据类型线性表的操作 2.3 线性表的存储结构 2.4 基于顺序存储结构的线性表操作算法 2.5 基于链式存储的线性表操作算法 2.6 循环链表的操作算法 2.7 双向链表的操作算法 2.8 顺序存储线性表与链式存储线性表的比较 2.9 一元多项式的表示及相加. 第 2 章 线性表. 2.1 线性表的定义. 1 、名词术语 · 线性表 --n 个数据元素的有限序列 .
E N D
数据结构 C语言版
内 容 • 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、两种存储结构: 顺序存储--数组 链式存储--链表
存储地址 内存状态 数据元素在线性表中的位序 b 1 b+l 2 … … b+(i-1)l i … … b+(n-1)l n b+nl 空闲 … b+(maxlen-1)l 2、线性表的顺序存储结构 示意图:
类型定义: //---线性表的动态分配顺序存储结构---#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; //空表长度为0L.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)+1if(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; //表长增1return 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; //被删除元素的值赋给eq=L.elem+L.length-1; //表尾元素的位置for(++p; p<=q; ++p) *(p-1)=*p; //被删除元素之后的元素左移--L.length; //表长减1return 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个位置之前插入元素ep=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个元素的位置指针preturn 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,ElemType &e){//删除带头结点的双链循环线性表L中第i个元素,i的合法值为1≤i≤表长。if (!(p=GetElemp_DuL(L,i))) //在L中确定第i个元素的位置指针preturn ERROR;e=p->data;p->prior->next=p->next;p->next->prior=p->prior;free(p); return OK;}//ListDelete_DuL
*顺序表的优点: ①存取数据速度快②占用的存储空间小 2.8 顺序存储线性表与链式存储线性表的比较 *顺序表的缺点: ①需占用连续存储空间②插入操作需移动元素 *链表的优点: ①不需占用连续存储空间②插入操作不需移动元素 *链表的缺点: ①存储数据麻烦、速度慢②占用的存储空间大
两个多项式的相加操作算法 根据一元多项式相加的运算规则:对于两个一元多项式中所有指数相同的项,对应指数相加,若其和不为零,则构成"和多项式"中的一项;对于两个一元多项式中所有指数不相同的项,则分别复抄到"和多项式"中去。在此,"和多项式"链表中的结点无需另生成,而应该从两个多项式的链表中摘取。其运算规则如下:假设指针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和+1 void 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}//whileif(!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)。 二、算法设计题