定义:由零个或多个数据元素组成的有限序列
(1.首先它是一个序列,也就是说元素之间是有先来后到的。2.若元素存在多个,则第一个元素无前驱,而最后一个元素无后继。3.list是有限的)- 若将线性表记为(a1,...,ai-1,ai,ai+1,...,an),则称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。
- list的元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表。
0 抽象数据类型(ADT)
定义:一个数学模型以及定义在此数学模型上的一组操作。 (类=属性+方法)
- 数据类型:一组性质相同的值的集合及定义在此集合上的一些操作的总称,eg.:整型,浮点型。。。)
描述
ADT 抽象数据类型名
Data 数据元素之间逻辑关系的定义 Operation 操作 endADT
ADT ListData 线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。Operation InitList(*L):初始化,建立一个空的线性表L。 ListEmpty(L):判断 ClearList(*L):清空 GetElem(L,i,*e):将线性表L中的第i个位置元素值返回给e。 LocateElem(L,e):在线性表L中查找于给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。 ListInsret(*L,i,e):第i个位置插入新元素e ListDelete(*L,i,*e):。。。 ListLength(L):返回长度 endADT代码实现AUB
void unionL(List *a, List b){ int len_a, len_b, i; ElemType e; len_a=ListLengh(*a); len_b=ListLengh(b); for(i=1;i<=len_b;i++) { GetElem(b,i,&e); if( !LocateElem(*a,e) { ListInsert(a, i ,e); } }}
1 线性表的顺序存储结构
结构代码(有点像数组)
#define MAXSIZE 20typedef int ElemType;typedef struct{ ElemType data[MAXSIZE]; int length; // 线性表当前长度} Sqlist;
由此可见,顺序存储结构封装需要三个属性:
- 存储空间的起始位置
- 线性表的最大存储容量:数组的长度
- 线性表当前长度:Length
注意:数组的长度是存放线性表的存储空间的总长度,一般初始化后不变(动态数组可变)。而线性表的当前长度是线性表中元素的个数,是会变化的。
地址计算方法
数组从0开始计算,线性表从1开始。
假设ElemType占用的是c个存储单元(字节),线性表中第i+1个元素和第i个数据元素的存储位置的关系是:LOC(ai+1)= LOC(ai) + c1.1 插入操作
算法思路
- 如果插入位置不合理,抛出异常
- 如果线性表长度大于等于数组长度,抛出异常或者动态增加数组容量
- 从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置
- 将要插入的元素填入位置i处
- 线性表长+1
Status ListInsert(SqList *L, int i, ElemType e) //status表示函数的类型,返回的是函数的状态{ int k; if(i<1||i>L->length+1){ return error; } if(L->length==MAXSIZE){ return error; } if(ilength)//不在表尾 { for(k=L->length-1;k>=i-1;k--){ L->data[k+1]=L->data[k]; } } L->data[i-1]=e; L->length++; return OK;}
1.2 删除操作
- 删除L的第i个数据元素,并用e返回其值
Status ListInsert(SqList *L, int i, ElemType e) //status表示函数的类型,返回的是函数的状态 { int k; if(L->length==0) { return error; } if(i<1||i>L->length){ return error; } *e= L->data[i-1];//因为数组是从0开始索引的,所以线性表中的i-1号文章才是 if(ilength )//不在表尾 { for(k=i; k < L->length; k++){ L->data[k-1]=L->data[k]; } } L->length--; return OK;}
1.3 线性表顺序存储结构的优缺点
存、读数据时,不管那个位置,时间复杂度都是O(1),而在插入或删除时,时间复杂度为O(n)(在表尾时,时间复杂度为O(1)).
这就说明它比较适合元素个数比较稳定,不经常插入和删除元素,而更多时存取数据。
优点
- 无需为表示表中元素之间的逻辑关系而增加额外的存储空间。
- 可以快速地存取表中任意位置的元素
缺点
- 插入和删除需要移动大量元素
- 存储空间利用率低,预先分配内存可能造成存储空间浪费。
2. 线性表的链式存储结构
(链式存储)每个数据需要两个存储空间
把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或链。这两部分信息组成数据元素称为存储映像,称为节点(Node)。- n个结点链接成一个链表,即为线性表(a1,a2,a3,...,an)的链式存储结构。
- 因为此链表的每个结点中只包含一个指针域,所以叫单链表。
- 头指针是链表的必要元素
- 头节点(在单链表的第一个结点之前附设一个结点,它没有直接前驱,称之为头结点。)
- 有了头节点,对第一元素节点前插入节点和删除第一结点起操作与其它结点的操作就统一了。(头结点不是单链表的必要元素)
结构指针描述单链表
typedef struct Node
{ ElemType data; struct Node Next; } Node; typedef struct Node LinkList;
2.1 单链表的读取
获取链表第i个数据的算法思路
- 声明一个结点p指向第一个结点,初始化j从1开始
- when j<i, 遍历链表,让p后移,不断指向下一个结点,j+1
- 若到链表末尾,p为空,说明第i个元素不存在
- 否则查找成功,返回结点p的数据
Status GetElem(LinkList L, int i, ElemType *e){ int j; LinkList p;// p是个指针 p=L->next; j=1; while(p && j< i ) { p=p->next; j++; } if(!p||j>i) { return ERROR; } *e=p->data; } return OK;}
2.2 单链表的插入
代码实现在L中的第i个位置之前插入新的数据元素e, L的长度加一Status ListInsert(LinkList *L, int i, ElemType e){ int j; LinkList p,s; p=*L; j=1; while(p&&jnext; j++; } if(!p||j>i){ return ERROR; } s=(LinkList)malloc(sizeof(Node));//用sizeof避免了不知道结点数据类型的情况 s->data=e; s->next=p->next; p->next=s L++; return OK;}
2.3 单链表的整表创建
- 头插法
- 尾插法
2.4 单链表的整表删除
就是在内存中将其释放,留给其他程序使用。
C代码实现Status ClearList(LinkList *L) { LinkList p,q; p=(*L)->next;
while(p){ q=p->next; free(p); p=q; } (*L)->next=NULL;return OK;
}
3. 静态链表
define MAXSIZE 1000
typedef struct
{ ElemType data; //数据 int cur; //游标(Cursor) } Component,StaticLinkList[MAXSIZE];