数据结构实践.线性表_2

线性表的顺序表示与实现:

  1. 顺序表示

    顺序存储:将逻辑上相邻的数据元素存储到物理上也相邻的存储单元,即逻辑上相邻,物理上也相邻

    基地址:顺序表中第一个元素 a 1 a_{1} a1 的存储位置,由于线性表的顺序存储结构占用的是一片连续存储空间,因此知道首元素的地址就可以通过元素下标访问任意元素。

    随机访问:又称随机存取,指访问线性表中任意元素的时间相同,线性表就是顺序存储。

  2. 特点与实现

    顺序表中的数据元素具有以下特点:
       a. 地址连续;
       b. 依次存放;
       c. 随机存取;
       d. 类型相同;
    这些特点与数组元素相似性很高,考虑采用一维数组的形式来实现顺序表:

//使用 *静态分配* 数组定义线性表
# define Max_size 1000          //顺序表存储空间的初始分配大小
typedef char ElemType;          //定义数据元素类型

typedef struct{
    ElemType elem[Max_size];
    int length;                 //顺序表表长(即数据元素个数)
} Sqlist;
*由于线性表长度可变(插入、删除等操作)但数组长度不可动态定义,因此通过整型变量 length 来表示顺序表的
 长度属性(即定义一个大数组,使用其中的一部分作为线性表)
# define Max_size 1000          //顺序表存储空间的初始分配大小
typedef char ElemType;          //定义数据元素类型

typedef struct{
    ElemType * elem;
    int length;                 //顺序表表长(即数据元素个数)
} Sqlist;

Sqlist L;
L.elem = (ElemType *)malloc(sizeof(ElemType) * Max_size);         //动态分配内存空间
L.length = 0;                   //空表(length = 0)
*通常建议采用动态分配内存的数组作为建立顺序表的对象
*一般而言,顺序表的定义分为 3 步:
	1)定义存放线性表所用的一维数组的最大长度(最大表长);
	2)定义顺序表的数据元素类型;
	3)声明线性表,分配内存空间并初始化表长。

由此得到的顺序表如下:

顺序表图示
  通过 L . e l e m [ i ] L.elem[i] L.elem[i] 访问第 i + 1 i+1 i+1 个成员;通过 L . l e n g t h L.length L.length 访问顺序表成员数量(即表长)
  需要注意的是,顺序表的逻辑位序与物理位序相差1。

Eg. 图书表的顺序存储结构类型定义:

//图书表定义
# define Max_size 10000          //图书表最大长度

//声明图书元素
typedef struct{
    char no[20];                //ISBN
    char name[50];              //图书名字
    float price;                //图书价格
} Book;

typedef struct{
    Book * elem;
    int length;                 //图书表表长
} Book_Sqlist;
  1. 基本操作与其实现
    3.1 初始化线性表 L L L:
//异常
# define OVERFLOW -2            //溢出异常
# define OK 0                   //正常

typedef int status;

//初始化顺序表L
status InitSqList(SqList &L){
    L.elem = new ElemType[Max_size];             //动态分配内存空间
    if(!L.elem){                         //检查是否分配内存成功
        exit(OVERFLOW);                      //分配失败则退出程序
    }
    L.length = 0;                //初始化表长
    return OK;
}
*return 返回一个值给其上层函数,但是不立刻结束程序,其中0表示正常结束,-1表示程序异常。
*exit(int) 立刻终止程序,其中exit(0)表示正常退出,除0以外的值都表示异常退出。

  3.2 销毁顺序表 L L L:

//销毁顺序表L
void DestroyList(SqList &L){
    if(L.elem)                  //检测线性表L是否存在
        delete L.elem;           //释放线性表L的存储空间
}

  3.3 清空顺序表 L L L:

//清空线性表L 
void ClearList(SqList &L){
    L.length = 0;              //将表长L.length重置为0
}
*虽然此时线性表L中的elem依然有元素,但是在下次使用时会覆盖

  3.4 获得顺序表 L L L长度:

//获得线性表L的表长
int GetLength(SqList L){
    return L.length;            //返回线性表表长
}

  3.5 判断顺序表 L L L是否为空:

//判断线性表L是否为空
int IsEmpty(SqList L){
    if(L.length == 0)                   //如果为空,返回1
        return 1;
    else
        return 0;                       //否则,返回0
}

  3.6 顺序表 L L L取值:

# define ERROR 1                //错误

//顺序表的取值
//依据位置i获取相应位置数据元素的内容
int GetElem(SqList L, int i, ElemType &e){
    if(i<1||i>L.length)
        return ERROR;                 //判断取值位置是否合法
    e = L.elem[i-1];                  //将对应成员的值返回变量e
    return OK;
}

  3.7 顺序表 L L L的查找
算法思想:
  在顺序表中从序号1一直查找到最后一个元素,找到则退出循环,返回元素序号;否则查找失败。

//顺序表的查找
//按值查找:在顺序表中查找与指定值e相同的数据元素,返回其序号
int LocateElem(SqList L, ElemType e){
    for (int i = 0; i < L.length; i++)
        if(L.elem[i]==e)
            return i + 1;           //查找成功则返回其序号
    return 0;
}

*算法分析
  为确定记录在表中的位置,需要与给定值进行比较的关键字的个数的期望即为平均查找长度ASL。因此对含有n个元素的顺序表,查找成功时有

          A S L = ∑ i = 1 n P i C i ASL = \sum_{i=1}^{n} P_{i}C_{i} ASL=i=1nPiCi
  其中
     P i P_{i} Pi为第 i i i个元素被查找的概率;
     C i C_{i} Ci为找到第 i i i个元素需要比较的次数;

∵ \because 顺序表中各个元素被查找的概率相等, P i = 1 n P_{i}=\frac{1}{n} Pi=n1, C i = i C_{i}=i Ci=i;
∴ \therefore A S L = ∑ i = 1 n i n = 1 2 ( n + 1 ) ASL = \sum_{i=1}^{n}\frac{i}{n}=\frac{1}{2}(n+1) ASL=i=1nni=21(n+1)
∴ \therefore T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n)

  3.8 顺序表 L L L的插入
算法思想:
  Step.1 判断插入位置 i i i是否合法 ,线性表 L L L是否会溢出;

  Step.2 将第 n n n位至第 i i i位的元素依次后移一个位置,空出位置 i i i;
  Step.3 将新元素 e e e插入到位置 i i i;
  Step.4 表长L.length+1,返回OK。

//顺序表的插入
status SqListInsert(SqList &L, int i, ElemType e){
    if(i<1||i>L.length+1)
        return ERROR;              //插入位置i不在[1,n+1]中,返回ERROR
    if(L.length == Max_size)
        return ERROR;               //顺序表已满,继续插入会造成溢出
    for (int j = L.length - 1; j >= i - 1; j--){
        L.elem[j + 1] = L.elem[j];          //将[i,n]的元素依次后移
    }
    L.elem[i - 1] = e;                 //插入元素e
    L.length += 1;                  //表长+1
    return OK;
}
插入位置:
	1) 插入位置在开头(i = 1);
	2) 插入位置在中间(i = 2~n);
	3) 插入位置在最后(i = n+1);
	共计n+1个插入位置。
异常:
	1) 插入位置i不合法:i > L.length 或 i < 0;
	2) 顺序表已满,继续插入会造成溢出;

*算法分析
  考虑顺序表中元素的平均移动次数AMN(一共n+1个位置可插入):

          A M N = ∑ i = 1 n + 1 P i M i AMN = \sum_{i=1}^{n+1} P_{i}M_{i} AMN=i=1n+1PiMi
  其中
     P i P_{i} Pi为第 i i i个位置被插入的概率;
     M i M_{i} Mi为插入第 i i i个位置时元素需要移动的次数;

∵ \because 顺序表中各个元素被查找的概率相等, P i = 1 n + 1 P_{i}=\frac{1}{n+1} Pi=n+11, M i = n + 1 − i M_{i}=n+1-i Mi=n+1i;
∴ \therefore A M N = ∑ i = 1 n + 1 n + 1 − i n + 1 = n 2 AMN = \sum_{i=1}^{n+1}\frac{n+1-i}{n+1}=\frac{n}{2} AMN=i=1n+1n+1n+1i=2n
∴ \therefore T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n)

  3.9 删除顺序表 L L L中的元素

算法思想:
  Step.1 判断插删除位置 i i i是否合法 , i ∈ [ 1 , n ] i\in [1,n] i[1,n];

  Step.2 将欲删除的元素保留在 e e e中;
  Step.3 将第 i + 1 i+1 i+1至第n位的元素依次前移一个位置;
  Step.4 表长L.length-1,返回OK。

//顺序表的删除
//删除顺序表中的第i个位置的元素并用e返回
status SqListDelete(SqList &L, int i, ElemType &e){
    if(i<1||i>L.length)
        return ERROR;              //删除位置i不在[1,n]中,返回ERROR
    if(L.length == 0)
        return ERROR;              //空表,无法删除元素
    e = L.elem[i - 1];              //将欲删除的值用e返回
    for (int j = i - 1; j < L.length;j++){
        L.elem[j - 1] = L.elem[j];          //第i到第n个元素依次前移
    }
    L.length--;                 //表长减1
    return OK;
}

*算法分析
  考虑顺序表中元素的平均移动次数AMN(一共n个位置可删除元素):

          A M N = ∑ i = 1 n P i M i AMN = \sum_{i=1}^{n} P_{i}M_{i} AMN=i=1nPiMi
  其中
     P i P_{i} Pi为第 i i i个位置的元素被删除的概率;
     M i M_{i} Mi为删除第 i i i个位置时元素需要移动的次数;

∵ \because 顺序表中各个元素被查找的概率相等, P i = 1 n P_{i}=\frac{1}{n} Pi=n1, M i = n − i M_{i}=n-i Mi=ni;
∴ \therefore A M N = ∑ i = 1 n n − i n = 1 2 ( n − 1 ) AMN = \sum_{i=1}^{n}\frac{n-i}{n}=\frac{1}{2}(n-1) AMN=i=1nnni=21(n1)
∴ \therefore T ( n ) = O ( n ) T(n) = O(n) T(n)=O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值