知识点1 线性表
1 线性表的逻辑关系
线性逻辑结构,也称为线性表。线性表中的所有元素之间满足一对一的线性逻辑关系,具体如下
-
线性表中的起始元素有且仅有一个,该数据元素没有直接前驱,有且仅有一个直接后继。
-
线性表中的末尾元素有且仅有一个,该数据元素没有直接后继,有且仅有一个直接前驱。
-
线性表中的中间元素有且仅有一个直接前驱和直接后继。
2 线性表的定义
3 线性表的存储结构
线性表中的数据元素在内存中存储方式包含两种存储方式:
-
顺序存储结构:
所谓的顺序存储结构,指的是在内存中开辟连续存储空间,并将线性表中的元素按照存储位置序号连续存储。此时所构成的线性表称为顺序表。
类似于C语言程序中的数组,不同于数组的时候,需要保证元素连续存储。
-
链式存储结构:
所谓的链式存储结构,指的是采用非连续存储空间存储线性表中的所有数据元素,其中每一个数据元素需要存储当前元素的数据内容和后继数据元素存储空间的地址。可以通过数据元素存储空间的地址表示数据元素的逻辑关系。此时所构成的线性表称为链表。
类似于C语言中指针管理非连续内存块。
知识点2 顺序表
1 顺序表的基础操作
所谓的顺序表指的是线性逻辑结构的顺序存储。
1.1 构建顺序表的数据类型
/* 抽象数据类型的定义:需要存储数据元素的各个字段数据类型抽象并封装为整体
* 数据类型由实际需要存储数据元素进行类型的定义
*/
#if 1
/* 需要存储数据元素为学生信息,数据元素类型的定义 */
typedef struct Stu {
int id;
char name[32];
int score;
} data_t; /* 抽象数据类型名为data_t */
#else
/* 需要存储数据元素为int整型数据 *
typedef int data_t;
#endif
#define NUM 10
/* 顺序表存储空间的定义 */
typedef struct sqlist {
data_t data[NUM]; /* 连续存储空间的定义:使用数组表示 */
int last; /* 存储最后一个元素存储空间序号,默认为顺序表从起始元素序号0开始存储 */
} sqlist_t;
1.2 创建顺序表
/* 创建顺序表:
* 实质是开辟顺序表存储空间
* 1) 全局变量形式开辟,此时存储空间的生命周期为整个程序执行周期,存在空间利用率较低;
* 2) 局部变量形式开辟,此时存储空间的生命周期为函数调用周期,不能在其它函数中访问;
* 3) 动态管理,在需要使用的时候开辟空间,不需要的时候手动释放。
*/
sqlist_t *CreateSqList(void)
{
sqlist_t *list;
/* 动态开辟顺序表存储空间 */
list = malloc(sizeof(sqlist_t));
if (list == NULL)
return NULL;
/* 初始化顺序表存储空间元素 */
memset(list, 0, sizeof(sqlist_t)); /* 顺序表连续存储空间清零:将list起始地址开始的sizeof(sqlist_t)字节数据填充为数值0 */
list->last = -1; /* 置空表:末尾元素序号为默认起始元素序号(0)-1 */
return list; /* 返回顺序存储空间地址 */
}
1.3 顺序表遍历
所谓的顺序表的遍历,指的是从起始元素开始,依次顺序将顺序表中的所有数据元素访问且仅访问一次。
void DisplaySqList(sqlist_t *list);
{
int i;
/* 判断顺序表是否存在:list地址为NULL表示顺序表存储空间无效 */
if(list == NULL)
return;
/* 循环遍历顺序表中元素 */
for (i = 0; i <= list->last; i++) {
printf("%d ", list->data[i]);
}
printf("\n");
}
1.4 顺序表插入元素
/* 定义顺序表插入数据元素函数
* list表示顺序表的地址
* local表示插入数据元素的存储位置序号
* mydata表示需要插入的数据元素值
*/
int InsertSqList(sqlist_t *list, int local, data_t mydata)
{
/* 判断顺序表是否存在:list地址为NULL表示顺序表存储空间无效 */
if(list == NULL)
return;
/* 判断顺序表是否为满表 */
if (list->last == NUM-1)
return -1;
/* 判断插入数据元素的位置是否合法 */
if(local < 0 || local > list->last+1)
return -1;
/* 找插入元素的位置:需要从末尾元素开始直到插入元素的所有元素依次顺序向后偏移一个数据元素位置 */
for (i = list->last; i >= local; i--)
list->data[i+1] = list->data[i];
/* 插入元素并修改last游标 */
list->data[local] = mydata;
list->last ++; /* 添加新的数据元素,游标+1 */
return 0;
}
1.5 顺序表删除元素
/* 定义顺序表删除数据元素的函数接口
* list表示需要删除数据元素顺序表地址
* local表示删除元素位置序号
*/
int DeleteSqList(sqlist_t *list, int local)
{
int i;
/* 判断顺序表是否存在:list地址为NULL表示顺序表存储空间无效 */
if (list == NULL)
return -1;
/* 判断顺序表是否为空表 */
if (list->last == -1)
return -1;
/* 判断是删除元素的位置是否合法 */
if (local < 0 || local > list->last)
return -1;
/* 删除数据元素 */
for (i = local; i < list->last; i++)
list->data[i] = list->data[i+1];
list->last--;
return 0;
}
1.6 顺序表查询元素
int SearchSqList(sqlist_t *list, int local, data_t *mydata)
{
/* 判断顺序表是否存在:list地址为NULL表示顺序表存储空间无效 */
if(list == NULL)
return -1;
/* 判断顺序表是否为空表,如果为空表无数据可查询 */
if (list->last == -1)
return -1;
/* 所要查询元素的位置是否合法 */
if (local < 0 || local > list->last)
return -1;
/* 获取需要插入位置元素的数据值 */
*mydata = list->data[local];
return 0;
}
1.7 顺序表修改元素
int ChangeSqList(sqlist_t *list, int local, data_t newdata)
{
/* 判断顺序表是否存在:list地址为NULL表示顺序表存储空间无效 */
if(list == NULL)
return -1;
/* 判断顺序表是否为空表,如果为空表无数据可修改 */
if (list->last == -1)
return -1;
/* 所要修改元素的位置是否合法 */
if (local < 0 || local > list->last)
return -1;
/* 修改数据元素 */
list->data[local] = newdata;
return 0;
}
1.8 其它操作
/* 置空顺序表 */
void ClearSqList(sqlist_t *list)
{
/* 判断顺序表是否存在:list地址为NULL表示顺序表存储空间无效 */
if(list == NULL)
return;
list->last = -1;
}
/* 顺序表资源释放的函数接口定义
* 参数使用二级指针,实现在空间释放后,将使用者(main函数中顺序表的地址值设置为NULL),从而避免野指针的出现。
*/
void DestorySqList(sqlist_t **list)
{
if(list == NULL)
return;
free(*list); /* 释放所开辟顺序表存储空间 */
*list = NULL;
}
/* 统计顺序表中的数据元素个数 */
int LenthSqList(sqlist_t *list)
{
if(list == NULL)
return -1;
return (list->last+1);
}
/* 判断顺序表是否为空表 */
int isEmptySqList(sqlist_t *list)
{
#if 0
if (list->last == -1)
return 1;
else
return 0;
#else
return (list->last == -1);
#endif
}
/* 判断顺序表是否为满表 */
int isFillSqList(sqlist_t *list)
{
return (list->last == NUM-1);
}
2 优化顺序表
在前面1.1所创建的顺序表中,可存储最大数据记录元素个数是固定的,此时存在以下问题:
-
如果现有数据元素的记录条数 大于 设定(NUM)值,导致所开辟的存储空间资源不足,最终数据元素不能完整存储;
-
如果现有数据元素的记录条数 小于或者远小于 设定(NUM)值,导致所开辟存储空间资源的浪费。
在已有问题的前提下,可以进行优化顺序表,其思路:
根据实际数据元素的条数,开辟顺序表数据元素记录存储空间。也就说可以将数据元素存储空间从原有固定数组存储空间优化为动态数组存储空间实现。
2.1 可变顺序表数据类型的定义
-
存储动态数组首元素地址:data_t data;/ 使用malloc函数动态内存管理形式开辟 */
-
存储动态数组元素个数:int nmemb;/* 以形参传递设置 */
/* 可变数据记录顺序表数据类型的定义: */
typedef struct sqlist {
data_t *data; /* 顺序表首元素存储空间地址 */
int nmemb; /* 顺序表可存储数据元素记录条数 */
int last; /* 游标:起始元素的序号默认规定为0,存储最后一个元素的序号 */
} sqlist_t;
2.2 顺序表的创建
/* 创建顺序表:
* 实质是开辟顺序表存储空间
* 1) 全局变量形式开辟,此时存储空间的生命周期为整个程序执行周期,存在空间利用率较低;
* 2) 局部变量形式开辟,此时存储空间的生命周期为函数调用周期,不能在其它函数中访问;
* 3) 动态管理,在需要使用的时候开辟空间,不需要的时候手动释放。
*/
sqlist_t *CreateSqList(int mynmemb)
{
sqlist_t *list;
/* 动态开辟顺序表存储空间 */
list = malloc(sizeof(sqlist_t));
if (list == NULL)
return NULL;
/* 初始化顺序表存储空间元素 */
memset(list, 0, sizeof(sqlist_t)); /* 顺序表基本信息存储空间的清零 */
/* 动态开辟数据元素记录存储空间 */
list->data = calloc(mynmemb, sizeof(data_t));
if (list->data == NULL) {
free(list);
list = NULL;
return list;
}
list->nmemb = mynmemb;
list->last = -1; /* 置空表:末尾元素序号为默认起始元素序号(0)-1 */
return list; /* 返回顺序存储空间地址 */
}