线性表文档之顺序表

Posted 二木成林

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表文档之顺序表相关的知识,希望对你有一定的参考价值。

顺序表

定义

概念

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

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

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

结构体

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

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

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

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

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

一维数组可以是静态分配的,也可以是动态分配的。在静态分配时,由于数组的大小和空间已经固定,一旦空间占满,再加入新的数据就会产生溢出,进而导致程序奔溃。而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,初始时顺序表中没有任何元素,所以为零。

实现代码如下:

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

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

findByIndex

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

实现步骤:

  • 参数校验,如果下标 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。

实现步骤:

  • 遍历顺序表所有元素,然后与指定值 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。

实现步骤:

  • 参数校验,如果 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

实现步骤:

  • 参数校验,如果顺序表已满,则返回 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 表示删除成功。

实现步骤:

  • 参数校验。如果下标 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 维护了顺序表的实际元素个数,所以直接返回即可。

实现步骤:

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

实现代码如下:

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

isEmpty

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

实现步骤:

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

实现代码如下:

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

print

打印顺序表所有元素。

实现步骤:

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

实现代码如下:

/**
 * 打印顺序表
 * @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 来判断顺序表是否为空。

练习题

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

以上是关于线性表文档之顺序表的主要内容,如果未能解决你的问题,请参考以下文章

线性表--链表基础

第02次作业-线性表

第02次作业-线性表

数据结构——线性表之顺序存储结构

C#线性表之顺序表

线性表之顺序存储-顺序表