第2章 线性表之顺序表示与实现

118 阅读6分钟

第2章 线性表之顺序存储

自定义头文件

#include <stdio.h>/* 状态码 */
#define TRUE        1   // 真/是
#define FALSE       0   // 假/否
#define OK          1   // 通过/成功
#define ERROR       0   // 错误/失败//系统中已有此状态码定义,要防止冲突
#ifndef OVERFLOW
#define OVERFLOW    -2  //堆栈上溢
#endif//系统中已有此状态码定义,要防止冲突
#ifndef NULL
#define NULL ((void*)0)
#endif/* 状态码类型 */
typedef int Status;
typedef int ElemType;
​
/* 布尔类型 */
typedef int Boolean;

线性结构: 线性表、栈、队列、串和数组。

线性结构基本特点: 除第一个元素无直接前驱,最后 一 个元素无直接后继之外,其他每个数据元素都有一个前驱和 后继。

线性表: 零个或多个数据元素的有限序列。

2.1 线性表的抽象数据类型

ADT List{
    数据对象:D
    数据关系:S
    基本操作:
    InitList(&L)//初始化操作,建立一个空的线性表L
    DestroyList(&L)//销毁线性表,并释放线性表L所占用的内存空间。
    CreateList(&L,len)//表的扩容操作
    ListEmpty(L)//若线性表为空,返回true,否则返回false        
    ClearList(&L)//将线性表清空    
    GetElem(L,i,&e)//按位查找    
    LocateElem(L,e)//按值查找    
    ListInsert(&L,i,e)//在线性表第i个位置插入新元素e    
    ListDelete(&L.i,&e)//删除线性表第i个位置的元素,并用e返回值    
    ListLength(L)//返回线性表L的元素个数    
    PrintList(L)//前后顺序输出线性表L的所有元素值    
}ADT List;

假设La表示线性表A,Lb表示线性表B;求在线性表Lb而不在线性表La中的数据插入到La中

//顺序表的合并
Status ListUnion(SqList& La, SqList Lb) {
    int La_len = La.length;
    int Lb_len = Lb.length;
    //循环Lb中的每一个元素
    for (int i = 0; i < Lb_len; i++) {
        //如果La中存在与Lb相等的值则返回位序,未找到返回0
        int exit = LocatElem(La, Lb.data[i]);
        if (exit == 0) {//说明在La中没有找到和Lb相同的元素
            //插入之前,看是否需要扩容
            if (La.length == La.MaxSize) CreateList(La, 10);
            //调用插入函数
            ListInsert(La, La.length ++, Lb.data[i]);
        }
    }
    return OK;
}

当你传递一个参数给函数的时候,这个参数会不会在函数内被改动。决定了使用什么参数形式,如果需要被改动,则需要传递指向这个参数的指针。如果不用被改动,可以直接传递这个参数。

2.2 线性表的顺序存储结构

顺序表(Sequent ialList ) :采用顺序存储结构的线性表为顺序表。

其特点是,逻辑上相邻的数据元素, 其物理次序也是相邻的。

假设线性表的每一个数据占用c个存储单元,那么第i个元素的存储单元为

  • Loc(ai)=Loc(a1)+(i-1)*c
//数组静态分配
# include<stdio.h>
# define InitSize 10 // 线性表初始容量
# define ElemType int// ElemType根据你要存什么元素而确定类型
typedef struct {
    ElemType data[InitSize]; // 用静态的“数组”存放数据元素
    int length;// 顺序表当前长度
} SqList;
​
//数组动态分配
# include<stdio.h>
# define InitSize 10 // 线性表初始容量
# define ElemType int// ElemType根据你要存什么元素而确定类型
typedef struct {
    ElemType *data; // 用静态的“数组”存放数据元素
    int MaxSize;// 用于扩容的字段
    int length;// 顺序表当前长度
} SqList;

ps:在任意时刻,线性表的长度应该小于数组的长度

2.3 顺序表基本操作的实现

顺序表的初始化

① 为顺序表L动态分配一个预定义大小的数组空间,使data指向这段空间的基地址。

②将表的当前长度设为0。

//初始化顺序表
Status InitList(SqList& L) {
    //分配空间方式一:malloc
    L.data = (ElemType*)malloc(InitSize * sizeof(ElemType));//为局部指针变量分配空间
    // 分配空间方式二:C++的new分配
    //L.data = new ElemType(InitSize);//为顺序表分配一个大小为InitSize的数组空间
    if (!L.data) exit(OVERFLOW);//内存空间分配失败
    L.MaxSize = InitSize;//初始化最大空间
    L.length = 0;//将线性表的长度置空
    return OK;
}

顺序表的基础操作

//清空线性表
void ClearList(SqList& L) {
    for (int i = 0; i < L.length; i++) {
        L.data[i] = NULL;//将每一个元素释放
    }
    L.length = 0;//长度置空
    printf("线性表已清空!");
}
​
//销毁顺序表
void DestroyList(SqList& L) {
    //前提条件:存在此空间
    if (L.data) {
        //new p→delete p;   malloc p→free(p)
        free(L.data);//删除分配空间
        L.data = NULL;//基地址置空
    }
    L.length = L.MaxSize = 0;//表空间和表长度置空
    printf("顺序表已销毁!\n");
}
//顺序表判空
Status ListEmpty(SqList L) {
    //前提条件不是空表
    if (L.length != 0) return 0;//不是空表
    return 1;//是空表
}
//顺序表的遍历
void ListPrint(SqList L) {
    for (int i = 0; i < L.length; i++) {
        printf("data[%d]=%d\n", i, L.data[i]);
    }
}

malloc(m)函数,开辟m字节长度的地址空间,并返回这段空间的首地址

sizeof(x)运算,计算变量x的长度

free(p)函数,释放指针p所指变量的存储空间,即彻底删除一个变量

delete 指针p,释放指针P所指向的内存。P必须建new操作的返回值

顺序表的扩容

//顺序表的扩容操作
Status CreateList(SqList& L, int len) {
    //1、将原来的空间备份一份
    ElemType* p = L.data;
    //2、重新分配一个增加len后的新空间
    L.data = (ElemType*)malloc((L.MaxSize + len) * sizeof(ElemType));
    if (!L.data) exit(OVERFLOW);//空间分配失败
    //3、将每个元素放入新空间
    for (int i = 0; i < L.length; i++) {
        L.data[i] = p[i];
    }
    //4、线性表的长度+len
    L.MaxSize += len;
    //5、释放临时空间
    free(p);
    return OK;
}

顺序表的查找

按位查找:

  • ① 判断指定的位置序号i值是否合理(1≤i≤L.length ),若不合理,则返回ERROR。
  • ② 若i值合理,则将第i个数据元素L.data[i-1]赋给参数e,通过e返回第i个数据元素的传值。

按值查找:

  • ① 从第一个元素起,依次和e相比较,若找到与e相等的元素L.data[i],则查找成功,返回该元素的序号i+1。
  • ② 若查遍整个顺序表都没有找到,则查找失败,返回0。
//顺序表的按位查找
Status GetElem(SqList L, int i, ElemType& e) {
    if (L.length == 0) return NULL;//顺序表中没有元素
    if (i<1 || i>L.length) return ERROR;//插入位置不合理
    e = L.data[i - 1];//第data[i-1]的存储单元存放第i个元素
    return OK;
}
​
//顺序表的按值查找
Status LocatElem(SqList L, ElemType e) {
    if (L.length == 0) return ERROR;//顺序表中没有元素
    for (int i = 0; i < L.length; i++) {
        if (L.data[i] == e) return i + 1;//查找成功,返回序号i+1;
    }
    return 0;//查找失败,返回0
}

顺序表的插入

① 判断插入位置i是否合法(i值的合法范围是1≤i≤n+1),若不合法则返回 ERROR。

②判断顺序表的存储空间是否已满,若满则返回ERROR。

③将第n个至第 i个位置的元素依次向后移动一个位置,空出第i个位置((i=n+1时无需移动)。

④将要插入的新元素e放入第i个位置。

⑤表长加1。

//顺序表的插入
Status ListInsert(SqList& L, int i, ElemType e) {
    if (i<1 || i>(L.length + 1)) return ERROR;//插入位置不合法
    if (L.length == L.MaxSize) return ERROR;//顺序表空间已满
    //非最后一个元素,需要挪动
    for (int j = L.length; j >= i; j--) {
        L.data[j] = L.data[j - 1];//插入位置及之后的的元素后移
    }
    //最后一个元素可以直接插
    L.data[i - 1] = e;//将新的元素放到第i个位置
    ++L.length;//插入元素后,表长+1
    return OK;
}

顺序表的删除

① 判断删除位置i是否合法(合法值为1≤i≤n),若不合法则返回ERROR。

②将第i+1个至第n个的元素依次向前移动一个位置( i=n时无需移动)

③表长减1。

//顺序表的删除
Status ListDelete(SqList& L, int i, ElemType& e) {
    if (i<1 || i>L.length) return ERROR;//判断删除位置是否合法
    if (L.data == 0) return ERROR; //空间没有可以删除的元素
    e = L.data[i - 1];//先将要删除的值返回出去
    for (int j = i - 1; j < L.length - 1; j++) {
        L.data[j] = L.data[j + 1];//被删除的元素之后依次前移
    }
    --L.length;//每删除一个元素,表长减1;
    return OK;
}