01--线性表的顺序存储

404 阅读5分钟

线性表是数据结构中的线性结构,关于线性结构的相关知识可以查看文章---01-数据结构与算法基础知识

准备工作

这里我们使用的IDE是XCode,使用的语言是C语言,当然,你也可以使用别的IDE和别的计算机语言。

  • 1.在Xcode上创建一个MacOS命令行工程
  • 2.在main.c文件中定义一些数据类型和状态;
#define MAXSIZE 100 //线性表的长度

#define OK 1        //执行成功的状态

#define ERROR 0     //执行错误的状态

#define TRUE 1      //

#define FALSE 0     //

/* ElemType类型根据实际情况而定,这里假设为int */

typedef int ElemType;  //数据类型重定义

/* Status是函数的类型,其值是函数结果状态代码,如OK等 */

typedef int Status;   //数据类型重定义

一、线性表的设计

线性表的特性:

  • 有唯一的第一个元素和最后一个元素;
  • 除第一个元素和最后一个元素外的其他元素都有一个前驱和一个后续
  • 第一个元素没有前驱,只有后续,最后一个元素有前驱,没有后续。

结合这个特性我们即可以将线性表设计成顺序存储,也可以设计成链式存储,这里实现线性表的顺序存储

我们使用结构体来设计一个线性表:

typedef struct {

    ElemType *data; //存储数据

    int length;     //记录数据量

}Sqlist;

data是一个指针,可以使用它来存储数据;length是一个整型数据,用于记录当前存储的数据量,方便对线性表进行清空、控制线性表的数据量和获取线性表的长度。

二、线性表的初始化

Status InitList(Sqlist *L){

    //为线性表分配一个大小为MAXSIZE 的数组空间

    L->data =  malloc(sizeof(ElemType) * MAXSIZE);

    //存储分配失败退出

    if(!L->data) exit(ERROR);

    //空表长度为0
    L->length = 0;

    return OK;

}

使用malloc分配一块内存,长度由数据类型和线性表的长度决定;初始化时线性表是没有数据的,所以length为0

三、线性表的插入

Status ListInsert(Sqlist *L,int i,ElemType e){
    //i值不合法判断
    if((i<1) || (i>L->length+1)) return ERROR;

    //存储空间已满
    if(L->length == MAXSIZE) return ERROR;
    
    //插入数据不在表尾,则先移动出空余位置
    if(i <= L->length){
        for(int j = L->length-1; j>=i-1;j--){
            //插入位置以及之后的位置向后移动1位
            L->data[j+1] = L->data[j];
        }
    }
    //将新元素e 放入第i个位置上
    L->data[i-1] = e;

    //长度+1;
    ++L->length;
    return OK;
}
  • i是插入的位置,应该在有数据的位置表尾才能插入,非表尾的没有数据的位置插入是不合法的,因为这不符合线性表的特性;
  • 当线性表的数据长度等于最大数据长度时,说明表已满,就不能再插入数据了;
  • 如果不是插入到表尾,则需要把插入的位置空出来,所以要把插入位置后面的数据向后移动
  • 线性表的下标是从0开始的,但人的思维是从1开始的,所以数据插入的真正位置应该是i-1的位置;
  • 插入数据后,线性表的长度也要+1

四、线性表的删除

Status ListDelete(Sqlist *L,int i){
    //线性表为空
    if(L->length == 0) return ERROR;

    //i值不合法判断
    if((i<1) || (i>L->length)) return ERROR;
    
    for(int j = i; j < L->length;j++){
        //被删除元素之后的元素向前移动
        L->data[j-1] = L->data[j];
    }
    //表长度-1;
    L->length --;
    return OK;
}
  • 删除时要判断是否为,为空的话删除是无意义的;
  • 删除的位置要在合法的范围之内:0 ~ length-1;
  • 删除了某个位置的数据后,需要把后面的数据向前移动一位,以满足线性表的特性;
  • 删除成功后,长度也要-1

五、线性表的取值

Status GetElem(Sqlist L,int i, ElemType *e){
    //判断i值是否合理, 若不合理,返回ERROR
    if(i<1 || i > L.length) return ERROR;

    //data[i-1]单元存储第i个数据元素.
    *e = L.data[i-1];
    return OK;
}
  • 取值时也要判断所取的位置是否有数据,不然后导致数组越界,引发crash;
  • 合法的取值范围是:0 ~ length-1;
  • *e是函数调用时传入的地址,这种地址的赋值和函数外部访问的是同一内存地址。

六、线性表改值

Status setListIndex(Sqlist L,int i,ElemType e){

    if (i < 1 || i > L.length) {
        return ERROR;
    }
    L.data[i-1] = e;
    return OK;
}

合法的取值范围是:0 ~ length-1,所以只能修改这个范围内的数据

七、清空线性表

Status ClearList(Sqlist *L)

{
    L->length=0;
    return OK;
}
  • 线性表的增、删、改、查都是基于length的,由length来决定当前线性表有多少个数据,所以清空线性表时只需要将length置为0就不会访问到数据了,即线性表被清空了。

八、获取线性表的长度

int ListLength(Sqlist L)

{
    return L.length;
}

length就是线性表的长度

九、线性表的判空

Status ListEmpty(Sqlist L)

{
    if(L.length==0)
       return TRUE;
    else
       return FALSE;
}

length就是线性表的长度,length大于0,则线性表不为空,否则为空。

十、打印线性表

Status TraverseList(Sqlist L)
{
    int i;
    for(i=0;i<L.length;i++)
       printf("%d\n",L.data[i]);
    printf("\n");
    return OK;
}

按顺序遍历线性表

十一、总结

从上面对线性表的设计可以看出:

  • 线性表的顺序存储查、改很方便,但是增、删很麻烦,增删后为了保证顺序存储的内存的连续性,需要移动大量的数据;
  • 顺序存储时内存是连续的,我们也可以通过指针移动的方式来遍历它。