[C语言数据结构] 顺序表详解

目录

1. 什么是顺序表?

1.1 顺序表的定义

1.2 顺序表 vs. 链表

顺序表的基本操作

2. 结构设计

数据结构解释

3. 代码实现

3.1 初始化

3.2 扩容机制

3.3 插入元素

尾部插入

头部插入

任意位置插入

3.4 删除元素

尾部删除

头部删除

任意位置删除

3.5 打印顺序表

3.6 查找元素

3.7 释放内存

4. 测试


1. 什么是顺序表?

1.1 顺序表的定义

顺序表(Sequence List) 是一种使用连续内存空间存储数据的线性数据结构,本质上就是动态数组。它具备以下特点:

  • 顺序存储:元素在内存中是连续存放的,可随机访问(即 O(1) 时间复杂度访问)。
  • 动态扩容:当存储空间不足时,可以自动扩容,克服静态数组的限制。
  • 支持插入、删除、查找:通过索引可以直接访问元素,也可以对数据进行动态操作。

1.2 顺序表 vs. 链表

数据结构存储方式访问速度插入/删除速度额外空间
顺序表连续内存O(1)O(n)需要预留空间
链表非连续内存O(n)O(1)(头插法)需要额外指针存储
  • 顺序表适用于:查找频繁的场景(因为支持随机访问)。
  • 链表适用于:插入、删除频繁的场景(因为不需要移动数据)。

顺序表的基本操作

操作说明
初始化(Init)创建一个空的顺序表
插入(PushBack, PushFront, Insert)在表尾、表头或指定位置插入元素
删除(PopBack, PopFront, Erase)删除表尾、表头或指定位置的元素
查找(Find)查询某个元素是否存在,并返回索引
打印(Print)输出顺序表中的所有元素
销毁(Destroy)释放动态分配的内存,防止内存泄漏

2. 结构设计

SeqList.h 头文件中,我们定义顺序表的数据结构相关操作

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

// 定义数据类型(可以换成 float、double 等)
typedef int SLDataType;

// 顺序表结构体
typedef struct SequenceList {
    SLDataType* arr;  // 动态数组指针
    int size;         // 当前元素个数
    int capacity;     // 当前数组的容量
} SL;

// 函数声明
void SLInit(SL* ps);        // 初始化
void SLDestroy(SL* ps);     // 销毁顺序表
void SLPrint(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);  // 指定位置删除
int SLFind(SL* ps, SLDataType x); // 查找元素

数据结构解释

  1. arr:指向动态分配的数组,存储顺序表中的元素。
  2. size:当前已存储的元素个数(用于控制数据访问范围)。
  3. capacity:当前数组的总容量(当 size == capacity 时,需要扩容)。

3. 代码实现

3.1 初始化

void SLInit(SL* ps)
{
    ps->arr = NULL;  // 初始时不分配空间
    ps->size = 0;    
    ps->capacity = 0;
}

解释

  • arr 设为 NULL,表示还未分配内存。
  • size 设为 0,因为表里没有元素。
  • capacity 设为 0,表示没有预分配空间。

3.2 扩容机制

size == capacity 时,必须扩展内存,否则无法继续插入新元素。

void SLCheckCapacity(SL* ps)
{
    int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity; // 扩容策略
    if (ps->size == ps->capacity) 
    {
        SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
        if (tmp == NULL)
        {
            perror("内存分配失败!");
            return;
        }
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
}

扩容策略

  1. 初次分配:如果 capacity == 0,分配 4 个单位的空间。
  2. 后续扩容:当空间不足时,容量扩大 2 倍,提高效率(减少 realloc 调用次数)。

3.3 插入元素

尾部插入

void SLPushBack(SL* ps, SLDataType x)
{
    assert(ps != NULL);
    SLCheckCapacity(ps);
    ps->arr[ps->size++] = x;
}

步骤

  1. 先检查是否需要扩容。
  2. arr[size] 位置插入数据 x
  3. size++,更新当前元素数量

头部插入

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

步骤

  1. 先检查是否需要扩容。
  2. 所有元素后移,腾出 arr[0] 位置。
  3. 插入 xsize++

任意位置插入

void SLInsert(SL* ps, int pos, SLDataType x)
{
    assert(ps);
    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;
}

  • 检查 pos 是否有效pos 不能超过 size
  • 扩容:确保有足够空间。
  • 元素后移,插入 x

3.4 删除元素

尾部删除

void SLPopBack(SL* ps)
{
    assert(ps && ps->size);
    --ps->size;
}

步骤

  1. 检查 size > 0,防止越界访问。
  2. 直接 size--,逻辑上删除元素。

头部删除

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

  • 所有元素前移,覆盖 arr[0]
  • size--,更新元素个数。

任意位置删除

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

  • 检查 pos 是否有效
  • 元素前移,覆盖 pos 位置。

3.5 打印顺序表

void SLPrint(const SL* ps)
{
    assert(ps != NULL);  // 确保指针有效
    for (int i = 0; i < ps->size; i++)
    {
        printf("%d ", ps->arr[i]);
    }
    printf("\n");
}

作用

  • 遍历 arr,按顺序打印所有元素。

3.6 查找元素

int SLFind(const SL* ps, SLDataType x)
{
    for (int i = 0; i < ps->size; i++)
    {
        if (ps->arr[i] == x)
        {
            return i;
        }
    }
    return -1;
}

逻辑

  • 遍历 arr,找到 x 并返回索引,否则返回 -1

3.7 释放内存

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

作用

  • 释放 arr,避免内存泄漏。

4. 测试

#include "SeqList.h"

void test()
{
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPushBack(&sl, 5);

	SLPushFront(&sl, 0);
	SLPrint(&sl);

	SLPopBack(&sl);
	SLPrint(&sl);

	SLPopFront(&sl);
	SLPrint(&sl);

	SLInsert(&sl, 3, 250);
	SLPrint(&sl);

	SLErase(&sl, 3);
	SLPrint(&sl);

	SLDestroy(&sl);
}

int main()
{
	test();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值