顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储;即通过数据元素物理存储的连续性来反应元素之间逻辑上的相邻关系。
采用顺序存储结构存储的线性表通常简称为顺序表。
特点
- 随机访问,即可以在O(1)时间内找到第i个元素
- 存储密度高,每个节点只存储数据元素
- 拓展容量不方便(即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高)
- 插入,删除操作不方便,需要大量移动元素
顺序表的定义
静态分配
分配空间长度不可变
#define MAXSIZE 100 //定义线性表的最大长度
typedef struct{
int data[MAXSIZE]; //用静态的数组存放数据元素(int可以为其他数据类型)
int length; //顺序表当前的长度
}SqList;
给各个数据元素分配空间,大小为MAXSIZE*sizeof(int)
初始化顺序表
#include<stdio.h>
#define MAXSIZE 100 //定义线性表的最大长度
typedef struct{
int data[MAXSIZE]; //用静态的数组存放数据元素
int length; //顺序表当前的长度
}SqList; //顺序表类型的定义
void InitList(SqList *L){
for(int i = 0; i < MAXSIZE; i++)
L->data[i] = 0; //将所有数据元素默认初始值设为0
L->length = 0; //顺序表初始长度为0
}
int main(){
SqList L; //声明一个顺序表
InitList(&L); //初始化顺序表
return 0;
}
动态分配
#define INITSIZE 100 //定义线性表的最大长度
typedef struct{
int *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SqList; //顺序表类型的定义
增加顺序表长度
#include<stdio.h>
#include<stdlib.h>
#define INITSIZE 100 //定义线性表的最大长度
typedef struct{
int *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SqList; //顺序表类型的定义
void InitList(SqList *L){
//用malloc函数申请一片连续的存储空间
L->data = (int*)malloc(INITSIZE*sizeof(int));
L->length = 0;
L->MaxSize = INITSIZE;
}
//增加动态数组的长度
void IncreaseSize(SqList *L,int len){
int *p = L->data;
L->data = (int *)malloc((L->MaxSize+len)*sizeof(int));
for(int i = 0; i < L->length; i++){
L->data[i] = p[i]; //将数据分配到新的区域
}
L->MaxSize = L->MaxSize+len;
free(p); //释放原来的内存空间
}
int main(){
SqList L; //声明一个顺序表
InitList(&L); //初始化顺序表
IncreaseSize(&L,5); //让数组长度增加5
return 0;
}
顺序表的插入
把需要插入的元素放入元素i的位置,然后元素i以及后面的元素依次往后移动
#define MAXSIZE 100 //定义线性表的最大长度
typedef struct{
int data[MAXSIZE]; //用静态的数组存放数据元素(int可以为其他数据类型)
int length; //顺序表当前的长度
}SqList;
bool ListInsert(SqList *L,int i,int e){
//在i位置插入元素e
if(i<1||i>L.length) //判断i的范围是否有效
return false;
if(L.length >= MAXSIZE) //判断存储空间已满,不能插入
return false;
for(int j = L.length; j >= i; j--) //将第i个元素及之后的元素后移
L.data[j] = L.data[j-1];
L.data[i-1] = e; //在位置i处放入e
L.length++; //长度加1
return true;
}
** 插入时间复杂度**
- 最好情况:新元素插入到表尾,不需要移动元素
i = n + 1,循环0次;最好时间复杂度=O(1) - 最坏情况:新元素插入到表头,需要将原有的n个元素全都向后移动
i = 1,循环n次;最坏时间复杂度=O(n) - 平均情况:假设新元素插入到任何一个位置的概率相同,即i = 1,2,…,
length+1的概率都是p = 1/(n+1)
i = 1,循环n次;i = 2,循环n-1次…i = n+1时,循环0次
平均循环次数n/2,平均时间复杂度=O(n)
顺序表的删除
#define MAXSIZE 100 //定义线性表的最大长度
typedef struct{
int data[MAXSIZE]; //用静态的数组存放数据元素(int可以为其他数据类型)
int length; //顺序表当前的长度
}SqList;
bool ListInsert(SqList *L,int i){
if(i<1||i>L.length) //判断i的范围是否有效
return false;
for(int j = i; j < L.length; j++) //将第i个元素及之后的元素后移
L.data[j-1] = L.data[j];
L.length--; //长度减1
return true;
}
时间复杂度
- 最好情况:删除表尾元素,不需要移动其他元素
i=n,循环0次,最好时间复杂度=O(1) - 最坏情况:删除表头元素,需要将后续的n-1个元素全都向前移动
i=1, 循环n-1次,最坏时间复杂度=O(n); - 平均情况:假设删除任何一个元素的概率相同,即i= 1,2,3, …length的概率都是p=1/n
i = 1时,循环n-1次; i=2时, 循环n-2次; i=3,循环n-3次…
i = n 时,循环0次
平均循环次数= (n-1)/2,平均时间复杂度=O(n)
顺序表的查找
按位查找
获取表的第i个位置的元素的值
#define INITSIZE 100 //定义线性表的最大长度
typedef struct{
int *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义(动态分配方式)
int GetElem(SqList L,int i){
return L.data[i-1];
}
按值查找
获取元素e在线性表中的位置
#define INITSIZE 100 //定义线性表的最大长度
typedef struct{
int *data; //指示动态分配数组的指针
int MaxSize; //顺序表的最大容量
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义(动态分配方式)
//在顺序表中查找第一个元素值等于e的元素,并返回其位序
int GetElem(SqList L,int e){
for(int i = 0; i < L.length; i++)
if(L.data[i] == e)
return i+1;
return 0;
}
时间复杂度
-
最好情况:目标元素在表头
循环一次;最好时间复杂度=O(1) -
最坏情况:目标元素在表尾
循环n次;最坏时间复杂度=O(n) -
平均情况:假设目标元素出现在任何一个位置的概率相同,都是1/n
目标元素在第一位,循环一次;目标元素在第二位,循环两次;…在第n位循环n次,平均循环次数=(n+1)/2
平均时间复杂度O(n)