线性表(List):零个或多个数据元素的有限序列;
线性表可以记为(a1, a2, …, ai-1, ai, ai+1, .., an),表中ai-1领先于ai,称ai-1为ai的直接前驱元素;ai领先于ai+1,称ai+1为ai的直接后继元素;
当i=1,2,…,n-1时,ai有且仅有一个直接后继;当i=2,3,…n时,ai有且仅有一个直接前驱;
线性表中元素的个数n(n>=0)定义为线性表的长度;n=0时,线性表为空表;
线性表中每个元素都有确定的位置,ai是第i个元素,i称为ai元素的位序;
线性表的抽象数据类型:
ADT List
Data
(a1, a2, …, ai-1, ai, ai+1, .., an)
Operation
InitList(*L); //初始化操作,建立空表L;
ListEmpty(L); //判断表L是否空表;
ListLength(L); //获取表L长度;
ClearList(*L); //清空表L;
GetElem(L,i,*e); //返回表L的第i个位置元素给e;
LocateElem(L,e); //返回表L中值与e相等的元素位序;
ListInsert(*L,i,e); //在表L的第i个位置插入新元素e;
ListDelete(*L,i,*e); //删除表L的第i个位置元素并用e返回其值;
endADT
顺序表
线性表的顺序存储结构,是指用一段地址连续的存储单元依次存储线性表的数据元素,也称作顺序表;
C语言中可以使用一维数组来实现顺序存储结构;
#define MAXSIZE 100 //存储空间的最大长度;
typedef int ElemType; //数据元素的数据类型;
typedef struct
{
ElemType data[MAXSIZE]; //存储数据元素的数组;
int length; //线性表当前长度;
}SqList;
顺序表中的地址计算:
由于顺序表的存储空间是连续的,假设每个数据元素需要占m个存储单元,则:
LOC(ai+1) = LOC(ai) + m
LOC(ai) = LOC(a1) + m*(i-1)
也就是说,获取任意一个元素的地址,时间复杂度都是O(1),所以顺序存储结构也称为随机存取结构;
顺序表的代码描述:
#include <stdio.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SeqList;
int InitList(SeqList *L);
int ListLength(SeqList L);
int ListEmpty(SeqList L);
int ClearList(SeqList *L);
int PrintList(SeqList L);
int GetElem(SeqList L, int pos, ElemType *e);
int LocateElem(SeqList L, ElemType e);
int ListInsert(SeqList *L, int pos, ElemType e);
int ListDelete(SeqList *L, int pos, ElemType *e);
//初始化操作,建立空表L;
int InitList(SeqList *L)
{
L->length = 0;
return OK;
}
// 判断表L是否空表;
int ListEmpty(SeqList L)
{
if (0 == L.length) {
return TRUE;
} else {
return FALSE;
}
}
// 获取表L长度;
int ListLength(SeqList L)
{
return L.length;
}
// 清空表L;
int ClearList(SeqList *L)
{
int i;
for (i = 0; i < MAXSIZE; i++)
{
L->data[i] = 0;
}
L->length = 0;
return OK;
}
// 打印表L;
int PrintList(SeqList L)
{
if (0 == L.length) {
printf("空表\n");
} else {
int i;
for (i = 0; i < L.length; i++)
{
printf("%d ", L.data[i]);
}
printf("\n");
}
return OK;
}
// 返回表L的第pos个元素给e;
int GetElem(SeqList L, int pos, ElemType *e)
{
if (pos < 1 || pos > L.length) {
printf("非法位序!!!\n");
return ERROR;
} else {
*e = L.data[pos - 1];
return OK;
}
}
// 返回表L中值与e相等的元素位序;
int LocateElem(SeqList L, ElemType e)
{
int i;
int pos = -1;
if (L.length > 0)
{
for (i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
pos = i + 1;
break;
}
}
}
return pos;
}
// 在表L的第pos个位置插入新元素e;
int ListInsert(SeqList *L, int pos, ElemType e)
{
if (MAXSIZE == L->length) {
printf("顺序表已经满了!!!\n");
return ERROR;
}
if (pos < 1 || pos > L->length + 1) {
printf("非法位序不能插入数据!!!\n");
return ERROR;
}
int i;
// 插入数据的位置不在表尾;
if (pos <= L->length)
{
// 从第pos个元素到最后一个元素, 全部向后移动一位;
for (i = L->length - 1; i >= pos - 1; i--)
{
L->data[i + 1] = L->data[i];
}
}
L->data[pos - 1] = e;
L->length++;
return OK;
}
// 删除表L的第pos个元素并用e返回其值;
int ListDelete(SeqList *L, int pos, ElemType *e)
{
if (0 == L->length) {
printf("顺序表是空的!!!\n");
return ERROR;
}
if (pos < 1 || pos > L->length) {
printf("非法位序不能删除数据!!!\n");
return ERROR;
}
*e = L->data[pos - 1];
int i;
// 删除的数据位置不在表尾;
if (pos < L->length)
{
// 从第pos+1个元素到最后一个元素, 全部向前移动一位;
for (i = pos - 1; i <= L->length - 1; i++)
{
L->data[i] = L->data[i + 1];
}
}
L->length--;
return OK;
}
int main()
{
SeqList sList;
InitList(&sList);
int len = ListLength(sList);
printf("len = %d\n", len);
if (TRUE == ListEmpty(sList)) {
printf("是个空表!!!\n");
} else {
printf("不是空表.\n");
}
// 赋值;
len = 10;
int i;
for (i = 0; i < len; i++)
{
sList.data[i] = i + 1;
}
sList.length = len;
PrintList(sList);
// 取值;
int ret, val;
ret = GetElem(sList, 6, &val);
if (OK == ret) {
printf("val = %d\n", val);
} else {
printf("GetElem ERROR!!!\n");
}
// 插入;
ret = ListInsert(&sList, 6, -1);
if (OK == ret) {
PrintList(sList);
} else {
printf("ListInsert ERROR!!!\n");
}
// 删除;
ret = ListDelete(&sList, 3, &val);
if (OK == ret) {
printf("Delete val = %d\n", val);
PrintList(sList);
} else {
printf("ListDelete ERROR!!!\n");
}
return 0;
}
顺序表插入操作的复杂度:
- 对于顺序表L=(a1, a2, …, ai-1, ai, ai+1, .., an):
在第1个位置插入新元素时,需要将n个元素全部往后移动一位;
在第2个位置插入新元素时,需要将n-1个元素全部往后移动一位;
……
在第n个位置插入新元素时,需要将1个元素往后移动一位;
在第n+1个位置(表尾)插入新元素时,不需要移动元素;
根据概率原理,在每个位置插入元素的概率是相同的,平均移动次数为 n/2;
因此时间复杂度为O(n);
顺序表删除操作的复杂度:
- 对于顺序表L=(a1, a2, …, ai-1, ai, ai+1, .., an):
删除第1个元素时,需要将n-1个元素全部往前移动一位;
删除第2个元素时,需要将n-2个元素全部往前移动一位;
……
删除第n-1个元素时,需要1个元素往前移动一位;
删除第n个元素时,不需要移动元素;
根据概率原理,在每个位置插入元素的概率是相同的,平均移动次数为 (n-1)/2;
因此时间复杂度也为O(n);
顺序表的优缺点:
- 优点:可以快速存储表中任一位置的元素;没有为了表示各元素之间的逻辑关系而增加额外的存储空间;
- 缺点:插入和删除操作需要移动大量元素;表长度变化较大时难以确定存储空间的容量;