之前我们讲了线性表, 本篇来阐述下线性表的顺序存储——顺序表.
定义
线性表的顺序存储又称为顺序表, 它是用一组地址连续的存储单元依次存储线性表中的数据元素. 逻辑上相邻的两个数据元素在物理位置上同样相邻.
规律
顺序表中逻辑顺序与物理顺序相同
L = (a1, a2, ...,ai ,ai+1 ,an ..., )
其中在逻辑上相邻的两个数据元素,在顺序表中也存放在相同的存储单元当中,每一个小格子就代表一个存储单元。
注 线性表中的元素的位序是从
1
开始, 而数组中元素下标是从0
开始的
若线性表存储的起始位置为Loc(A)
, sizeof(ElemType)
为每个数据元素所占用的存储空间大小, 那么根据这一特点,我们可以计算出每一个数据元素存储的地址。
第一个元素的地址是 LOC(A)
,计算第二个元素的地址就可以用第一个元素的地址加上第一个数据元素 所消耗的存储空间,用 sizeof
可求得该数据元素所消耗的存储空间大小。这里需要注意的一点是,n
与 MaxSize
是有含义上的不同的,其中 代表的是顺序表中最后一个数据元素
,而 MaxSize 代表的是数组的最后一个存储单元
。
顺序表的两种实现方法
顺序表可以用数组来实现。根据数组的两种分配方式,也就有两种描述顺序表的方法。分别是静态描述分配顺序表
的方法和动态描述分配顺序表
的方法。首先来看数组静态分配时时如何描述一个顺序表的。
- 静态描述分配顺序表
#define MaxSize 50 typedef struct{ ElemType data[MaxSize]; int length; }SqList;
这就是描述顺序表的语句。第一句是定义了一个宏,也就是定义线性表的最大长度为 50,同时这也是数组的最大容量。接着定义了一个结构体。结构体就是把多个基本数据类型组合到一起构成一个新的数据类型。它的定义语句是用 typedef struct
,然后用大括号圈起来所要包含的基本数据类型
。最后 SqList
代表着该结构体的名字。
在静态分配时数组的大小和空间已固定, 空间一旦占满, 再加入新数据将会产生溢出, 从而导致程序崩溃.
- 动态描述分配顺序表
#define MaxSize 50 typedef struct{ ElemType *data; //指示动态分配数组的指针 int MaxSize, length; //数组的最大容量和当前个数 }SqList;
这是动态分配时描述顺序表的语句,观察发现这里用的是指针
,指针是存放一个存储单元地址的。顺序表根据第一个数据元素的地址和数据元素的大小,就可以计算出任意数据元素的位置。那么只要定义了第一个数据元素的指针
,就可以描述整个顺序表。但是这一个变量它仅仅是一个地址,而没有确切的空间,所以在使用时,需要动态的申请空间。怎样动态的申请空间呢?:
- C的初始动态分配语句
L.data = (Elemtype*)malloc(sizeof(ElemType)*InitSize);
- C++的初始动态分配语句
L.data = new ElemType[InitSize];
动态分配时, 一旦数据空间占满, 就另外开辟一块更大的存储空间, 代替原来的存储空间.
顺序表上基本操作实现
定义
#define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef int ElemType; typedef int Status; //----- 顺序表的顺序存储表示 ----- #define LIST_INIT_SIZE 100 // 存储空间的初始分配量 #define LISTINCREMENT 10 // 存储空间的分配增量 typedef struct { ElemType *elem; // 存储空间的基址 int length; // 表长 int size; // 存储容量 int increment; // 扩容时,增加的存储容量 } SqList; //顺序表
初始化顺序表
Status InitSqlist(SqList &L){ L.elem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if(!L.elem) exit (OVERFLOW); L.length = 0; L.size = LIST_INIT_SIZE; L.increment = LISTINCREMENT; return OK; }
判顺序表是否为空表
Status ListEmpty(SqList L){ if (L.length == 0) return OK; else return ERROR; }
插入操作
Status ListInsert_Sq(SqList &L, int i, ElemType e){ if(i < 1 || i > L.length + 1) return ERROR; if(L.length >= L.size) return ERROR; for(int j = L.length; j >= i; j--) L.elem[j] = L.elem[j - 1]; L.elem[i - 1] = e; L.length++; return OK; }
删除元素
Status ListDelete_Sq(SqList &L, int i, ElemType &e){ if(i < 1 || i > L.length) return ERROR; e = L.elem[i - 1]; for(int j = i; j < L.length; j++) L.elem[j - 1] = L.elem[j]; L.length--; return OK; }
输出顺序表
void OutList_Sq(SqList L) { int i; ElemType e; if(ListEmpty(L)) { printf("这是一个空表!"); } else { printf("顺序表为:"); for(i = 0; i < L.length; i++) { printf("%6d", L.elem[i]); } printf("\n"); } }
测试
int main() { SqList L; int cord,i; ElemType a; printf("第一次使用必须初始化!\n"); do { printf("\n 主菜单 \n"); printf(" 1 初始化顺序表 "); printf(" 2 插入一个元素 "); printf(" 3 删除一个元素 "); printf(" 4 结束程序运行 "); printf("\n-------------------------------------------------------------------\n"); printf("请输入您的选择( 1, 2, 3, 4)"); scanf("%d", &cord); printf("\n"); switch(cord) { case 1: InitSqlist(L); OutList_Sq(L); break; case 2: printf("\n请输入要插入的插入位置和数据元素(如:3 20)"); scanf("%d%d", &i, &a); ListInsert_Sq(L, i, a); printf("\n插入%d元素之后的", a); OutList_Sq(L); break; case 3: printf("\n请输入要删除的数据元素的位置(如:3)"); scanf("%d", &i); ListDelete_Sq(L, i, a); printf("\n删除第%d个位置的元素之后", i); OutList_Sq(L); break; case 4: exit(0); default : break; } } while(cord <= 4); return 1; }