线性表文档之顺序表

顺序表

定义

概念

线性表的顺序存储称为顺序表,它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。

1 个元素存储在线性表的起始位置,第 i 个元素的存储位置后面紧接着存储的是第 i+1 个元素,称 i 为元素 ai 在线性表中的位序。

所谓线性表中任一数据元素可以随机存取,就是可以通过常数访问或者修改线性表中的数据元素,通常用高级程序设计语言中的数组来描述线性表的顺序表存储结构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tRcLl4SX-1649165730021)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220326234106962.png)]

结构体

线性表的结构体定义如下:

#define MAXSIZE 100 // 这里定义了一个整型常量 MAXSIZE,值为 100,表示数组长度为 100

typedef struct {
    int data[MAXSIZE];// 存放顺序表元素的数组,默认是 int 类型,可换成其他类型
    int length;// 存放顺序表实际元素个数,而非 MAXSIZE
} SeqList;// 顺序表类型的定义

如图,一个顺序表包括一个存储表中元素的数组 data[] 和一个指示元素个数的变量 length

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XavvzHG5-1649165730022)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220326235312646.png)]

注:下面的内容是补充,通常都使用静态分配,考研知道静态分配结构体定义就行,下面的内容了解即可。

一维数组可以是静态分配的,也可以是动态分配的。在静态分配时,由于数组的大小和空间已经固定,一旦空间占满,再加入新的数据就会产生溢出,进而导致程序奔溃。而int data[MAXSIZE] 就是静态分配的,通常使用的是静态分配。

而在动态分配时,存储数组的空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就可以另外开辟一块更大的存储空间,用来替换原来的存储空间,从而达到扩充存储数组空间的目的,而不需要为线性表一次性划分所有空间。而动态分配的结构体定义语句如下:

#define INIT_SIZE 100 // 顺序表长度的初始定义
typedef struct {
    int *data;// 设置数据类型是整型,可以修改为其他类型。指定动态分配数组的指针
    int MAXSIZE;// 数组的最大容量
    int length;// 顺序表实际元素个数
} SeqList;// 动态分配数组顺序表的类型定义

C 语言初始动态分配语句为(该语句通常写在初始化函数 init 中):

L.data = (int*)malloc(sizeof(int)*INIT_SIZE);

而如果想要扩容,即重新为 L.data 分配更大的空间就可以了。

特点

顺序表的主要特点:

  • 顺序表最主要的特点是随机访问,即可以通过首地址和元素序号可以在时间 O(1) 内找到指定的元素。如数组 L = [1, 2, 3, 4, 5] 可以通过 L[2] 访问到 L 中的第 3 个元素。
  • 顺序表的存储密度高,每个节点只存储数据元素。
  • 顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量元素。

基本操作

注,完整代码请参考:

概述

顺序表的常见基本操作如下:

  • void init(SeqList *L):初始化顺序表。
  • int findByIndex(SeqList L, int index, int *ele):通过下标查找顺序表中的元素,保存到 ele 中。
  • int findByEle(SeqList L, int ele, int *index):通过指定值 ele 在顺序表中查找元素,并将元素的下标保存到 index 中。
  • int insert(SeqList *L, int index, int ele):在顺序表中指定 index 位置插入新元素 ele
  • int add(SeqList *L, int ele):在顺序表的尾部添加新元素 ele
  • removeByIndex(SeqList *L, int index, int *ele):删除指定 index 位置的元素,并将被删除元素保存到 ele 中。
  • size(SeqList L):获取顺序表中实际元素个数。
  • isEmpty(SeqList L):判断顺序表是否为空。
  • print(SeqList L):打印顺序表中所有元素。

init

初始化顺序表操作。即将顺序表的 length 字段值置为 0,初始时顺序表中没有任何元素,所以为零。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWibsZqK-1649165730025)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327120359273.png)]

实现代码如下:

/**
 * 初始化顺序表,仅需要将 length 置为 0 即可
 * @param list 待初始化的顺序表
 */
void init(SeqList *list) {
    // 仅需要将 length 置为 0 即可
    (*list).length = 0;
    // 或者可以用下面的语法
    // list->length=0;
}

注意,如果顺序表结构体定义中 data 是动态分配的,则需要在 init 函数中队 data 进行初始化分配。

findByIndex

在顺序表中查找指定位序(用数组表示顺序表,所以从 0 开始)的元素,而用 ele 存放查找的元素。如果查找成功则返回 1,否则返回 0。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0NQ2NTKB-1649165730027)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327121134783.png)]

实现步骤:

  • 参数校验,如果下标 index 小于零或者大于 MAXSIZE 则无法获取到元素;如果下标大于等于顺序表的实际元素个数 length 也会返回 0。
  • 然后通过下标直接访问 data 数组中的元素即可,无论是最好的情况还是最坏的情况,时间复杂度都是 O(1)

实现代码如下:

/**
 * 查找顺序表中指定索引所表示的元素
 * @param list 待查询的顺序表
 * @param index 索引,即数组的下标,从 0 开始
 * @param ele 保存查找到的元素
 * @return 如果查询成功则返回 1,如果查找失败则返回 0
 */
int findByIndex(SeqList list, int index, int *ele) {
    // 0.参数校验
    // 0.1 如果索引小于 0 或者大于等于设定的最大长度,则无法获取到元素
    if (index < 0 || index >= MAXSIZE) {
        return 0;
    }
    // 0.2 如果索引在 [0, MAXSIZE) 范围内,但超过了实际元素个数,也是无法获取到的
    if (index >= list.length) {
        return 0;
    }
    // 1.查找指定索引所表示的元素
    // 1.1 直接返回下标为 index 的元素值即可,不需要遍历循环
    *ele = list.data[index];
    return 1;
}

findByEle

在顺序表 list 中查找第一个元素值等于 ele 的元素,并返回它在顺序表中的位序(即数组下标)。如果顺序表中存在元素 ele 则函数返回 1,如果顺序表中不存在该元素则返回 0。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kNY1xYla-1649165730030)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327122539755.png)]

实现步骤:

  • 遍历顺序表所有元素,然后与指定值 ele 进行比较,如果相等则用 index 保存该元素在顺序表中的位序(即下标)。并且返回 1 退出程序。
  • 遍历完顺序表发现不存在与指定值 ele 相等的元素,则返回 0。

实现代码如下:

/**
 * 查找指定元素在顺序表中的索引
 * @param list 待查询的顺序表
 * @param ele 指定元素
 * @param index 保存查询成功的索引
 * @return 如果查询成功则返回 1,查询失败则返回 0
 */
int findByEle(SeqList list, int ele, int *index) {
    // 循环顺序表中的所有元素
    for (int i = 0; i < list.length; i++) {
        // 比较迭代的每一个元素的值是否等于传入的参数值,如果相等则返回对应的索引(下标)
        if (list.data[i] == ele) {
            // 保存索引
            *index = i;
            // 结束程序
            return 1;
        }
    }
    return 0;
}

insert

在顺序表 list 的第 index0<=index<list.length)个位置插入新元素 ele。若 index 的输入不合法,则返回 0,表示插入失败;否则,将第 index 个元素及其后的所有元素依次往后移动一个位置,腾出一个空位置插入新元素 ele,顺序表的长度 length 增加 1,表示插入成功,返回 0。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nha4lbrR-1649165730033)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327140904743.png)]

实现步骤:

  • 参数校验,如果 index 小于 0 或者大于 MAXSIZE 都不是有效参数;如果顺序表已经满了,也无法进行插入操作;如果 index 超过顺序表实际元素个数也无法插入成功,都返回 0 表示插入失败。
  • 将顺序表中 index 及之后的所有元素向后移动一位。
  • 然后将空出来的 index 位置填入待插入的元素 ele
  • 最后修改顺序表的 length 增加 1。

实现代码如下:

/**
 * 向顺序表指定索引处插入一个新元素。原索引处的元素及之后的所有元素都向后移动一位。
 * @param list 顺序表
 * @param index 指定插入位置
 * @param ele 待插入的新元素
 * @return 如果插入成功则返回 1,如果插入失败则返回 0
 */
int insert(SeqList *list, int index, int ele) {
    // 0.参数校验
    // 0.1 如果索引小于 0 或者大于等于设定的最大长度,则插入失败
    if (index < 0 || index >= MAXSIZE) {
        return 0;
    }
    // 0.2 向顺序表中插入元素要检查顺序表是否已经满了,如果已经满了则不能再插入新元素则插入失败
    if (list->length == MAXSIZE) {
        return 0;
    }
    // 0.3 如果插入的索引超过了数组长度的范围也不行
    if (index > list->length) {
        return 0;
    }
    // 1.如果顺序表没有满,则继续插入元素
    // 1.1 循环顺序表,从后往前遍历,将指定索引及之后的所有元素(包括指定索引)向后移动一位
    for (int i = list->length - 1; i >= index; i--) {
        list->data[i + 1] = list->data[i];
    }
    // 1.2 将移动后空出来的位置(即指定索引所在的位置)插入新元素
    list->data[index] = ele;
    // 1.3 不要忘记将 length 加 1 表示顺序表新增一个元素
    list->length++;
    return 1;
}

add

为了弥补 insert 方法无法在顺序表尾部插入的问题(因为参数校验,所以不能插入,当然可以修改代码让它能够通过 insert 方法插入)。直接在顺序表的尾部插入新元素 ele

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WP6WkHVP-1649165730037)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327141647114.png)]

实现步骤:

  • 参数校验,如果顺序表已满,则返回 0 表示插入失败。
  • 直接在顺序表的 list->length 位置插入新元素 ele,因为该位置为空,所以直接赋值即可。
  • 插入新元素后,将顺序表的 length 加 1。
  • 返回 1 表示插入成功。

实现代码如下:

/**
 * 直接添加新元素到顺序表的尾部
 * @param list 顺序表
 * @param ele 待添加的新元素
 * @return 如果插入成功则返回 1,否则返回 0
 */
int add(SeqList *list, int ele) {
    // 0.校验
    // 0.1 向顺序表中插入元素要检查顺序表是否已经满了,如果已经满了则不能再插入新元素则添加失败
    if (list->length == MAXSIZE) {
        return 0;
    }
    // 1.插入新元素
    // 1.1 直接获取顺序表的 length,然后将新元素的值赋予到 length 位置即可
    list->data[list->length] = ele;
    // 1.2 注意修改 length
    list->length++;
    return 1;
}

removeByIndex

删除顺序表 list 中第 index0<=index<list.length)个位置(表示数组下标)的元素,用引用变量 ele 存放被删除元素的值。若输入的 index 不合法则返回 0 表示删除失败;否则,将被删元素赋给引用变量 ele,并将第 index+1 个元素及其后的所有元素依次往前移动一个位置,返回 1 表示删除成功。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C2frFDN1-1649165730041)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327143142460.png)]

实现步骤:

  • 参数校验。如果下标 index 小于零或者大于 MAXSIZE 表示超出范围,返回 0 表示删除失败;如果顺序表为空则无法删除元素,返回 0 表示删除失败;如果下标 index 大于等于顺序表长度 list->length 则也无法删除,返回 0 表示删除失败。
  • 将待删元素 list->data[index] 保存到引用变量 ele 中。
  • 然后将 index+1 及其后的所有元素都向前移动一位,即用后面的元素覆盖前面的元素。
  • 循环完成后,将顺序表的实际元素个数 length 减去 1,表示已经删除一个元素。
  • 返回 1 表示删除成功。

实现代码如下:

/**
 * 移除顺序表中指定索引的元素
 * @param list 顺序表
 * @param index 指定索引,即下标,从 0 开始
 * @param ele 被删除元素的数据值
 * @return 如果删除成功则返回 1,删除失败则返回 0
 */
int removeByIndex(SeqList *list, int index, int *ele) {
    // 0.参数校验
    // 0.1 判断输入的索引是否超出范围
    if (index < 0 || index >= MAXSIZE) {
        return 0;
    }
    // 0.2 在删除顺序表元素之前,要判断顺序表是否为空,如果为空则不能进行删除
    if (list->length == 0) {
        return 0;
    }
    // 0.3 判断输入的索引虽然在数组范围内,但是否存在元素
    if (index >= list->length) {
        return 0;
    }
    // 1.删除指定索引的元素
    // 1.1 保存待删除索引所表示的元素的数据值
    *ele = list->data[index];
    // 1.2 循环遍历顺序表,从前往后,将指定索引之后的所有元素(不包括指定索引)向前移动一步
    for (int i = index; i < list->length; i++) {
        list->data[i] = list->data[i + 1];// 后面的元素覆盖前面的元素
    }
    // 1.3 将记录数组实际元素个数的 length 减去 1,表示已经删除了一个元素
    list->length--;
    return 1;
}

size

获取顺序表的实际元素个数,而非 MAXSIZE。在顺序表中已经有一个 length 维护了顺序表的实际元素个数,所以直接返回即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-meMNbrKt-1649165730042)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327143350399.png)]

实现步骤:

  • 直接返回顺序表的 length 属性即可。

实现代码如下:

/**
 * 获取顺序表的长度
 * @param list 顺序表
 * @return 顺序表中实际元素个数
 */
int size(SeqList list) {
    return list.length;
}

isEmpty

判断顺序表是否为空,如果顺序表为空则返回 1;如果顺序表不为空则返回 0。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UojUTgYk-1649165730043)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327143845250.png)]

实现步骤:

  • 判断顺序表的 length 属性值是否等于 0,如果等于 0 表示顺序表实际元素个数为 0,即为空表;否则不是空表。

实现代码如下:

/**
 * 顺序表是否为空
 * @param list 顺序表
 * @return 如果为空则返回 0,不为空则返回 1
 */
int isEmpty(SeqList list) {
    return list.length == 0;
}

print

打印顺序表所有元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8e0Hxxw0-1649165730090)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327144244457.png)]

实现步骤:

  • 遍历顺序表所有元素,按顺序输出元素值。

实现代码如下:

/**
 * 打印顺序表
 * @param list 待打印的顺序表
 */
void print(SeqList list) {
    printf("[");
    for (int i = 0; i < list.length; i++) {
        printf("%d", list.data[i]);
        if (i != list.length - 1) {
            printf(", ");
        }
    }
    printf("]\n");
}

注意事项

顺序表是否为空

顺序表结构体 SeqList 中维护了一个字段 length 专门用来记录顺序表中实际存储的元素个数。而可以根据 list.length==0 来判断顺序表是否为空。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6jw4dTc-1649165730092)(image-%E9%A1%BA%E5%BA%8F%E8%A1%A8/image-20220327143845250.png)]

练习题

以下是一些顺序表的练习题:

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值