1919 字
10 分钟
数据结构笔记【1】

数据的三种结构#

逻辑结构#

包含:

  • 集合
  • 线性结构(一对一),如线性表、栈、队列
  • 树形结构 一个对多个
  • 图状结构 多个对多个

存储结构#

  • 顺序存储:按照逻辑顺序存放于连续空间
  • 链式存储(重点):放到存储区的不同位置,用地址(指针)方式建立逻辑上的联系
  • 索引存储:建立附加的索引表(电话簿)
  • 散列存储:依据数据元素的特殊字段(关键字key)计算元素的存放地址,然后按地址存放

线性表#

线性表是包含若干数据元素的一个线性序列,记为

L=(a0,...,ai1,ai,ai+1,...,an1)L=(a_0,...,a_{i-1},a_i,a_{i+1},...,a_{n-1})

LL为表名,aia_i为数据元素,nn为表长,n>0n>0时表为非空表,否则为空表

线性表可以用二元组形式描述

L=(D,R)L=(D,R)

即线性表LL包含数据元素集合DD和关系集合RR

  • 关系符<aia_i,ai+1a_{i+1}>为有序对
  • 表示任意两个相邻元素之间的先后次序

E.g.E.g. 有顺序表L={1,2,3,4,5,6},若使用L=(D,R)L=(D,R)表示,则D=1,2,3,4,5,6D={1,2,3,4,5,6},R=<1,2>,<2,3>,...R=<1,2>,<2,3>,...

线性表的特征:

  • 表头无前驱
  • 表尾无后继
  • 其余元素仅有一个直接前驱和直接后继

顺序存储结构的特点:

  • 逻辑上相邻,则存储位置也相邻
  • 对元素的存取为随机存取或按地址存储
  • 存储密度高
  • 对表的插入和删除等运算的时间复杂度高

C语言中,可借助一维数组类型来表述线性表的顺序存储结构

/*
* @brief:创建新线性表
* @param:无
* @return:线性表指针(sqlink类型),申请失败返回NULL
*/
sqlink list_create()
{
sqlink L; //声明sqlink类型的表L
L = (sqlink)malloc(sizeof(sqlist)); // 为空表申请内存,返回分配的内存地址
//判断内存是否申请成功
if (L == NULL)
{
printf("memory allocation failed\n");
return NULL;
}
memset(L, 0, sizeof(sqlist)); //L指向的sqlist结构体的内存清零
L->last = -1; //表示线性表为空
return L;
}
/*
* @brief:清除线性表内的元素,全部置为零
* @param: L:指向表的指针
* @return:成功返回0,失败返回-1
*/
int list_clear(sqlink L)
{
//判断是否是空表
if (L == NULL)
{
return -1;
}
memset(L, 0, sizeof(sqlist)); //L指向的sqlist结构体的内存清零
L->last = -1; //last置为-1,表示线性表为空
return 0;
}
/*
* @brief:判断线性表是否为空表
* @param: L:指向线性表的指针
* @return:空表返回1,非空表返回0
*/
int list_empty(sqlink L)
{
//判断是否为空表
if (L->last == -1)
{
return 1;
}
else
{
return 0;
}
}
/*
* @brief:求线性表的长度(有效元素个数)
* @param: L:指向线性表的指针
* @return:表不存在返回-1,否则返回长度
*/
int list_length(sqlink L)
{
if (L == NULL)
{
return -1;
}
return (L->last + 1); //长度为表尾下标加1
}
/*
* @brief:向线性表内插入数值,插入后原位置向后的所有元素后移一位
* @param: L:指向线性表的指针
* @param: value:待插入值
* @param: pos:插入位置
* @return:插入成功返回0,失败返回-1
*/
int list_insert(sqlink L, data_t value, int pos)
{
if (L == NULL) return -1; //表不存在返回-1
//判断线性表是否满
if (L->last == N - 1)
{
printf("table is full\n");
return -1;
}
//检查插入位置是否正确,应属于[0,last],如果是空表则不检查
if (pos < 0 || pos >= L->last + 1)
{
if (L->last != -1)
{
printf("pos is illegal\n");
return -1;
}
}
//向后移动原有元素,从最后一个元素开始
for (int i = L->last; i >= pos; i--)
{
L->data[i+1] = L->data[i];
}
//更新插入值和last
L->data[pos] = value;
L->last++;
return 0;
}
/*
* @brief:遍历并打印线性表内元素
* @param: L:指向线性表的指针
* @return:成功返回0,失败返回-1
*/
int list_show(sqlink L)
{
if (L == NULL) return -1; //判断是否为有效表
if (L->last == -1) printf("table is empty\n"); //判断是否为空表
//遍历并打印
for (int i = 0; i <= L->last; i++) //注意是小于等于,不然不显示表尾
{
printf("%d ",L->data[i]);
}
return 0;
}
/*
* @brief:删除全表,包括创建表时所分配的内存
* @param: L:指向线性表的指针
* @return:表不存在返回-1,成功返回0
*/
int list_delete(sqlink L)
{
if (L == NULL) return -1;
free(L); //释放为表L申请的内存
L = NULL; //标记表L为无效表
return 0;
}
/*
* @brief:删除表中某一位置的元素
* @param: L:指向线性表的指针
* @param: pos:待删除元素位置
* @return:删除成功返回0,失败返回-1
*/
int list_delete_single(sqlink L, int pos)
{
if (L == NULL) return -1; //判断是否为无效表
//判断pos是否位于有效范围内 应为[0,last]
if (pos < 0 || pos > L->last)
{
printf("pos is illegal\n");
return -1;
}
//[pos+1,last]区间内已有元素前移
for (int i = pos + 1; i < L->last + 1; i++)
{
L->data[i-1] = L->data[i]; //数据前移,自动覆盖
}
//更新last
L->last--;
return 0;
}
/*
* @brief:合并两个线性表,L2全表元素置于L1元素之后
* @param: L1:指向待合并线性表L1的指针
* @param: L1:指向待合并线性表L2的指针
* @return:合并成功返回0,失败返回-1
*/
int list_merge(sqlink L1, sqlink L2)
{
int count = 0;
if (L1 == NULL || L2 == NULL) return -1;
//判断合并后L1是否越界
if (L1->last + 1 + L2->last + 1 > N) printf("table is too big\n");
//L2的第i位加到L1的last+1+i位置,总共传递L2->last个数
for (int i = 0; i <= L2->last; i++)
{
L1->data[L1->last+1+i] = L2->data[i];
count++; //计数
}
//更新L1->last为原last值+传递完成的元素个数
L1->last = L1->last + count;
return 0;
}
/*
* @brief:查询线性表中是否存在某一元素
* @param: L:指向待查询线性表L的指针
* @param: value:待查询值
* @return:值对应的元素存在返回1,不成功返回-1
*/
int list_locate(sqlink L, data_t value)
{
int i = 0;
//搜索L中是否有元素等于value
for (i = 0 ; i <=L->last; i++)
{
if (L->data[i] == value) return 1;
}
return -1;
}
/*
* @brief:查找L1和L2中是否有相同的元素,将不同的元素存于新表L3
* @param: L1:指向待合并线性表L1的指针
* @param: L2:指向待合并线性表L2的指针
* @return:合并成功返回0,失败返回-1
*/
sqlink list_mergetonewtable(sqlink L1, sqlink L2)
{
sqlink L3 = list_create(); //创建新表L3
int i = 0;
int ret;
if (L1 == NULL || L2 == NULL)
{
printf("no such table");
return NULL;
}
//遍历L2内元素
while (i <= L2->last)
{
ret = list_locate(L1, L2->data[i]); //查找L1中是否有L2中的第i个元素,即判断是否重复
//某元素不重复则向L3插入
if (ret == -1)
{
list_insert(L3, L2->data[i], L3->last+1); //按顺序向L3插入非重复值
}
i++; //更新索引
}
return L3;
}
/*
* @brief:清除线性表内的重复元素,清除后所有元素前移补空
* @param: L:指向待合并线性表L1的指针
* @return:操作成功返回0,失败返回-1
*/
int list_purge(sqlink L)
{
if (L == NULL) return -1;
for (int i = 0; i <= L->last; i++) //遍历线性表
{
for (int j = i + 1; j <= L->last; j++) //从索引i+1开始,如果i+1及之后的元素和data[i]相等,判断为重复
{
if (L->data[i] == L->data[j])
{
printf("The repeated elements are:%d\n",L->data[i]);
list_delete_single(L, j); //删除重复的第j个元素
j--; //回退索引,防止跳过未检查的元素
}
}
}
return 0;
}

线性表的顺序存储缺点#

  • 要求系统提供一大片连续存储空间
  • 插入、删除等运算需要遍历整个内存,运算耗时,且元素可能在存储器中成片移动
数据结构笔记【1】
https://arkmostima.pages.dev/posts/数据结构笔记1/
作者
Aki
发布于
2024-10-10
许可协议
CC BY-NC-SA 4.0