C语言之线性表
文章目录
一、数据结构
数据结构分为逻辑结构和物理结构
1.逻辑结构
数据对象中,数据元素的相互关系。
-
集合结构
集合结构中的元素,除了同属一个集合外,它们没有任何关系。元素之间是“平等的”,共同属性是“同属一个集合”。
-
线性结构
线性结构中元素是一对一的关系。
-
树形结构
树形结构中数据元素之间存在一种一对多的层次关系。
-
图形结构
图形结构元素之间是多对多的关系。
总结:逻辑结构是针对具体问题的,是为了解决某个问题的,在理解问题的基础上,选择一个合适的数据结构表示数据元素之间的逻辑关系。
2.物理结构
物理结构:是指数据的逻辑结构在计算机中的存储方式;分为两种,顺序结构和链式结构
- 顺序结构
把数据元素放到地址连续的存储单元里,其数据的逻辑关系和物理关系是一致的。
这种存储结构就是排队占位,大家按照顺序排好,每个人一段空间,谁也不谁的队。(类似数组) - 链式结构
把数据元素存放到任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的。
二、线性表的定义
线性表:零个或者多个数据元素有序的排列。
举例:
在广场上,有大人,有小孩,有宠物,他们散在广场各处,这些小朋友数据对广场来说不能算是线性表;但像一个班级的小朋友,一个开头,一个结尾,当中的小朋友都知道自己的前方是谁,后方是谁,这样如同一根把他们串起来,就可以称为线性表。
三、线性表的特点
1.有序性
元素之间有顺序,若元素存在多个,则第一个元素无前驱,最后一个元素无后继,
2.有限性
线性表是有限的,小朋友也是有限的,用数学语言来定义可如下:
3.线性表的抽象数据类型
线性表的抽象数据类型如下:
线性表(List)
Data
线性表的数据对象集合为(a1,a2...an),每个元素的类型均为DataType。其中,除一个元素为a1外,每个元素有且只有一个前驱元素,除最后一个元素an外,每个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
InitList(*L); 初始化操作,建立一个空的线性表L。
ListEmpty(*L); 若线性表为空,返回 true,否则返回fase;
CleatList(*L); 将线性表清空
GetElem(L,i,*e); 将线性表L中的第i个位置元素值返回给e
LocateElem(L,e);在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中的序号;否则返回0表示失败
四、线性表之顺序存储结构
1.概念
线性表的顺序存储结构,指的是用一段连续的存储单元一次存储线性表的数据元素。
2.存储方式
线性表的顺序存储结构,说白了,就是在内存中找了一块地儿,通过占位的方式,占用一定内存空间,然后把相同数据类型的数据元素一次放入到这块内存中。
因线性表的每个数据元素的类型都相同,所以可以用C语言中的一维数组来实现顺序存储结构。
2.代码演示(VS2019)
#define MAXSIZE 20 //存储空间的分配量
typedef int Elemtype; //Elemtype 类型根据实际情况而定,这里假设为int类型
typedef int Status;
struct sqList
{
Elemtype data[MAXSIZE]; //数组存储数据元素,最大值为MAXSIZE
int length; //线性表长度
};
typedef struct sqList sqList; //别名
第二种方式,与上述方式一致
typedef struct sqList
{
Elemtype data[MAXSIZE]; //数组存储数据元素,最大值为MAXSIZE
int length; //线性表长度
}sqList;
结构体初始化
sqList L = {
{1,2,3,4,5,6,7,8},
8
};
根据结构体可以发现顺序存储结构的三个属性:
1.存储空间的其实位置:数组data,他的存储位置就是顺序结构的空间的存储位置。
2.线性表的最大容量:数组长度MAXSIZE。
3.线性表当前的长度:length
获取表值——算法思路:
只要i的数值在数组的下标范围内,就把数组的第i-1下标的值返回即可。
函数功能:线性表顺序存储结构,实现GetElem操作
已知条件:顺序线性表L存在
传递参数:结构体指针,第i个元素,存储到e所指的内存空间
返回结果:用e返回L中的第i个数据元素的值
Status GetElem(sqList *L,int i,Elemtype *e)
{
if (L->length == 0 || i >L->length || i < 1)
return ERROR;
*e = L->data[i - 1];
return OK;
}
表中插入数据——算法思路:
- 如果插入位置不合格,报出异常。
- 如果线性表长度大于数组长度,报出异常。
- 从最后一个元素开始向前遍历到第i个位置,分别将它们向后移动一个位置。
- 将要插入元素填入位置i处
- 表长加1
函数功能:线性表顺序存储结构,实现插入操作
已知条件:顺序线性表L存在
传递参数:结构体指针,第i个元素,将e的值插入表中
返回结果:
Status ListInsert(sqList* L, int i, Elemtype e)
{
int k;
if(L->length==MAXSIZE) //线性表已经满了
return ERROR;
if ( i > L->length+1 || i < 1) //插入i位置不在线性表范围内
return ERROR;
if (i <= L->length)
{
for (k = L->length - 1; k >= i - 1; k--)
{
L->data[k + 1] = L->data[k];
}
}
L->data[i - 1] = e;
L->length++;
return OK;
}
表中删除数据——算法思路:
- 如果删除位置不合格,报出异常。
- 取出删除元素
- 从删除元素位置开始向后遍历到最后一个元素位置,分别将它们向前移动一个位置。
- 将要插入元素填入位置i处
- 表长减1
函数功能:线性表顺序存储结构,实现删除线性表指定位置数据
已知条件:顺序线性表L存在
传递参数: L线性表地址,位置i
返回结果:用e返回L中被删除的第i个数据元素的值
Status ListDelete(sqList* L, int i, Elemtype *e)
{
int k;
if (i > L->length || i < 1) //插入i位置不在线性表范围内
return ERROR;
*e = L->data[i-1];
for (k=i; k<L->length;k++)
{
L->data[k- 1] = L->data[k];
}
L->length--;
return OK;
}
主函数
int main()
{
int a,b=12;
Elemtype *temp=&a;
GetElem(&L,7, temp); //将线性表的地址传递给函数
printf_s("data1=%d\n", *temp);
ListInsert(&L,7,b);
GetElem(&L, 7, temp); //将线性表的地址传递给函数
printf_s("data2=%d\n", *temp);
GetElem(&L, 8, temp); //将线性表的地址传递给函数
printf_s("data3=%d\n", *temp);
ListDelete(&L, 7, temp);
printf_s("data4=%d\n", *temp);
}
指针在使用前要赋值
指针覆值的两种方式:
定义指针的同时就进行赋值:
int a;
int* p = &a;
先定义指针变量后在赋值
int a;
int* p;
p = &a;
& 是取地址,*是引用地址里存储的值
int main()
{
此代码在传递参数时,会运行出错,指针要赋初值
Elemtype* temp = NULL;
GetElem(&L, 7, temp);
printf_s("data=%d\n", *temp);
}
总结
线性表的优缺点:
优点:
-
无需为表示线性表中元素之间的逻辑关系而增加额外的存储空间
-
可以快速的存取表中的任一位置的元素
缺点: -
插入和删除操作需要移动大量元素
-
当线性表长度变化较大时,难以确定存储空间
-
造成存储空间的碎片