线性表是最常用且最简单的一种数据结构。我们经常看到的栈、队列串以及数组等都是线性表,可以说,线性结构在计算机所处理的数据中,广泛而又深刻的存在着。
线性表的逻辑结构:
在数据的非空有限集中,(1)存在唯一的一个被称做“第一个”的数据元素;(2)存在唯一的一个被称做“最后一个”的数据元素;(3)除第一个之外,集合中的每一个数据元素均只有一个前驱;(4)除最后一个之外,集合中的每一个数据元素均只有一个后继。简言之,一个线性表是n个数据结构的有限数列。
线性表的表示形式:
List=(D,R) D={ai/ai∈D0,i=1,2,3·····,n,n≥0}
R={N},N={<ai-1,ai>/ai-1,ai∈D0;i=2,3,·····n}
D0为某个数据对象——数据的子集。
线性表的数学模型的建立及操作(ADT):
ADT List{
数据对象:D={ai|ai∈ElemSet,i=1,2,3,···,n,n≥0}
数据关系R1={<ai-1,ai>/ai-1,ai∈D0;i=2,3,·····n}
基本操作:
InitList(&L)
初始化,构造一个空的线性表
ListLength(L)
求长度,返回线性表中数据元素个数
GetElem(L,i,&e)
取表L中第i个数据元素赋值给e
LocateElem(L,e)
按值查找,若表中存在一个或多个值为e的结点,返回第一个找到的数据元素的位序,否则返回一个特殊值。
ListInsert(&L,i,e)
在L中第i个位置前插入新的数据元素e,表长加1。
DeleteList(&L,i,e)
删除表中第i个数据元素,e返回其值,表长减1。
}//这里只是列举了最基本的那部分
线性表的存储结构:
一、线性表的顺序存储:
特点:用一组地址连续的存储单元依次存储线性表的数据元素,以元素在计算机内“物理位置相邻”来表示线性表中数据元素之间的逻辑关系。
优点:结构简单,结构紧凑,存储效率高,可随机存储。
缺点:插入删除数据元素时要移动数据元素,效率比较低。对于长度变化较大的线性表需要预先分配较大的存储空间或经常扩充线性表,给操作带来不便。
二、线性表的链式存储
结点结构:数据域+指针域
特点:
在内存中用一组任意的存储单元来存储线性表的数据元素,用每个数据元素所带的指针来确定其后继元素的存储位置。
优点:插入删除数据元素时不需要移动数据元素,效率较高,可方便的控制长度变化较大的线性表。
缺点:结构较为复杂,不能随机存储。
数据的运算(算法):
一、顺序存储(顺序表):
基本算法:
1、
顺序表的动态分配顺序存储结构
#define MAXSIZE 100
#define INCREMENT 10
typedef struct {
ElemType *elem;
int length;
int listsize;
}SqList;
SqList L;
2、
顺序表的初始化(构造一个空的线性表)
具体的代码实现算法(C):
二、链式存储(线性链表)
具体的代码实现算法(C):
Status InitList (SqList&L)
{
L.elem=(ElemType*)malloc(MAXSIZE*sizeof(ElemType));
if (!L.elem) exit(OVERFLOW);
L.length = 0;
L.listsize = MAXSIZE;
return OK;
}
3、 在顺序表中插入元素
Status ListInsert_sq(SqList&L,int i,ElemType e)
{
if (i<1 || i>L.length+1) return ERROR;
if (L.length >= L.listsize)
{ newbase=(ElemType*)realloc(L.elem,
(L.listsize+INCREMENT)*sizeof(ElemType));
if (!L.elem) exit(OVERFLOW);
L.elem = newbase;
L.listsize+=LISTINCREMENT;
}
4、 在顺序表中删除元素
Status ListDelete_sq(SqList&L,int i,ElemType&e)
{
if (i<1||i>L.length) return ERROR;
p=&(L.elem[i-1]);
e=*p;
q=L.elem+length-1;//表尾
for (++p;p<=q;++p) *(p-1)=*p;
--L.length;
return OK
}
5、在顺序表中查找元素
int LocateElem_Sq(SqList L, ElemType e)
{ i=1;
while ( i<=L.length&& e != L.elem[i-1]) ++i;
if (i<=L.length) return i; else return 0;
}
具体的代码实现算法(C):
#include<string.h>
#include<ctype.h>
#include<malloc.h> /* malloc()等 */
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
#define LIST_INIT_SIZE 10 /* 线性表存储空间的初始分配量 */
#define LISTINCREMENT 2 /* 线性表存储空间的分配增量 */
typedef struct
{
int *elem; /* 存储空间基址 */
int length; /* 当前长度 */
int listsize; /* 当前分配的存储容量(以sizeof(ElemType)为单位) */
}SqList;
int InitList(SqList *L)
{ /* 操作结果:构造一个空的顺序线性表 */
(*L).elem=(int*)malloc(LIST_INIT_SIZE*sizeof(int));
if(!(*L).elem)
exit(-2); /* 存储分配失败 */
(*L).length=0; /* 空表长度为0 */
(*L).listsize=LIST_INIT_SIZE; /* 初始存储容量 */
return 1;
}
int ListLength(SqList L)
{ /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
return L.length;
}
int LocateElem(SqList L,int e,int(*compare)(int,int))
{ /* 初始条件:顺序线性表L已存在,compare()是数据元素判定函数(满足为1,否则为0) */
/* 操作结果:返回L中第1个与e满足关系compare()的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0。算法2.6 */
int *p;
int 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;
}
int ListInsert(SqList *L,int i,int e) /* 算法2.4 */
{ /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)+1 */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
int *newbase,*q,*p;
if(i<1||i>(*L).length+1) /* i值不合法 */
return 0;
if((*L).length>=(*L).listsize) /* 当前存储空间已满,增加分配 */
{
newbase=(int *)realloc((*L).elem,((*L).listsize+LISTINCREMENT)*sizeof(int));
if(!newbase)
exit(-2); /* 存储分配失败 */
(*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; /* 表长增1 */
return 1;
}
int ListDelete(SqList *L,int i,int *e) /* 算法2.5 */
{ /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
int *p,*q;
if(i<1||i>(*L).length) /* i值不合法 */
return 0;
p=(*L).elem+i-1; /* p为被删除元素的位置 */
*e=*p; /* 被删除元素的值赋给e */
q=(*L).elem+(*L).length-1; /* 表尾元素的位置 */
for(++p;p<=q;++p) /* 被删除元素之后的元素左移 */
*(p-1)=*p;
(*L).length--; /* 表长减1 */
return 1;
}
void main()
{
SqList L;
int e,e0;
int i;
int j,k;
i=InitList(&L);
printf("初始化L后:L.elem=%u L.length=%d L.listsize=%d\n",L.elem,L.length,L.listsize);
for(j=1;j<=5;j++)
i=ListInsert(&L,1,j);
printf("在L的表头依次插入1~5后:*L.elem=");
for(j=1;j<=5;j++)
printf("%d ",*(L.elem+j-1));
printf("\n");
printf("L.elem=%u L.length=%d L.listsize=%d\n",L.elem,L.length,L.listsize);
for(j=1;j<=10;j++)
ListInsert(&L,j,j);
printf("在L的表尾依次插入1~10后:*L.elem=");
for(j=1;j<=10;j++)
printf("%d ",*(L.elem+j-1));
printf("\n");
printf("L.elem=%u L.length=%d L.listsize=%d\n",L.elem,L.length,L.listsize);
k=ListLength(L); /* k为表长 */
for(j=k+1;j>=k;j--)
{
i=ListDelete(&L,j,&e); /* 删除第j个数据 */
if(i==0)
printf("删除第%d个数据失败\n",j);
else
printf("删除的元素值为:%d\n",e);
}
k=ListLength(L);
for(j=1;j<=k;j++)
printf("%d ",*(L.elem+j-1));
}
二、链式存储(线性链表)
1、线性链表的存储结构
typedef struct ListNode{
DataType data;
Struct ListNode *next;
} ListNode, *LinkList;
LinkList L; //L是LinkList类型的变量,表示单链表的头指针
2、建立链表:
void CreateList_L(LinkList&L, int n)
{ LinkList p;
L=(LinkList)malloc(sizeof(LNode)); //L设为头结点
L->next = NULL;
for (int i=n;i>0;--i) {
p=(LinkList)malloc(sizeof(LNode));
scanf("%d",&p->data);
p->next = L->next;
L->next=p;
}
}
3、取元素(按序号查找)
Status GetElem_L(LinkList L, int i,ElemType&e)
{ LinkList p;
p=L->next; intj=1;
while (p && j<i) { p=p->next; ++j; }
if (!p || j>i) return ERROR;
e=p->data;
return OK;
}
4、插入元素:
Status ListInsert_L(LinkList&L,int i,ElemType e)
{ LinkList p,s;
p=L; intj=0;
while (p && j<i-1) { p=p->next; ++j;}
if (!p || j> i-1) return ERROR;
s = (LinkList) malloc( sizeof (LNode));
s->data = e; s->next = p->next;
p->next = s;
return OK;}
5、删除元素:
Status ListDelete_L(LinkList&L, int i, ElemType&e)
{ LinkList p,q;
p=L; intj=0;
while (p->next && j<i-1) { 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;
}
#include<string.h>
#include<ctype.h>
#include<malloc.h> /* malloc()等 */
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
struct LNode
{
int data;
struct LNode *next;
};
typedef struct LNode *LinkList; /* 另一种定义LinkList的方法 */
#define MAXSIZE 100 /* 链表的最大长度 */
typedef struct
{
int data;
int cur;
}component,SLinkList[MAXSIZE];
int InitList(LinkList *L)
{ /* 操作结果:构造一个空的线性表L */
*L=(LinkList)malloc(sizeof(struct LNode)); /* 产生头结点,并使L指向此头结点 */
if(!*L) /* 存储分配失败 */
exit(-2);
(*L)->next=NULL; /* 指针域为空 */
return 1;
}
int ClearList(LinkList L) /* 不改变L */
{ /* 初始条件:线性表L已存在。操作结果:将L重置为空表 */
LinkList p,q;
p=L->next; /* p指向第一个结点 */
while(p) /* 没到表尾 */
{
q=p->next;
free(p);
p=q;
}
L->next=NULL; /* 头结点指针域为空 */
return 1;
}
int ListLength(LinkList L)
{ /* 初始条件:线性表L已存在。操作结果:返回L中数据元素个数 */
int i=0;
LinkList p=L->next; /* p指向第一个结点 */
while(p) /* 没到表尾 */
{
i++;
p=p->next;
}
return i;
}
int GetElem(LinkList L,int i,int *e)
{ /* L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
int j=1; /* j为计数器 */
LinkList p=L->next; /* p指向第一个结点 */
while(p&&j<i) /* 顺指针向后查找,直到p指向第i个元素或p为空 */
{
p=p->next;
j++;
}
if(!p||j>i) /* 第i个元素不存在 */
return 0;
*e=p->data; /* 取第i个元素 */
return 1;
}
int ListInsert(LinkList L,int i,int e) /* 算法2.9。不改变L */
{ /* 在带头结点的单链线性表L中第i个位置之前插入元素e */
int j=0;
LinkList p=L,s;
while(p&&j<i-1) /* 寻找第i-1个结点 */
{
p=p->next;
j++;
}
if(!p||j>i-1) /* i小于1或者大于表长 */
return 0;
s=(LinkList)malloc(sizeof(struct LNode)); /* 生成新结点 */
s->data=e; /* 插入L中 */
s->next=p->next;
p->next=s;
return 1;
}
int ListDelete(LinkList L,int i,int *e) /* 算法2.10。不改变L */
{ /* 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值 */
int j=0;
LinkList p=L,q;
while(p->next&&j<i-1) /* 寻找第i个结点,并令p指向其前趋 */
{
p=p->next;
j++;
}
if(!p->next||j>i-1) /* 删除位置不合理 */
return 0;
q=p->next; /* 删除并释放结点 */
p->next=q->next;
*e=q->data;
free(q);
return 1;
}
int ListTraverse(LinkList L,void(*vi)(int))
/* vi的形参类型为ElemType,与bo2-1.c中相应函数的形参类型ElemType&不同 */
{ /* 初始条件:线性表L已存在 */
/* 操作结果:依次对L的每个数据元素调用函数vi()。一旦vi()失败,则操作失败 */
LinkList p=L->next;
while(p)
{
vi(p->data);
p=p->next;
}
printf("\n");
return 1;
}
void visit(int c)
{
printf("%d ",c);
}
void main()
{
LinkList L;
int e,e0;
int i;
int j,k;
i=InitList(&L);
for(j=1;j<=5;j++)
i=ListInsert(L,1,j);
printf("在L的表头依次插入1~5后:L=");
ListTraverse(L,visit); /* 依次对元素调用visit(),输出元素的值 */
GetElem(L,5,&e);
printf("第5个元素的值为:%d\n",e);
k=ListLength(L); /* k为表长 */
for(j=k+1;j>=k;j--)
{
i=ListDelete(L,j,&e); /* 删除第j个数据 */
if(i==0)
printf("删除第%d个数据失败\n",j);
else
printf("删除的元素为:%d\n",e);
}
k=ListLength(L);
printf("依次输出L的元素:");
ListTraverse(L,visit); /* 依次对元素调用visit(),输出元素的值 */
}