线性表是数据结构中的线性结构,关于线性结构的相关知识可以查看文章---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;
}
按顺序遍历线性表
十一、总结
从上面对线性表的设计可以看出:
- 线性表的
顺序存储下查、改很方便,但是增、删很麻烦,增删后为了保证顺序存储的内存的连续性,需要移动大量的数据;- 顺序存储时
内存是连续的,我们也可以通过指针移动的方式来遍历它。