数据结构.docx
- 文档编号:3522953
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:125
- 大小:83.70KB
数据结构.docx
《数据结构.docx》由会员分享,可在线阅读,更多相关《数据结构.docx(125页珍藏版)》请在冰豆网上搜索。
数据结构
第二章
2.顺序表的基本操作
⑴初始化
voidInitList_Sq(SqList&L,intxsize=LIST_INIT_SIZE,intincresize=LISTINCREMENT)
{
L.elem=(ElemType*)malloc(maxsize*sizeof(ElemType));if(!
L.elem)exit
(1);
L.length=0;
L.listsize=maxsize;
L.incrementsize=incresize;
}//InitList_Sq
¢Æ求表长
intListLength_Sq(SqListL)
{
returnL.length;
}//ListLength_Sq
intLocateElem_Sq(SqListL,ElemTypee)
{
for(inti=0;i if(L.elem[i]==e)returni; return-1; }//LocateElem_Sq 注意: ⑴当元素ElemType为结构体类型时,在比较两个元素是否相等时不能简单使用运算符“==”,而应该修改if条件表达式,使比较在相应的域上进行,并且此域为简单类型。 当然,也可利用C++的重载机制对运算符“==”进行重载,使之可以进行两个结构体的比较。 另外,若用于比较的元素为字符串,则需要使用字符串比较函数strcmp。 ⑵因为C/C++中数组的下标是从0开始,所以当查找失败时不能返回0,而应该返回一个有效下标之外的整数。 ¢È前插 算法思想 假设顺序表中已有length个数据元素,在第i(0≤i≤length)个数据元素之前插入一个新的数据元素e时,需将第length-1个至第i个存储位置(共length-i个)的数据元素依次后移,然后把e插入到第i个存储位置,并使顺序表当前长度length加1,最后返回true值;若插入位置i<0或i>length,则无法插入,此时返回false值。 操作示例 算法实现 boolListInsert_Sq(SqList&L,inti,ElemTypee) {intj; if(i<0||i>L.length)returnfalse; if(L.length>=L.listsize) { L.elem=(ElemType*)realloc(L.elem,(L.listsize+L.incrementsize)*sizeof(ElemType)); if(! L.elem)exit (1); L.listsize+=L.incrementsize; } for(j=L.length;j>i;j--) L.elem[j]=L.elem[j-1]; L.elem[i]=e; L.length++; returntrue; }//ListInsert_Sq 算法总结 ①插入操作时要判断插入的位置是否合理和是否表满两种情况。 ②如果表满,这时若插入一个元素则会发生上溢(overflow)。 解决上溢的办法是增补一段空间(空闲),将原数组中的元素依次复制到新空间内,然后再进行插入。 ③插入时,元素依次后移,同时线性表的长度加1。 ¢É删除 算法思想 假设顺序表中已有length个数据元素,要删除第i(0≤i≤length-1)个数据元素,需将第i至第length-1存储位置(共length-i个)的数据元素依次前移,并使顺序表当前长度减1,然后返回true值;若删除位置i<0或i>length-1,则无法删除,返回false值。 特殊情况下删除的元素x为表中的第num个元素(最后一个元素),则不需要移动任何元素,但要将表的长度减1。 操作示例 图2.6在顺序表中删除元素的过程 顺序表的删除过程演示 算法实现 boolListDelete_Sq(SqList&L,inti,ElemType&e) { intj;if(i<0||i>=L.length)returnfalse; if(L.length<=0)returnfalse; e=L.elem[i]; for(j=i+1;j<=L.length-1;j++) L.elem[j-1]=L.elem[j]; L.length--; returntrue; }//ListDelete_Sq 算法总结 ⑴删除操作时要判断删除的位置是否合理和是否表空两种情况。 ⑵删除时,元素依次前移,同时线性表的长度减1。 ⑹取数据元素 boolGetElem_Sq(SqListL,inti,ElemType&e) { if(i<0||i>L.length)returnfalse; if(L.length<=0)returnfalse; e=L.elem[i]; returntrue; }//GetElem_Sq ⑺顺序表的遍历 voidListTraverse_Sq(SqListL) { inti; for(i=0;i cout< cout< }//ListTraverse_Sq ⑻撤销顺序表 voidDestroyList_Sq(SqList&L) { free(L.elem); L.listsize=0; L.length=0; }//DestroyList_Sq 说明: 如果是用类来描述顺序表,那么以上所有操作都可以作为类的成员而与数据封装在一起成为顺序表类,这样顺序表所有对象都可以使用其中的操作。 3.算法分析 假设在顺序表的任何位置上插入数据元素都是等概率的,即 则有 同理,可推出在顺序表中删除一个数据元素的平均移动次数为 由此可见,在顺序表中插入和删除一个数据元素时,平均约需移动一半数据元素,即插入和删除元素的时间复杂度均为O(n)。 顺序表的其它操作中,查找元素操作的执行时间主要用于“进行两个元素之间的比较”,若L中存在和e相同的元素ai,则比较次数为i(0≤i (1),遍历顺序表的操作的时间复杂度为O(n),其中n为顺序表的长度。 2.3链表及其基本操作 2.3.1链式存储的概念 1.链表的定义 用一组任意的存储单元存储线性表里的各元素(这组存储单元可以是连续的,也可以是不连续的)。 为了反映出各元素在线性表中的逻辑关系(前后位置关系),除了存储元素本身的信息外,还需要添加一个或多个指针域,用来指示另一个数据元素在内存中的存储首址。 这称为线性表的链式存储结构,简称链表。 如图2.7是三个元素的线性表的链式存储结构示意图。 特点: 逻辑上相邻的元素其物理位置不一定相邻,它是一种非顺序的存储结构。 图2.7 2.链表的结点结构 存储数据元素的数据域和存储另一数据元素的地址的指针域组成了数据元素的存储映像,称为结点,其结构如图2.8所示。 图2.8链式存储中结点的结构 在链式存储结构中,由于每个元素的存储位置是保存在它的前驱结点或后继结点中的,所以只有当访问到其前驱结点或后继结点才能够按指针访问到该结点,这是一种顺序存取机制,也就是说,单链表是一种非随机存取的存储结构。 3.链表的分类 2.3.2单链表 1.单链表的定义 单链表是最简单的一种链式存储结构,其中每个结点只含有1个指针域,这个指针域用来存放其后继结点的地址。 其结点结构如图2.9所示。 2.结点结构描述 typedefstructNode{ ElemTypedata; structNode*next; }LNode,*LinkList; 3.单链表的逻辑表示 在逻辑上,一般把单链表画成用箭头相连的结点序列,结点之间的箭头表示指针域中的指针(地址)。 同时,为了算法实现上的方便,通常在单链表的第一个结点之前附设一个称为头结点的结点。 如图2.10所示。 图2.10带头结点的单链表 头结点: 单链表中第一个结点。 表头指针: 存放单链表中第一个结点的地址。 表尾结点: 单链表中最后一个结点,表尾结点的指针域指针为空。 开始结点: 存放线性表的第一个元素的结点。 注意: 头结点在链表中并不是必须的,仅仅是为了操作上的方便。 4.单链表的基本操作 ⑴初始化 voidInitList_L(LinkList&L) { L=(LNode*)malloc(sizeof(LNode)); if(! L)exit (1); L->next=NULL; }//InitList_L ⑵求表长 intListLength_L(LinkListL) { LinkListp;intk=0;p=L->next;while(p) { k++;p=p->next; } returnk; }//ListLength_L ⑶查找 LNode*LocateElem_L(LinkListL,ElemTypee){ LinkListp; p=L->next; while(p&&p->data! =e) p=p->next; returnp; }//LocateElem_L ⑷插入 算法思想 ①寻找单链表的第i-1个结点并用指针p指示; ②产生一个新结点,并由指针s指示,并置e为其数据域值; ③修改结点p的指针域,使结点s成为其后继(原来是ai所在的结点),并使第i个结点成为结点s的后继。 操作示例 算法实现 boolListInsert_L(LinkList&L,inti,ElemTypee) { LinkListp,s; intj; p=L;j=0; while(p->next&&j if(j! =i-1)returnfalse; if((s=(LNode*)malloc(sizeof(LNode)))==NULL) exit (1); s->data=e; s->next=p->next;p->next=s; returntrue; }//ListInsert_L 注意: while中的条件表达式: p->next&&j p&&j ⑸删除 算法思想 ①在单链表中寻找到第i-1个结点并用指针p指示,同时让指针变量q指向待删除的结点; ②修改结点p的指针域,使结点q的后继成为结点p的后继; ③取出被删元素的值,并释放q所指结点的空间。 操作示例 算法实现 boolListDelete_L(LinkList&L,inti,ElemType&e) { LinkListp,q; intj; p=L;j=0; while(p->next->next&&j if(j! =i-1)returnfalse; q=p->next; p->next=q->next; e=q->data;free(q); returntrue; }//ListDelete_L 注意: while中的条件表达式中的“p->next->next”是为了保证第i个结点存在。 ⑹取元素 boolGetElem_L(LinkListL,inti,ElemType&e) { LinkListp; intj; p=L;j=0; while(p->next&&jnext;j++;} if(j! =i)returnfalse; e=p->data; returntrue; }//GetElem_L 说明: 取元素操作与删除元素操作基本类同,主要差别是取数据元素时指针定位在第i个结点,并且不删除ai结点。 ⑺创建单链表 尾插法建表 voidCreateList_L_Rear(LinkList&L,ElemTypea[],intn) { LinkListp,q;inti; L=(LinkList)malloc(sizeof(LNode)); q=L; for(i=0;i { p=(LinkList)malloc(sizeof(LNode)); p->data=a[i]; q->next=p; q=p; } q->next=NULL; }//CreateList_L_Rear 头插法建表 voidCreateList_L_Front(LinkList&L,ElemTypea[],intn) { LinkListp;inti; L=(LinkList)malloc(sizeof(LNode)); L->next=NULL; for(i=n-1;i>=0;i--) { p=(LinkList)malloc(sizeof(LNode)); p->data=a[i]; p->next=L->next; L->next=p; } }//CreateList_L_Front ⑻单链表的遍历 voidListTraverse_L(LinkListL) { LinkListp=L->next; while(p) {cout< p=p->next; } cout< }//ListTraverse_L ⑼撤消单链表 voidDestroyList_L(LinkList&L) { LinkListp,p1; p=L; while(p) {p1=p; p=p->next; free(p1); } L=NULL; }//DestroyList_L ⑽总结 ①单链表中指针的基本操作 在单链表中除表头指针外,没有任何变量名,访问单链表中的数据只能进行间接访问,所以在单链表的基本操作的实现时,这就离不开指针变量,图2.13展示了指针在操作前后指针值的变化状况。 ② while(p) {p=p->next;} 注意: 在单链表中,p++操作只能指向相邻的下一个存储位置,而不能指向其后继,要使指针指向其后继,只能通过操作: p=p->next;来实现,如图2.14所示, 图2.13 图2.14 2.3.3算法分析 单链表的其它操作中,如求表长、查找元素、创建单链表、遍历单链表和撤销单链表的的时间复杂度均为O(n)。 2.3.4双向链表 1.双向链表的定义 双向链表就是每个结点中含有两个指针域的链表,其中一个指针域存放其前趋结点的地址,另一个指针域存放其后继结点的地址。 其结点结构如图2.15所示。 2.结点结构描述 typedefstructDuNode{ ElemTypedata; structDuNode*prior; structDuNode*next; }DuLNode,*DuLinkList; 3.双向链表的逻辑表示 图2.16(a)表示的是一个带有头结点的空双向链表,2.16(b)表示的是一个带有头结点的非空双向链表。 图2.16双向链表存储示意图 在双向链表中,若指针p指向表中任意一结点,则有: p==p→next→prior==p→prior→next 因而可随意在其上向前或向后移动,使插入和删除等操作变得容易,但占存储空间大。 4.双向链表的基本操作 ⑴初始化⑵插入⑶删除⑷总结 ⑴初始化 voidInitList_DuL(DuLinkList&L) { L=(DuLNode*)malloc(sizeof(DuLNode)); if(! L)exit (1); L->next=NULL; L->prior=NULL; }//InitList_DuL ⑵插入 算法思想 在带头结点的双向链表L中的第i个结点之前插入数据元素e: ①寻找单链表的第i个结点并用指针p指示; ②产生一个新结点,并由指针s指示; ③插入新结点到双向链表中(需要修改四个指针)。 操作示例 图2.17双向链表的插入操作示意图 基本操作语句: ①s->prior=p->prior;//结点p的前驱作为结点s的前驱②p->prior->next=s; //结点s作为结点p的前驱结点的后继③s->next=p; //结点p作为结点s的后继④p->prior=s; //结点s作为结点p的前驱 注意: ⑴在上面语句中,语句④不能放在最前面,否则会丢失数据信息,其它语句的顺序任意。 ⑵应该注意插入位置的特殊性,插入在表尾与插入在表中其操作语句稍有不同。 算法实现 boolListInsert_DuL(DuLinkList&L,inti,ElemTypee) { DuLinkListp,s,q; intj; q=L;j=0; while(q->next&&j if(j! =i-1)returnfalse; s=(DuLNode*)malloc(sizeof(DuLNode)); if(! s)exit (1); s->data=e; if(q->next) {p=q->next; s->prior=p->prior; p->prior->next=s; s->next=p; p->prior=s;} else {q->next=s; s->prior=q; s->next=NULL; } returntrue; }//ListInsert_DuL ⑶删除 操作示例 算法实现 boolListDelete_DuL(DuLinkList&L,inti,ElemType&e) { DuLinkListp; intj; p=L;j=0; while(p->next&&jnext;j++;} if(j! =i)returnfalse; if(p->next) p->next->prior=p->prior; p->prior->next=p->next; e=p->data; free(p); returntrue; }//ListDelete_DuL ⑷总结 ⑴空间性能: 与单链表的结点相比,双向链表的存储密度更低。 ⑵操作复杂性: 与单链表的插入和删除操作相比,在双向链表中的操作变得更复杂,因为还要维护prior指针。 ⑶时间性能: 虽然在双向链表中插入和删除的时间复杂度也均为O(n),但由于双向链表具有对称性,因此在某些情况下(例如对结点p的前后结点进行操作)会提高算法的时间性能。 2.3.5循环链表 1.循环链表的定义 循环链表是另一种形式的链式存储结构,它的基本思想是利用结点的空指针域,在链尾和链头之间增加链接,从而给某些操作带来方便。 一般有两种形式的循环链表,即单向循环链表和双向循环链表,在单向循环链表中,表尾结点的指针域不为空,回指第一个结点,整个链表形成一个环。 在双向循环链表中,除了表尾结点的后继指针域回指第一个结点外,同时表头结点的前驱指针域回指表尾结点,这样在链表中构成了两个环。 与非循环链表类似,循环链表也有带头结点和不带头结点两种形式。 图2.19和图2.20分别是带头结点的单向循环链表和带头结点的双向循环链表。 图2.19 图2.20 2.循环链表的特点 循环链表的特点是: 可及性,即只要给定表中任意一个结点的地址,通过它就可以访问到表中所有的结点,这给结点的访问带来方便。 循环链表的操作与非循环链表基本一致,差别仅在于算法中循环控制条件不再是判断p或p→next是否为空,而是判断它们是否等于头指针(因为循环链表中没有空指针)。 注意: 在循环链表的操作中,工作指针p的初值应与循环控制条件相对应,也就是说,若赋值语句为: p=L;则循环控制表达式为“p->next! =L”;若赋值语句为: p=L->next;则循环控制表达式为“p! =L”。 如果两者没有配合好,则有可能循环体一次也不执行。 3.双向循环链表的操作 ⑴初始化⑵插入⑶删除 ⑴初始化 voidInitList_DuL_C(DuLinkList&L) { L=(DuLNode*)malloc(sizeof(DuLNode)); if(! L)exit (1); L->next=L; L->prior=L; }//InitList_DuL_C ⑵插入 boolListInsert_DuL_C(DuLinkList&L,inti,ElemTypee) { DuLinkListp,s; intj; p=L->next;j=1; while(p! =L&&jnext;j++;} if(j! =i)returnfalse; s=(DuLNode*)malloc(sizeof(DuLNode)); if(! s)exit (1); s->data=e; s->prior=p->prior; p->prior->next=s; s->next=p; p->prior=s; returntrue; }//ListInsert_DuL_C ⑶删除 boolListDelete_DuL_C(DuLinkList&L,inti,ElemType&e) { DuLinkListp; intj; p=L->next;j=1; while(p->next! =L&&jnext;j++;} if(j! =i)returnfalse; e=p->data; p->prior->next=p->next; p->next->prior=p->prior; free(p); returntrue; }//ListDelete_DuL_C 说明: 因为在双向循环链表中没有空指针域,这样就不用考虑插入和删除操作的特例,而使工作指针都定位在插入和删除的位置,不过为了兼顾i≤0的特殊位置,所以在算
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构