【数据结构与算法】顺序表的实现

版权声明:转载请注明出处!欢迎大家提出疑问或指正文章中的错误! https://blog.csdn.net/pyuxing/article/details/89187822

本文系在学习数据结构过程中做的笔记记录,主要参考书籍为《大话数据结构》《算法导论》,文中如有纰漏欢迎指正。

1-线性表的定义

线性表(List):零个或者多个数据元素的有限序列

用数学语言可以表述如下:
若线性表记为 ( a 1 , , a i 1 , a i , a i + 1 , , a n ) (a_1, ···, a_{i-1}, a_i, a_{i+1}, ··· , a_n) ,则表中 a i 1 a_{i-1} 领先于 a i a_i a i a_i 领先于 a i + 1 a_{i+1} ,称 a i 1 a_{i-1} a i a_i 的直接前驱元素, a i + 1 a_{i+1} a i a_i 的直接后继元素。当 i = 1 2 , n 1 i = 1 , 2 ,··· ,n-1 时, a i a_i 有且仅有一个直接后继,当 i = 2 3 , n i = 2 , 3 ,··· ,n 时, a i a_i 有且仅有一个直接前驱。 如下图所示:

图1 线性表图示

所以线性表元素的个数 n ( n 0 ) n(n≥0) 定义为线性表的长度,当 n = 0 n=0 时,称为空表。

2-线性表的抽象数据类型

线性表的抽象数据类型可以定义如下:

ADT 线性表(List)
Data
operation:
    InitList(*L) ://初始化操作,建立一个空的线性表L
    ListEmpty(L) ://若线性表为空,返回true,否则返回false
    ClearList(*L) ://将线性表清空
    GetElem(L, i, *e) : //将线性表L中的第i个元素返回给e
    LocateElem(L, e) : //在线性表中查找与指定元素e相等的元素,如果查找成功,返回元素在表中的序号;否则返回0表示失败
    ListInsert(*L, i, e) : //在线性表L中的第i个位置插入新元素e
    ListDelete(*L, i, e) : //删除线性表中第i个位置的元素,并用e返回其值
    ListLeng(L) : //返回线性表L的元素个数
endADT

对于不同的应用,线性表的基本操作是不同的,上述操作是最基本的,对于实际问题中涉及的关于线性表的更复杂的操作,可以利用这些基本操作的组合来实现。

3- 线性表的顺序存储结构

1-顺序存储的定义

  • 线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

线性表 ( a 1 , , a i 1 , a i , a i + 1 , , a n ) (a_1, ···, a_{i-1}, a_i, a_{i+1}, ··· , a_n) 的顺序存储示意图如下:

图1 线性表顺序存储示意

2-顺序存储方式
因为线性表的每个数据元素的类型都相同,所以可以用一维数组来实现顺序存储结构,即把第一个数据元素存储到数组下标为0的位置中,接着把线性表相邻的元素存储在数组中的相邻的位置。
下面是线性表顺序存储结构代码:

#define MAXSIZE 20 //存储空间初始分配量
typedef int ElemType; //ElemType类型根据实际情况而定,这里假设为int
typedef struct
{
    ElemType data[MAXSIZE];//数组存储数据元素,最大值为MAXSIZE
    int length;//线性表当前长度
}Sqlist;    

这里需要注意顺序存储结构的3个属性:

  • 存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置
  • 线性表的最大存储容量:数组长度MAXSIZE
  • 线性表的当前长度:length

线性表的长度时线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是可以变化的,在任何时刻,线性表的长度应该小于等于数组的长度

3-地址计算方法
用数组存储顺序表意味着要分配固定的长度的数组空间,由于线性表中可以进行插入和删除操作,因此分配的数组空间要大于当前线性表的长度。
假设一个元素占用 c c 个存储单元,那么线性表中第 i + 1 i+1 个数据元素的存储位置和第 i i 个元素的存储位置满足如下关系( L O C LOC 表示获得存储位置的函数): L O C ( a i + 1 ) = L O C ( a i ) + c LOC(a_{i+1}) = LOC(a_i) + c ,所以对第 i i 个数据元素的存储位置可以用 a 1 a_1 推算出来: L O C ( a i + 1 ) = L O C ( a 1 ) + ( i 1 ) c LOC(a_{i+1}) = LOC(a_1) + (i-1) * c

图3 线性表顺序存储示意

4-顺序存储结构的插入与删除

1-获得元素操作
对于线性存储结构来说,如果我们要实现GetElem操作,即将线性表中第i个位置的元素值返回,下面是实现代码:

#define OK 1
#define ERROE 0
typedef int Status;
//Status是函数的类型,其值是函数结果状态代码,如OK等
//初始条件:顺序表 L 已经存在,1 ≤ i ≤ListLength(L)
//操作结果: 用 e 返回L中的第 i 个元素的值
Status GetElem(Sqlist L, int i, ElemType *e)
{
    if(L.length == 0 || i < 1 || i > L.length)     return ERROR;
    *e = L.data[i-1];
    return OK;
}

2-插入操作
插入算法的思路:

  • 如果插入位置不合理,抛出异常
  • 如果线性表长度大于或等于数组长度,则抛出异常或动态增加容量
  • 从最后一个元素开始向前遍历到第 i 个位置,分别将它们都向后移动一个位置
  • 将要插入元素填入位置 i 处
  • 表长加 1

实现代码如下:

//初始条件:顺序表 L 已经存在,1 ≤ i ≤ListLength(L)
//操作结果: 在L中第 i 个位置之前插入新的数据元素e, L的长度加 1
Status ListInsert(Sqlist *L, int i, ElemType e)
{
    if(L->length == MAXSIZE)    return ERROR;//顺序表已填满
    if(i < 1 || i > L->length + 1)     return ERROR;//i 不在范围内
    if(i <= L->length){//插入数据不在表尾
        for(int k = L->length - 1; k >= i-1; k--){//将要插入位置后的元素向后移动一位
            L->data[k+1] = L->data[k];
        }
    }
    L->data[i] = e;//新元素插入
    L->length ++;
    return OK;
}

3-删除操作
删除算法的思路:

  • 如果位置不合理,抛出异常
  • 取出删除元素
  • 从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置
  • 表长加 1

实现代码如下:

//初始条件:顺序表 L 已经存在,1 ≤ i ≤ListLength(L)
//操作结果: 删除L中的第 i 个元素, 并用e返回其值,L的长度减 1
Status ListDelete(Sqlist *L, int i, ElemType *e)
{
    if(L->length == 0)    return ERROR;//顺序表为空
    if(i < 1 || i > L->length)     return ERROR;//删除位置不正确
    *e = L->data[i - 1];
    if(i < L->length){//如果删除不是最后位置
        for(int k = i; k < L->length; k++){//将删除位置后继元素前移
            L->data[k-1] = L->data[k];
        }
    }
    L->length --;
    return OK;
}

5-优缺点

优点:

  • 无须为表示表中的元素之间的逻辑关系增加额外的存储空间
  • 可以快速地存取表中任一位置的元素

缺点:

  • 插入和删除操作需要移动大量元素
  • 当线性表长度变化较大时,难以确定存储空间的容量
  • 造成存储空间的“碎片”

PS:公众号上线啦,技术干货分享,欢迎关注。

猜你喜欢

转载自blog.csdn.net/pyuxing/article/details/89187822