【算法与数据结构】: 什么是线性表?如何实现顺序表?

171 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情

1、写在前面

大家好,我是翼同学。今天文章的内容是:

  • 线性表

2、内容

2.1、什么是线性表?

线性表(Linear_List)是一种基本且常用的数据结构。

线性表可以抽象的记为:

  • a0a_0, a1a_1, a2a_2 ... ai1a_{i-1}, aia_i, ai+1a_{i+1}, ... an1a_{n-1}

在上述抽象中:

  1. aia_i 是第 ii 个元素,并且称 ai1a_{i-1}aia_i 的前驱元素,ai+1a_{i+1}aia_i 的后继元素。
  2. 每个元素在线性表中都有确定的位置,即 ii 是元素 aia_i 在线性表中的位序(位置)。
  3. a0a_0 是首元素,只有后继元素没有前驱元素。
  4. an1a_{n-1} 是末元素,只有前驱元素没有后继元素。
  5. 每个数据元素都具有相同的数据类型。
  6. 数据元素的个数 nn 定义为线性表的长度。
  7. n=0n=0 时,称为空表。

总的来说,线性表就是由零个或多个类型相同的数据元素组成的有限序列。

2.2、线性表的基本操作

template <class T>
class List
{
public:
    virtual bool empty() const = 0;                     // 判断是是否为空表
    virtual int length() const = 0;                     // 求线性表的长度
    virtual int search(const T &value) const = 0;       // 查找值为 value 的元素
    virtual T vist(int i) const = 0;                    // 查找位置为 i 的元素
    virtual void insert(int i, const T &value) = 0;     // 在指定位置 i 插入元素 value
    virtual void delete(int i) = 0;                     // 删除位置为 i 的元素
    virtual void traverse() const = 0;                  // 遍历线性表
    virtual void inverse() = 0;                         // 逆置线性表
    virtual void clear() = 0;                           // 清空线性表
    virtual ~List(){};                                  // 析构函数
};
  • empty(): 如果线性表为空,则返回真值,否则返回假值。
  • length(): 返回线性表中的元素个数,即长度。
  • search(value): 查找线性表中值为value的位置。
  • vist(i): 查找位置为i的元素。
  • insert(i, value): 在指定位置i插入元素value
  • delete(i): 删除位置为i的数据元素。
  • traverse(): 遍历线性表中的每个元素
  • inverse(): 逆置线性表
  • clear(): 清空线性表

以上便是线性表基本运算的记录。

2.3、顺序表的介绍

如果想将一个数据结构存入计算机中,就必须在存储数据的同时将数据之间的关系表达出来。而在计算机中,我们可以用顺序表的形式来表示线性表。

顺序表的定义如下:

在内存中用地址连续的、有限的一块存储空间顺序存放线性表的数据元素。我们将这种存储方式称为顺序表。

由于计算机内存可以理解为一维数组,每个存储单元都有一个编号作为地址,也就是说内存中的地址空间是线性的,因此顺序表可以用物理上的相邻实现元素之间的逻辑相邻关系。

顺序表具有按照数据元素的位序随机存取的特点。

2.4、顺序表的存储结构

在C++语言中,我们可以用一维数组来实现定长的线性存储结:

  • 顺序表的存储结构是用一组地址连续的存储单元依次存储数据元素;
  • 需要注意,顺序表中元素的类型都是相同的,因此每个数据所占有的空间一样大;
  • 假设每个数据所占空间大小为LL,并且首元素a0a_0的地址为bb,则第ii个数据的地址为:b+i×Lb+i×L

示意图如下:

image.png

参数说明;

  1. capacity:考虑到顺序表的运算有插入、删除等,所以顺序表的容量需设计的足够大,我们会根据实际问题定义一个足够大的整数capacity作为数组的上界,表示顺序表可能达到的最大长度。
  2. length:顺序表中的数据从数据的零位置开始,依次顺序存放。由于顺序表的实际元素个数要少于capacity,因此用一个变量length来记录顺序表中实际元素的个数。

2.5、顺序表的实现

(1) 类型定义

image.png

(2) 构造函数

构造一个空顺序表:

image.png

(3) 插入运算

插入运算指的是在位序为ii处插入一个值为value的新元素,此时表长length加一,并且插入元素的后续元素都必须往后移一位。插入运算的时间复杂度为O(n)O(n).

image.png

(4) 删除运算

删除运算指的是将表中位序为ii处的元素删除掉,此时后续元素必须向前移一位,表长也会减一。同样的,删除运算的时间复杂度为O(n)O(n).

image.png

(5) 查找运算

查找运算就是查找值为value的数据元素在顺序表中第一次出现的位置ii。如果查找不到则返回-1。时间复杂度为O(n)O(n).

image.png

(6) 遍历运算

遍历运算就是访问顺序表中每一个元素。注意,需要先判断顺序表是否为空再进行遍历,否则输出所有数据元素。

image.png

(7) 逆置运算

逆置运算用两两交换的方法调整顺序表中元素的位置,使数据元素倒置过来。时间复杂度也为O(n)O(n).

image.png

(8) 扩表运算

当表满了后,我们就不能进行一些操作,比如插入运算,否则就会产生溢出错误。此时我定义扩表运算,当表满了后扩大顺序表的容量。

image.png

2.6、顺序表的小结

顺序表具有的特点如下:

  • 顺序表需要预先分配空间大小,可按序号ii直接存取数据元素;适合静态的,经常进行定位访问的线性表;
  • 顺序表的插入运算和删除运算需要移动较多数据元素,效率较低;
  • 改变顺序表的大小时,需要重新创建一个新的顺序表,将原表的数据复制到新表上,再释放原表空间。

3、写在最后

好了,今天文章的内容就到这里。