线性表的链式表示
使用一组任意的存储单元存储线性表的数据元素(这些存储单元可以是连续也可以是不连续的)。
常见的链式表:单链表、静态链表、循环链表、双向链表。
链表的存储方式和特点
我们修改一下上一篇文章的例子:假如现在是新生入校,按照新生的先来后到编号1-6,先到的同学可以随意选择床铺,
但是需要记住下一位同学的床铺号(1舍友记住2舍友床铺号,2舍友记住3舍友床铺号,依次类推)。
此时无形的就把同学构成了一条链表。
链表的存储方式也于此类似,下面讨论一下链表的特点:
(1)数据元素的逻辑顺序和物理顺序不一定相同。
(2)在查找数据元素时,必须从头指针开始依次查找,表尾的指针域指向NULL。
(3)在特定的数据元素之后插入或删除元素,不需要移动数据元素,因此时间复杂度为 O(1)。
(4) 存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素。
(5)长度不固定,可以任意增删。
在来看下链表中的结点:结点是数据元素信息和指示存储位置信息这两部分的组成,这两部分信息也称作数据元素的存储映像。
结点包括了两个域:存储数据元素data是数据域,用来存储位置信息next是指针域。
注意:为了增强程序可读性,通常在链表第一个结点之前我们会加一个头结点,
头结点的数据域可以不存储任何信息,也可以存储线性表的长度等附加信息。
另外,有头结点的好处是便于在第一个结点做插入和删除时不需要做特殊的处理。
下面来看单链表
单链表的存储结构
//----线性表的单链表存储结构(课本28页)---
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
单链表的基本操作(C语言)
基本操作包括了教材19页的12个操作,其中的操作也实现了教材中的2.8,2.9,2.10算法。
C语言代码:
- 头文件请参考顺序表文章
在给出基本操作代码:
//----单链表的存储结构(课本28页)--- typedef struct LNode{ ElemType data; struct LNode *next; }LNode,*LinkList; //-----线性表的链式 之单链表的基本操作(12个) */ //1, Status InitList(LinkList *L) { /* 操作结果:构造一个空的线性表L(默认带头结点) */ *L =(LinkList)malloc(sizeof(LNode)); //产生头结点,并初始化给头指针。 if(!*L) //分配空间失败 exit(OVERFLOW); (*L)->next =NULL;//头结点的指针域为空。 return OK; } //2 Status DestroyList(LinkList *L) { /* 初始条件:线性表L已存在。操作结果:销毁线性表L */ LinkList p; while(*L){ p=(*L)->next; free(*L);//从头结点释放资源 *L=p; } return OK; } //3, Status ClearList(LinkList L) /* 注意L不改变,不使用&L */ { /* 初始条件:线性表L已存在。操作结果:将L重置为空表 */ LinkList p,q; p=L->next; // p指向首元结点 while(p){ //释放第一个结点及以后的结点资源 q =p->next; free(p); p =q; } L->next =NULL; //头指针指向的头结点next域为空 return OK; } //4, Status ListEmpty(LinkList L) { /* 初始条件:线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */ if(L->next) // 不为空 return FALSE; else return TRUE; } //5, int ListLength(LinkList L) { /* 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 */ int i=0; //计数器 LinkList p=L->next; // 使p指向第一个结点 while(p){ //遍历链表 i++; p=p->next; //使p指向下一个结点 } return i; } //6,书中算法2.8(29页) Status GetElem(LinkList L,int i,ElemType *e) { /* L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */ int j=1; // 计数器 LinkList p=L->next; // 使p指向第一个结点 while(p && j next; j++; } //判断一下 if(!p || j>i) // 第i个元素不存在(前者判断i的值大于表长,后者判断i的值小于1) return ERROR; *e=p->data; // 取第i个元素 return OK; } //7, int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) { /* 初始条件: 线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) */ /* 操作结果: 返回L中第1个与e满足关系compare()的数据元素的位序。 */ /* 若这样的数据元素不存在,则返回值为0 */ int i=0; LinkList p=L->next; // 使p指向第一个结点 while(p) { i++; if(compare(p->data,e)) // 满足关系的数据元素,返回位序 return i; p=p->next; } return 0; } //8, Status PriorElem(LinkList L,ElemType cur_e,ElemType *pre_e) { // 初始条件: 线性表L已存在 // 操作结果: 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,返回OK;否则操作失败,pre_e无定义,返回OVERFLOW。 LinkList p =L->next; //p指向第一个结点,当前结点 LinkList q; while(p->next){ //从第二个结点遍历判断,保证了当前结点是别的结点前驱【在顺序表操作中 j =2】 q =p->next; //q指向当前p的后继【第一个结点没有前驱,不用判断了,从第二个结点】 if(q->data ==cur_e){ //当前结点指针p的后继q的data域 与cur_e 相同 *pre_e =p->data; //p的data域必定是cur_e的前驱 return OK;