顺序表基础

54 阅读3分钟

一、顺序表的结构

顺序表是线性表的一种实现形式,其底层基于数组结构,因此在物理存储和逻辑结构上均保持线性特征。

二、顺序表的类型

顺序表可分为静态顺序表和动态顺序表两种类型。

  1. 静态顺序表
    静态顺序表在定义时即固定其容量,无法在程序运行过程中调整大小。
  2. 动态顺序表
    动态顺序表具备在运行时根据需要调整容量的能力,从而更灵活地管理内存。

通过对比可以看出,动态顺序表在实际应用中通常优于静态顺序表。静态顺序表若预设空间过大易造成资源浪费,而空间不足又无法适应数据增长。因此,实际开发中更常使用动态顺序表。

三、动态顺序表的实现

我们通过以下步骤实现一个动态顺序表:

首先,创建头文件 Seqlist.h,定义顺序表结构及相关函数声明:

c

typedef int SLDataType;  // 便于后续替换数据类型

typedef struct SeqList {
    SLDataType* arr;     // 指向动态数组的指针
    int size;            // 当前有效数据个数
    int capacity;        // 当前分配的存储容量
} SL;

// 函数声明
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
void SLPrint(const SL* ps);
  1. 顺序表的初始化与销毁

初始化时,将数组指针设为 NULLsize 与 capacity 均设为 0。也可选择初始时分配一定空间。

c

void SLInit(SL* ps) {
    ps->arr = NULL;
    ps->size = 0;
    ps->capacity = 0;
}

销毁顺序表时,需释放动态申请的内存,避免内存泄漏:

c

void SLDestroy(SL* ps) {
    if (ps->arr) {
        free(ps->arr);
        ps->arr = NULL;
    }
    ps->size = ps->capacity = 0;
}
  1. 数据插入操作

在插入数据前,需检查当前容量是否足够。我们封装一个检查并扩容的函数:

c

void SLCheckCapacity(SL* ps) {
    if (ps->size == ps->capacity) {
        int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
        SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
        if (tmp == NULL) {
            perror("Realloc failed");
            exit(1);
        }
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
}
  • 尾插:在顺序表末尾插入元素

c

void SLPushBack(SL* ps, SLDataType x) {
    SLCheckCapacity(ps);
    ps->arr[ps->size] = x;
    ps->size++;
}
  • 头插:在顺序表头部插入元素,需将现有元素依次后移

c

void SLPushFront(SL* ps, SLDataType x) {
    SLCheckCapacity(ps);
    for (int i = ps->size; i > 0; i--) {
        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[0] = x;
    ps->size++;
}

使用打印函数验证插入操作:

c

void SLPrint(const SL* ps) {
    for (int i = 0; i < ps->size; i++) {
        printf("%d ", ps->arr[i]);
    }
    printf("\n");
}
  1. 数据删除操作
  • 尾删:仅需将 size 减一

c

void SLPopBack(SL* ps) {
    if (ps->size > 0) {
        ps->size--;
    }
}
  • 头删:将后续元素前移一位,并调整 size

c

void SLPopFront(SL* ps) {
    if (ps->size > 0) {
        for (int i = 0; i < ps->size - 1; i++) {
            ps->arr[i] = ps->arr[i + 1];
        }
        ps->size--;
    }
}
  1. 指定位置插入与删除
  • 指定位置插入:将目标位置及之后的元素后移,再插入新值

c

void SLInsert(SL* ps, int pos, SLDataType x) {
    assert(pos >= 0 && pos <= ps->size);
    SLCheckCapacity(ps);
    for (int i = ps->size; i > pos; i--) {
        ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[pos] = x;
    ps->size++;
}
  • 指定位置删除:将目标位置之后的元素前移,覆盖目标值

c

void SLErase(SL* ps, int pos) {
    assert(pos >= 0 && pos < ps->size);
    for (int i = pos; i < ps->size - 1; i++) {
        ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;
}

以上即为动态顺序表的基本实现,涵盖了初始化、销毁、插入、删除等核心操作,具备良好的可扩展性与实用性。