线性表
1.定义
由零个或者多个数据元素组成的有限序列,零个元素的是个空表。
- 首先是个序列,有先来后到。
- 存在多个元素,第一个元素没有前驱,也就是没有前一个元素,最后一个元素没有后继,中间的元素有且仅有一个前驱以及后继。
- 线性表强调是有限的,没有无限长度的线性表。
1.2 抽象数据类型
定义:是指一个数学模型以及定义在该模型上的一组操作,仅仅取决与它的一组逻辑特性,而与其在计算计算机内部如何表示与实现无关。
例如:1+2=3,这样的操作在不同CPU上的处理是不同的,但是由于其定义的数学特性相同,所以在计算机编程者看来是一致的。
数据类型的定义:指一组性质相同的值的集合以及定义在此集合上的一些操作的总称。整型,浮点型,字符型。
C语言中,按照取值分为两种大类的:
- 不可分解的基本类型–原子类型,例如整型,浮点型,字符型
- 可分解的类型,由若干个类型组合而成,类似于结构体,数组等等
ADT 抽象数据类型名
{
Data:
数据元素之间逻辑关系的定义;
Operation:
操作1;
操作2;
...
}
2.线性表
2.1存储结构
线性存储结构
#define MAXSIZE 20
typedeef int ElemType;
typedef struct{
ElemType data[MAXSIZE];
int leength;
}SqList;
顺序存储结构封装需要三个属性:
- 存储空间的起始位置,数组data的位置即线性表存储空间的存储位置。
- 线性表的最大存储容量:数组的长度MaxSize
- 线性表当前的长度:length
地址计算方法:
数组从0开始计算,假设ElemType占用的是C个存储单元(字节),那么线性表中第i+1个元素和第i个数据元素的存储位置关系是:Loc(ai+1)=Loc(ai)+C
2.2线性表顺序存储
定义线性表的结构,对象的长度,表长,以及表内元素的类型
typedef struct{
ElemType *elem;
int length;
int listsize;
}List;
定义线性表的方法
1.InitList(&L)
/*操作结果:构造一个空的线性表*/
2.DestroyList(&L)
/*初始条件:线性表L已存在*/
/*操作结果:销毁线性表L*/
3.ClearList(&L)
/*初始条件:线性表L已存在*/
/*操作结果:将L重置为空表*/
4.ListEmpty(L)
/*初始条件:线性表L已存在*/
/*操作结果:若L为空表,则返回TRUE,否则返回FALSE*/
5.ListLength(L)
/*初始条件:线性表L已存在*/
/*操作结果:返回L中数据元素个数*/
6.GetElem(L,i,&e)
/*初始条件:线性表L已存在,1≤i≤LIST_SIZE*/
/*操作结果:用e返回L中第i个数据元素的值*/
7.LocatElem(L,e,compare())
/*初始条件:线性表L已存在,compaer()是数据元素判断函数*/
/*操作结果:返回L中第1个与e满足关系compaer()的数据元素的位序。若这样的数据元素不存在,则返回0*/
8.PriorElem(L,cur_e,&pre_e)
/*初始条件:线性表L已存在*/
/*操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义*/
9.NextElem(L,cur_e,&next_e)
/*初始条件:线性表L已存在*/
/*操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后续,否则操作失败,next_e无定义*/
10.ListInsert(&L,i,e)
/*初始条件:线性表L已存在*/
/*操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1*/
11.ListDelete(&L,i,&e)
/*初始条件:线性表L已存在*/
/*操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1*/
2.2.1 GetElem()
typedef int Status;
Status GetElem(SqList L, int i ,ElemType *e)
{
if (L.length==0 || i<1 || i>L.length)
{
return 0;
}
*e=L.data[i-1];
return 1;
}
2.2.2 ListInsert()
Status ListInsert(List &L,int i,ElemType e)
{
int k;
if(L->length==MAXSIZE)
{
return 0;
}
if(i<1 || i>L->length+1)
{
return 0;
}
if(i<=L->length)
{
for(k=L->length-1;k>=i-1;k--)
{
L->data[k+1]=L->data[k];
}
}
L-?length[i-1]=e;
L->legnth++;
return 1;
}
2.2.3 ListDelete()
Status ListDeletet(List &L,int i,ElemType e)
{
int k;
if(L->length==0)
{
return 0;
}
if(i<1||i>L->length)
{
return 0;
}
if(i<L->length)
{
for(k=i;k<L->length;k++)
{
L->data[k-1]=L->data[k];
}
L->length--;
}
return 1;
}
2.3 链式存储方式
结构定义:
把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域,指针域中存储的信息成为指针或者链。两部分信息组成数据元素成为存储映像,称之为节点。只包含一个指针域的,称之为单链表,两个指针域称之为双链表。
单链表示意图:
结构定义:
typedef struct Node
{
ElemType data; //数据域
struct Node* Next; //指针域
}Node;
Typedef struct Node* LinkList;
P->Next->data //即获取下一个节点的数据域。
2.3.1 GetElem()
Status GetElem(LinkList L,int i ,ElemType *e)
{
int j;
LinkList p;
p=L->next;
j=1;
while(p && j<i)
{
p=p->next;
++j;
}
if(!p || i>i)
{
return 0;
}
*e=p->data;
return 1;
}
2.3.2 ListInsert()
Status GetElem(LinkList *L,int i ,ElemType e)
{
int j;
LinkList p,s;
p=*L;
j=1;
while(j<i)
{
p=p->next;
j++;
}
if( !p || j>i )
{
return 0;
}
s=(LinkList)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
2.3.3 CreateListhead()
void CreateListhead(Linklist *L ,int n)
{
LinkList p;
int i;
srand(time(0)); //初始化随机种子
*L=(LinkList)malloc(sizeof(Node));
(*L)->next=NULL;
for(i=0;i<n;i++)
{
p=(LinkList)malloc(sizeif(Node));
p->data=rand()%100+1;
p->next=(*L)->next;
(*L)->next=p;
}
}
2.3.4 CreateListTail()
void CreateListTail(LinkList *L,int n)
{
LinkList p,r;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));
r=*L;
for(i=0;i<n;i++)
{
p=(Node *)malloc(sizeof(Node));
p->data=rand()%100+1;
r->next=p;
r=p;
}
r-next=NULL;
}
2.3.5 ClearList()
Status ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
return 1;
}
3. 优缺点对比
- 存储分配方式
- 时间性能
- 空间性能
3.1 存储分配方式
顺序存储:采用一段连续的存储单元依次存储线性表的数据元素。
单链表:采用链式存储结构,用一组任意的存储单元存放线性表的元素。
单链表可以在任意空余空间进行存储,因为指针域中指向的是地址,可以有效利用碎片的空间,但是由于链表将空间分为指针域和数据域,所以在存储上需要的空间比顺序存储来得大。
3.2 时间性能
查找:
顺序链表:时间复杂度O(1)
单链表:时间复杂度O(n)
插入和删除:
顺序链表:需要平均移动表长一半的元素,时间复杂度为O(n)
单链表:得到某个位置的指针后,插入和删除需要的时间复杂度仅仅为O(1)
3.3 空间性能
顺序存储结构:需要预分配存储空间,预分配和实际大小存在一定的差距。
单链表:不需要分配存储空间,有就可以分配,元素个数不受限制。
单链表可以在任意空余空间进行存储,因为指针域中指向的是地址,可以有效利用碎片的空间,但是由于链表将空间分为指针域和数据域,所以在存储上需要的空间比顺序存储来得大。
4. 结论
如果需要频繁查找,较少进行插入和删除操作,则采用顺序表的存储结构,反之亦然。