从0开始学习数据结构-线性表②

200 阅读3分钟

这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战

线性表的顺序存储结构-常见操作

  • 插入
  • 删除
  • 元素定位
  • 排序

在长度为n的线性表A的第i个位置插入一个新数据元素item ※

  • 在线性表的第i-1个数据元素位置与第i个数据元素之间插入一个新的数据元素
  • 线性表的长度由n变成n+1 插入之前: (a1, a2, ..., ai-1, ai, ai+1, ..., an-1, an)

插入之后: (a1, a2, ..., ai-1, item, ai, ai+1, ..., an-1, an)


思路:

  • 判断线性表是否已满(分配给线性表的MaxSize个元素的存储空间是否已经被表中的元素全部占用)
    • 未满:
      • 测试插入的位置i是否合适,合适的插入位置为1 ≤ i ≤ n ≤ n+1(对应数组A的下标为0 ≤ i ≤ n)

步骤:

  • ①将线性表的第i个数据元素到第n个数据元素之间的所有元素依次向后移动一个位置(共移动n-i+1个元素)
  • ②将新的数据元素item插入到线性表的第i个位置上
  • ③修改线性表的长度为n+1

数据元素依次后移一个位置的方向,必须是从表的末尾元素开始后移,直到将第i个位置的元素后移一个位置为止

具体算法:

void INSERTLIST(ElemType A[], int &n, int i, ElemType item) 
{
    int j;
    if (n == MaxSize || i < 1 || i > n+1)
        ERRORMESSAGE("表满或插入位置不正确!"); // 插入失败
    for (j = n-1; j >= i - 1; j--)
        A[j+1] = A[j] // 数据元素依次后移一个位置
    A[i-1] = item; // 将item插入表的第i个位置
    n++; // 表的长度加1
}

算法的时间效率: 长度为n的线性表,算法的时间复杂度为O(n)

  • 最好的情况是不移动任何元素
  • 最坏的情况是移动了所有数据元素
  • 平均情况是移动表中一半的数据元素

删除长度为n的线性表A的第i个数据元素 ※

  • 将线性表的第i个数据元素从表中去掉
  • 线性表的长度由n变成n-1 删除之前: (a1, a2, ..., ai-1, ai, ai+1, ..., an-1, an)

删除之后: (a1, a2, ..., ai-1, ai+1, ..., an-1, an)


思路:

  • 删除之前首先判断线性表是否为空
    • 不空:
      • 判断被删除元素的位置i是否合适,合适的删除位置应该是 1 ≤ i ≤ n(对于的数组下标为 0 ≤ i ≤ n-1) 步骤:
  • 将表的第i+1个数据元素至第n个数据元素(一共n-i个元素)依次向前移动一个位置
  • 修改线性表的长度为n-1

具体算法:

void DELETELIST(ElemType A[], int &nn, int i)
{
    int j;
    if (i < 1 || i > n + 1)
        ERRORMESSAGE("表空或删除位置不正确!"); // 删除失败
    for (j = 1; j < n; j++)
        A[j-1] = A[j]; // 数据元素依次前移一个位置
    n--; // 表长减1
}

算法的时间效率: 算法的平均时间复杂度为O(n)

确定元素item在长度为n的线性表A中的位置 ※

思路 从线性表的第1个数据元素开始,从前向后依次通过比较来确定给定元素item在表中的位置

  • 找到:返回被查到元素在表中的位置
  • 未找到:返回-1

具体算法

int LOCATE(ElemType A[], int n, ElemType item) 
{
    for(i = 0; i < n; i++)
        if(A[i] == item)
            return i + 1; // 查找成功,返回元素在表中位置
    return -1; // 查找失败,返回-1
}

时间复杂度

  • 最好的情况下为O(1)
  • 最坏的情况和平均情况下为O(n)

删除表中重复出现的元素

思路 从线性表的第1个元素开始到最后1个元素,依次检查某个元素后面的元素中是否存在与之相同的元素

  • 存在
    • 删除后面那个元素
    • 修改线性表的长度

具体算法

void PURGE(ElemType A[], int &n) 
{
    int i = 0, j;
    while (i < n) {
        j = i + 1; // 从第i + 1个元素开始逐个与第i个元素比较
        while (j < n) 
        {
            if(A[j] === A[i]) // 若A[j]与A[i]相同
                DELETELIST(A, n, j + 1); // 删除元素A[j]
            else 
                j++;
        }
        i++;
    }
}

时间复杂度: O(n^2)

对线性表中元素进行排序

线性表的排序操作是指:按照线性表中数据元素的值或者某个数据项值的大小排列数据元素,使之成为一个有序表

  • 简单的选择排序方法(这里仅介绍此方法)

选择排序方法的基本思想 第i趟排序是从线性表后面的n-i+1个数据元素中选择一个值最小的数据元素,并将其与这n-i+1个数据元素中的第1个数据元素交换位置。比较n-1趟 => 线性表成了一个按值从小到大排列的线性表。

具体算法

void SELECTSORT(ElemType K[], int n) 
{
    int i, j, d;
    ElemType temp;
    for (i = 0; i < n - 1; i++) {
        d = i;  // 假设最小值元素为未排序元素的第1个元素
        for (j = i + 1; j < n; j++) {
            if(K[j] < K[d])
                d = j;   // 寻址真正的值最小元素,记录其位置d
        }
        if (d != i) { // 当最小值元素非第1个元素时
            temp = K[d];
            K[d] = K[i];
            K[i] = temp; // 最小值元素与未排序的第1个元素交换位置
        }
    }
}

时间复杂度:O(n^2)

顺序存储结构小总

特点

逻辑上相邻的两个数据元素在物理位置上也相邻。

优点

  • 构造原理简单,直观,易理解
  • 若已知每个数据元素所占用的存储单元个数,并且直到第1个数据元素的存储位置,则表中任意一个数据元素的位置可以通过一个简单的解析式计算出来
  • 对表中所有的数据元素,既可以顺序访问,也可以进行随机访问(既可以从表的第一个元素开始逐个访问,也可以根据元素的位置直接访问),并且访问任意一个数据元素的时间代价都相同
  • 只需要存数据元素本身的信息,而无其他额外空间开销,相对于链式存储结构,存储空间开销小

缺点

  • 需要一片地址连续的存储单元作为线性表的存储空间
  • 存储空间的分配需要事先进行,使得应该分配的存储空间大小不易估计
    • 线性表的长度变化较大时,必须按照可能的最大空间的需求量分配
      • 估计过大,容易导致分配的存储空间不能得到充分使用
      • 估计过小,空间容量的扩充通常比较困难
  • 进行插入和删除操作时,需要先对插入或删除位置后面的所有数据元素逐个进行移动,操作的时间效率低,尤其当表较长,且插入或删除点的位置靠前时

顺序存储结构比较适合于线性表的长度不经常发生变化,或者只需要在顺序存取设备上做批处理的场合。