顺序表的概念和特点
-
直接将线性表的逻辑结构映射到存储结构上。
-
基地址LOC(a1):顺序表中第一个数据元素a1的存储位置,也是线性表的起始位置。
-
线性表的顺序存储结构或顺序映像,称这种存储结构的线性表为顺序表。
-
顺序表的特点:为表中相邻的元素ai和ai+1赋以相邻的存储位置LOC(ai)和LOC(ai+1)。
-
以元素在计算机内“物理位置相邻”来表示线性表中的数据元素之间的逻辑关系。
-
每一个数据元素的存储位置都和线性表的起始位置相差一个 和数据元素在线性表中的位序成正比的常数 。
-
线性表的顺序存储结构是一种随机存取的存储结构。
-
高级程序设计语言中的数组类型也有随机存取的特性,因此,通常都用数组来描述数据结构中的顺序存储结构。
-
因为线性表的长度可变,所需最大存储空间随问题不同而不同,所以在C语言中可用动态分配的一维数组进行描述。
顺序表的实现
-
线性表的动态分配顺序存储结构
#define MAXSIZE 100 // 线性表存储空间的初始分配量 typedef struct { ElemType data[MAXSIZE]; // 存放顺序表的数组 int length; // 当前长度 }SqList;
-
// 初始化顺序表 void InitList(SqList &L){ L=(SqList *) malloc(sizeof(SqList)); // L指的是SqList *的指针,指向名为SqList的结构体 // malloc(sizeof(SqList))指的是向系统内存申请大小为sizeof(SqList)的内存地址 // (SqList *)指的是把这个地址强制转化为SqlList *的指针 L->length = 0; // 将当前线性表长度置0 } // 时间复杂度为O(1)
-
创建顺序表:已知数组a[n],数组中的元素依次插入到顺序表L中,从而建立顺序表。
// 建立顺序表(插入数据) void CreateList(SqList *&L,ElemType a[],int n) { // 由a中的n个数据元素 L=(SqList *) malloc (sizeof(SqList)); // 分配存放线性表的空间 for(int i=0; i < n; i++) { // 遍历数组元素,放置数据元素。 L->data[i] = a[i]; // L->data[i] 顺序表L的第i个元素 } L->length = n; // 最后将顺序表L的表长设为n; }
-
顺序表的插入:在表中的第i个位置上插入一个值为x的新元素,插入后使得原表长为n的表变成表长为n+1的表,i的取值范围为1≤i≤n+1。
仅当在表的最后插入(即插入位置为i=n+1)时,才无需移动结点,直接将x插入表的末尾即可。
如果在表的中间插入元素,则需要将位置在n,n-1,……,i的结点依次往后移一个位置到n+1,n,……,i+1处。
步骤为:
① 将an ~ ai按从后往前的顺序向后移动,为新元素让出第i个位置。
② x置入空出的第i个位置。
③ 修改表长。
bool InsertList(SqList &L, int i, ElemType x) { int j; if(L->length == MAXSIZE - 1) // 表空间已满,不能插入 return Overflow; if(i < 1 || i > L->length + 1) // 插入的位置i的值不合规时 return Error; for(j = L->length; j>=i; j--) { // 从表中最后一个元素开始往前 L->data[j+1] = L->data[j]; // 依次往后移动一个位置 即将第j个位置的值赋给第j+1个位置。 } L->data[i] = x; // 将需要插入的元素x的值,赋给L的第i个位置 L->length++; // L的长度加1 return TRUE; // 插入成功 }
注:插入算法的时间复杂度分析:时间主要消耗在数据元素的移动上,有n+1个位置可以插入。
最好的情况:如果插入的元素在线性表的最后一个位置(L的表尾),则不需要移动元素,在最后一个位置插入元素即可。时间复杂度为O(1).
最坏的情况:如果插入的元素在线性表的第一个位置(L的表头),则需要移动n个元素,时间复杂度为O(n)
平均时间复杂度为O(n)。
-
顺序表的删除:指将表中第i个元素从线性表中删除,删除后使原表长为n的线性表变为表长为n-1的线性表,i的取值范围为1≤i≤n。操作如下:
① 将ai+1 ~ an 从前往后依次向前移动,覆盖原来位置的数据元素
② 将线性表L的长度L->length减1。
bool DeleteList(SqList &L,int i, Elemtype &e){ if( i < 1 || i > L->length){ // 检查是否为空表以及删除的位置是否合法。 return Error; } *e = L->data[i]; // 用*e 存储待删除的元素值,将待删除的元素放入临时空间中, for(j=i;j<=L->length-1;j++) { L->data[j] = L->data[j+1]; // 向前移动,覆盖前面的数据 } L->length--; return Ture; }
注:删除算法的时间复杂度分析:时间主要消耗在数据元素的移动上,移动元素的个数取决于删除元素的位置。
最好的情况:当删除的元素位于表尾时(即i=n),无需移动元素,时间复杂度为O(1)。
最坏的情况:当删除的元素位于表头时(即i=1),需要移动除表头之外的所有元素,时间复杂度为O(n)。
平均时间复杂度为O(n)。
-
顺序表的查找:在顺序表L中查找第一个元素值等于e的元素,并返回其位序。步骤如下:
① 从第一个元素起,依次和e相比较,若找到与e相等的元素L->data[i],则查找成功,返回该元素的序号i+1。
② 若查遍整个顺序表都没有找到,则查找失败,返回0。
int LocateElem(SqList L, Elemtype e){ for(i=0; i<L->length;i++) { if(L.data[i]==e){ return i+1; // 下标为i的元素值等于e,返回其位序i+1 } } return 0; //退出循环,说明查找失败。 }
当在顺序表中查找一个数据元素时,其时间主要消耗在数据的比较上,而比较的次数取决于被查元素在线性表中的位置。
最好的情况:查找的元素就在表头,仅需比较一次,时间复杂度为O(1)
最坏的情况:查找的元素在表尾(或者不存在)时,则需要比较n次,时间复杂度为O(n)。
顺序表按值查找算法的平均时间复杂度为O(n)。