顺序表

顺序表的基本概念

概念:用一组地址连续的存储单元依次存储线性表的数据元素,这种存储结构的线性表称为顺序表。

特点:逻辑上相邻的数据元素,物理次序也是相邻的。

顺序表的基本操作

1.初始化与内存分配

静态分配
#include 
#define MaxSize 10    //定义最大长度
typedef struct{
    int data[MaxSize];//用静态的“数组”存放数据元素
    int length;      //顺序表的当前长度
}SqList;            //顺序表的类型定义

//基本操作—初始化一个顺序表
void InitList(SqList &L){
    for(int i=0; i
动态分配
#include 
#define InitSize 10 //默认的最大长度
typedef struct{
    int *data;      //指示动态分配数组的指针
    int MaxSize;    //顺序表的最大容量
    int length;     //顺序表的当前长度
}SeqList;

void InitList(SeqList &L){
    //用malloc函数申请一片连续的存储空间
    L.data=(int *)malloc(InitSize*sizeof(int));
    L.length=0;
    L.MaxSize=InitSize;
}

int main() {
    SeqList L;       //声明一个顺序表
    InitList(L);     //初始化顺序表
    //往顺序表中随便插入几个元素
    IncreaseSize(L, 5);
    return 0;
}

//增加动态数组的长度
void IncreaseSize(SeqList &L, int len){
    int *p=L.data;
    L.data=(int *)malloc((L.MaxSize+len)*sizeof(int));
    for(int i=0; i

 增加动态数组的长度先创造一个新的指针p指向原来数组的首地址,再用malloc为原来数组的指针开辟一块新的内存空间,再利用指针p将原来的数据复制给新的数组,最后释放p的内存空间。

2.插入元素

#define MaxSize 10    //定义最大长度
typedef struct{
    int data[MaxSize];//用静态的“数组”存放数据元素
    int length;      //顺序表的当前长度
}SqList;            //顺序表的类型定义

void ListInsert(SqList &L,int i,int e){
    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
}

int main() {
    SqList L;          //声明一个顺序表
    InitList(L);       //初始化顺序表
    //...此处省略一些代码,插入几个元素
    ListInsert(L, 3, 3);
    return 0;
}

插入元素要让被插入位置之后的所有元素往后移一个咋,再将插入的元素放入插入的位置上。(为了增强代码的健壮性,要对插入位置i进行判断,i

时间复杂度

最好情况:新元素插入到表尾,不需要移动元素
i = n + 1,循环 0 次;最好时间复杂度 = O (1)
最坏情况:新元素插入到表头,需要将原有的 n 个元素全都向后移动
i = 1,循环 n 次;最坏时间复杂度 = O (n);
平均情况:假设新元素插入到任何一个位置的概率相同,即 i = 1,2,3,...,length + 1 的概率都是 p = 1/(n + 1)
i = 1,循环 n 次;i = 2 时,循环 n - 1 次;i = 3,循环 n - 2 次...... i = n + 1 时,循环 0 次
平均循环次数 = np + (n - 1) p + (n - 2) p +...... + 1・p = n (n + 1)/2・1/(n + 1) = n/2
平均时间复杂度 = O (n)

3.删除元素

bool ListDelete(SqList &L,int i,int &e){
    if(i<1||i>L.length)            //判断i的范围是否有效
        return false;
    e=L.data[i-1];                //将被删除的元素赋值给e
    for(int j=i;j

 将被删除元素后面的每个元素往前移即可。先移动下标小的。

时间复杂度

最好情况:删除表尾元素,不需要移动其他元素
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) p + (n - 2) p +...... + 1・p = n (n - 1)/2・1/n = (n - 1)/2
平均时间复杂度 = O (n)

4.按位查找

#define MaxSize 10        //定义最大长度
typedef struct{
    ElemType data[MaxSize];  //用静态的“数组”存放数据元素
    int length;              //顺序表的当前长度
}SqList;                    //顺序表的类型定义(静态分配方式)

ElemType GetElem(SqList L, int i){
    return L.data[i - 1];
}

时间复杂度

O(1)

4.按值查找

#define InitSize 10        //顺序表的初始长度
typedef struct{
    ElemType *data;         //指示动态分配数组的指针
    int MaxSize;            //顺序表的最大容量
    int length;             //顺序表的当前长度
} SeqList;                 //顺序表的类型定义(动态分配方式)

//在顺序表L中查找第一个元素值等于e的元素,并返回其位序
int LocateElem(SeqList L,ElemType e){
    for(int i=0;i

如果要查结构体的话,那判断语句要分成结构体的各个分量比较。

时间复杂度

最好情况:目标元素在表头
循环 1 次;最好时间复杂度 = O (1)
最坏情况:目标元素在表尾
循环 n 次;最坏时间复杂度 = O (n);
平均情况:假设目标元素出现在任何一个位置的概率相同,都是 1/n
目标元素在第 1 位,循环 1 次;在第 2 位,循环 2 次;......;在第 n 位,循环 n 次
平均循环次数 = 1・1/n + 2・1/n + 3・1/n +...... + n・1/n = n (n + 1)/2・1/n = (n + 1)/2

平均时间复杂度 = O (n)

 顺序表优缺点

优点:

  1. 随机访问:可以在 O (1) 时间内根据下标快速访问表中任意元素,因为顺序表是用连续的存储单元存储数据元素,通过计算元素的地址偏移量就能直接定位到要访问的元素,这使得顺序表在需要频繁随机访问元素的场景下效率很高,例如查询数组中某个特定位置的元素。
  2. 存储密度高:每个节点只存储数据元素本身,不需要额外的空间来存储指针等辅助信息(与链表等相比),空间利用率相对较高,尤其是当数据元素本身占用空间较大时,这种优势更为明显。

缺点:

  1. 拓展容量不方便
    • 静态分配方式:如果采用静态分配的顺序表(即事先确定好最大长度),一旦存储空间被分配,在运行过程中很难再进行扩展。如果在使用过程中发现存储空间不足,可能需要重新创建一个更大的顺序表,并将原有的数据元素复制到新表中,这是一个比较耗时且复杂的操作,会影响程序的性能和效率。
    • 动态分配方式:即便采用动态分配的方式实现顺序表,虽然可以在一定程度上根据需要扩展存储空间,但扩展长度的时间复杂度也比较高。通常需要重新分配一块更大的连续内存空间,将原有的数据元素复制到新空间,然后释放原来的内存,这个过程涉及到内存的分配、数据的复制和内存的释放等操作,开销较大,尤其是当数据量较大时,扩展操作会消耗较多的时间和系统资源。
  2. 插入和删除操作效率低(中间位置)
    • 插入操作:在顺序表中间位置插入元素时,需要将插入位置之后的所有元素依次向后移动一位,为新元素腾出空间。平均情况下,插入操作的时间复杂度为 O (n),其中 n 为顺序表的长度。例如,在一个长度为 n 的顺序表中,若要在中间某个位置插入一个元素,平均需要移动大约 n/2 个元素,当顺序表长度较大时,插入操作的效率会显著降低。
    • 删除操作:删除顺序表中间位置的元素时,需要将删除位置之后的所有元素依次向前移动一位,以填补被删除元素留下的空位。同样,平均情况下删除操作的时间复杂度也为 O (n)。例如,删除一个长度为 n 的顺序表中间某个位置的元素,平均需要移动大约 (n - 1)/2 个元素,这在频繁进行插入和删除操作且数据量较大的情况下,会对程序性能产生较大影响。

顺序表适用于数据元素数量相对固定,且主要进行随机访问操作,较少进行插入和删除操作(尤其是中间位置的插入和删除)的场景。在实际应用中,需要根据具体的需求和操作特点来选择是否使用顺序表,或者考虑结合其他数据结构来优化程序的性能。

你可能感兴趣的:(数据结构)