本文知识点来源:
一.线性表相关概念
线性表:线性表是包含若干数据元素的一个线性序列
线性表L可用二元组形式描述:L= (D,R)
1.1 线性表特征:
-
有序性:线性表中的数据元素按照一定的顺序排列,每个元素都有一个前驱(除第一个元素)和/或后继(除最后一个元素)。
-
有限性:线性表由有限个数据元素组成,这意味着可以确切地计算出表中元素的数量。
-
数据类型一致性:线性表中所有元素都属于同一数据类型,或者说是同质的。
-
灵活性:线性表可以为空(即不含任何元素),也可以根据需要动态地增加或删除元素(在动态数组或链表等实现方式中)。
-
访问方式:线性表支持两种基本的访问操作,即按位置(索引)访问和顺序访问。具体实现时,数组支持快速的随机访问,而链表更适合顺序访问。
-
操作集:常见的线性表操作包括但不限于初始化、判断是否为空、查找、插入、删除、遍历等。
-
两种主要实现形式:线性表可以通过数组或链表来实现。数组实现的线性表称为静态数组或顺序表,特点是随机访问快但插入删除效率较低;链表实现的线性表则便于插入删除操作,但随机访问效率相对较低。
二.顺序表相关函数实现
2.1定义顺序表
// 定义一种整型数据类型,命名为data_t,方便后续使用及表示数据元素
typedef int data_t;
// 定义常量N,其值为128,作为静态数组的默认大小
#define N 128
// 定义一个结构体sqlist,包含两个成员:
// data:一个大小为N的数据元素数组,数组元素类型为data_t
// last:记录当前线性表中最后一个元素的实际位置(索引),初始化时应为-1表示空表
typedef struct{
data_t data[N];
int last;
} sqlist;
// 定义sqlink为指向sqlist结构体的指针类型,便于通过指针操作线性表
typedef sqlist *sqlink;
2.2 初始化顺序表
/**
* 初始化一个sqlink列表。
*
* 该函数动态分配内存以创建一个sqlink列表,并对其进行初始化。
* 列表初始化后,最后一个元素的索引被设置为-1,表示列表为空。
*
* @return 初始化后的sqlink列表指针。如果内存分配失败,返回NULL。
*/
sqlink list_init()
{
sqlink L;
// 为sqlink列表分配内存
L = (sqlink)malloc(sizeof(sqlist));
// 检查内存分配是否成功
if (L = NULL)
{
printf("list malloc error\n");
return NULL;
}
// 清零列表内存,确保所有字段都被初始化为0
memset(L, 0, sizeof(sqlist));
// 初始化列表的last字段为-1,表示列表为空
L->last = -1;
return L;
}
该函数用于初始化一个sqlink
类型的链表。首先通过malloc
函数为链表结构体sqlist
分配内存空间,并将指针赋值给L
。然后检查内存分配是否成功,如果失败则输出错误信息并返回NULL
。接着使用memset
函数将链表内存清零,确保所有字段都被初始化为0。最后将链表的last
字段初始化为-1,表示链表为空,然后返回链表指针L
。
2.3 清空顺序表
/**
* 清空链表
*
* 本函数用于清空一个链表,将链表的所有元素置为空,并将链表的尾指针重置为-1。
* 如果链表为空,则函数会打印提示信息并返回-1;否则,函数会成功清空链表并返回0。
*
* @param L 链表的头指针
* @return 如果链表为空,则返回-1;否则返回0。
*/
int list_clear(sqlink L)
{
/* 检查链表是否为空 */
if (L == NULL)
{
/* 如果链表为空,则打印提示信息并返回错误码 */
printf("list is null\n");
return -1;
}
/* 将链表头部结构体的所有字节置为0,以清空链表 */
memset(L, 0, sizeof(sqlist));
/* 将链表的尾指针重置为-1,表示链表为空 */
L->last = -1;
/* 清空操作成功,返回0 */
return 0;
}
该函数用于清空一个链表。首先检查链表是否为空,如果为空则打印提示信息并返回错误码-1。如果链表不为空,则将链表头部结构体的所有字节置为0,以清空链表,并将链表的尾指针重置为-1,表示链表为空。最后返回0,表示清空操作成功。
2.4 判断链表是否为空
/**
* 检查链表是否为空
*
* 本函数通过检查链表头指针是否为NULL来确定链表是否为空。如果链表为空,即头指针为NULL,
* 则函数会打印一条消息并返回-1;如果链表不为空,则返回0。
*
* @param L 链表的头指针
* @return 如果链表为空,则返回-1;否则返回0。
*/
int list_empty(sqlink L)
{
// 检查链表头指针是否为NULL
if (L == NULL)
{
// 打印消息并返回,表示链表为空
printf("list is null\n");
return -1;
}
// 返回0,表示链表不为空
return 0;
}
2.5 链表长度
/**
* 求链表长度
*
* @param L 链表的首节点指针
* @return 如果链表为空,则返回-1;否则返回L->last+1
*/
int list_length(sqlink L)
{
if (L == NULL)
{
printf("list is null\n");
return -1;
}
return (L->last + 1);
}
2.6 查找表中元素
/**
* 在有序链表中定位元素的位置。
*
* 本函数通过遍历链表寻找给定元素,如果找到,则返回其位置;如果链表为空,或未找到元素,则返回-1。
* 请注意,这个函数假设链表是按照数据值的顺序排列的。
*
* @param L 链表的头指针,类型为sqlink。
* @param e 需要查找的元素,类型为data_t。
* @return 如果找到元素,则返回其在链表中的位置(从0开始);如果链表为空或未找到元素,则返回-1。
*/
int list_locate(sqlink L, data_t e)
{
/* 检查链表是否为空 */
if (L == NULL)
{
printf("list is null\n");
return -1;
}
/* 初始化索引变量 */
int i = 0;
/* 遍历链表,查找元素 */
for (i = 0; i <= L->last; i++)
{
/* 如果找到匹配的元素,则返回其位置 */
if (L->data[i] == e)
{
return i;
}
}
/* 如果遍历结束仍未找到元素,则返回-1 */
return -1;
}
2.7 插入元素
/**
* 在单向链表中插入一个元素
* @param L 链表的头指针
* @param pos 插入位置的索引
* @param e 要插入的元素
* @return 如果插入成功,返回0;如果链表已满,返回-1;如果位置错误,返回-1。
*/
int list_insert(sqlink L, int pos, data_t e)
{
/* 检查链表是否已满 */
if (L->last == N - 1)
{
printf("list is full");
return -1;
}
/* 检查插入位置是否有效 */
if (0 <= pos && pos <= L->last)
{
/* 将插入位置之后的元素向后移动 */
for (int i = L->last; i >= pos; i--)
{
L->data[i + 1] = L->data[i];
}
/* 在指定位置插入新元素 */
L->data[pos] = e;
L->last++;
}
else
{
printf("pos is error");
return -1;
}
return 0;
}
2.8 打印链表
/**
* 函数功能:显示链表中的所有元素
* 参数 L:链表的头指针
* 返回值:如果链表为空或未初始化,则返回-1;否则返回0。
*/
int list_show(sqlink L)
{
/* 检查链表是否为空或未初始化 */
if (L == NULL || L->last == -1)
{
/* 如果是,则输出提示信息并返回-1 */
printf("list is null\n");
return -1;
}
/* 遍历链表,打印每个元素 */
for (int i = 0; i <= L->last; i++)
{
printf("%d ", L->data[i]);
}
puts("");
return 0;
}
2.9 删除链表
/**
* 删除单链表中指定位置的元素
*
* @param L 单链表的头指针
* @param pos 要删除的元素的位置
* @return 如果删除成功返回0,如果链表为空或位置不正确返回-1。
*
* 该函数首先检查链表是否为空,然后检查指定的位置是否有效。
* 如果位置有效,它会通过移动元素将指定位置的元素删除。
* 最后,它会更新链表的长度。
*/
int list_delete(sqlink L, int pos)
{
/* 检查链表是否为空 */
if (L->last == -1)
{
printf("list is null\n");
return -1;
}
/* 检查指定的位置是否有效 */
if (pos < 0 || pos > L->last)
{
printf("pos is error\n");
return -1;
}
/* 将指定位置后的元素向前移动以覆盖删除的元素 */
for (int i = pos; i < L->last; i++)
{
L->data[i] = L->data[i + 1];
}
/* 减少链表的长度 */
L->last--;
return 0;
}
2.10 合并两个顺序表
/**
* 合并两个有序链表
* @param L1 第一个有序链表的头结点
* @param L2 第二个有序链表的头结点
* @return 返回0,表示合并操作完成
*
* 该函数的目的是将第二个有序链表L2中的所有元素合并到第一个有序链表L1中。
* 合并过程中,保证L1仍然保持有序。函数通过遍历L2中的每个元素,然后在L1中查找
* 相同的元素。如果L1中不存在相同的元素,则将该元素插入到L1的末尾。
*/
int list_merge(sqlink L1, sqlink L2)
{
int i = 0; // 初始化索引变量i,用于遍历L2链表中的元素
// 遍历L2链表,直到最后一个元素
while (i <= L2->last)
{
// 在L1中查找当前元素是否存在
int ret = list_locate(L1, L2->data[i]);
// 如果L1中不存在当前元素,则将其插入到L1的末尾
if (ret == -1)
{
list_insert(L1, L2->data[i], L1->last + 1);
}
i++; // 移动到下一个元素
}
return 0; // 合并操作完成,返回0
}
该函数用于将链表L2中的所有元素合并到链表L1中,如果L1中已经存在该元素,则不进行任何操作;如果L1中不存在该元素,则将其插入到L1的末尾。函数首先初始化一个索引变量i,用于遍历L2链表中的元素。然后通过循环遍历L2链表,对于每个元素,使用list_locate函数在L1中查找是否存在相同的元素。如果L1中不存在该元素,则使用list_insert函数将其插入到L1的末尾。最后,函数返回0,表示合并操作完成。
2.11 清除顺序表中重复元素
/**
* 清理列表中重复的元素。
*
* 该函数遍历列表,如果发现有重复的元素,则删除重复元素,保持列表中元素的唯一性。
*
* @param L 指向sqlink列表的指针。
* @return 始终返回0,目前未使用返回值来指示函数执行状态。
*/
int list_purge(sqlink L)
{
// 如果列表为空,则直接返回,无需进行清理。
if (L->last == 0)
return 0;
// 双重循环遍历列表,外层循环遍历每个元素,内层循环比较该元素与后面元素的重复情况。
for (int i = 0; i < L->last; i++)
{
for (int j = i + 1; j < L->last; j++)
{
// 如果发现重复元素,则删除重复元素,并调整内层循环的索引。
if (L->data[i] == L->data[j])
{
list_delete(L, j);
j--;
}
}
}
return 0;
}
该函数用于清除列表中的重复元素。它通过双重循环遍历列表,外层循环遍历每个元素,内层循环比较该元素与后面元素的重复情况。如果发现重复元素,则删除重复元素,并调整内层循环的索引。如果列表为空,则直接返回。函数返回0,表示操作完成。
2.12 修改指定元素
/**
* 修改列表中指定位置的元素
*
* 本函数用于修改单向链表中指定位置的元素。如果位置无效(小于0或大于链表长度),函数将不进行任何修改并返回-1。
* 否则,函数将用新的元素e替换链表中索引为i的元素,并返回0表示修改成功。
*
* @param L 链表的头指针,类型为sqlink。
* @param i 需要修改的元素的位置索引,从0开始计数。
* @param e 新的元素值,类型为data_t。
* @return 如果修改成功,返回0;如果位置索引无效,返回-1。
*/
int list_alter(sqlink L, int i, data_t e)
{
/* 检查索引是否有效 */
if (i < 0 || i > L->last)
return -1;
/* 替换链表中指定位置的元素 */
L->data[i] = e;
return 0;
}
三 sqlist.h sqlist.c
sqlist.h:
#ifndef _SQLIST_H_
#define _SQLIST_H_
typedef int data_t;
#define N 128
typedef struct{
data_t data[N];
int last;
}sqlist, *sqlink;
sqlink list_init();
int list_clear(sqlink L);
int list_empty(sqlink L);
int list_length(sqlink L);
int list_locate(sqlink L, data_t e);
int list_insert(sqlink L, int i, data_t e);
int list_show(sqlink L);
int list_free(sqlink L);
int list_delete(sqlink L, int pos);
int list_merge(sqlink L1, sqlink L2);
int list_purge(sqlink L);
int list_locate_(sqlink L, data_t e);
int list_alter(sqlink L, int i, data_t e);
#endif
sqlist.c
#include "sqlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* 初始化一个sqlink列表。
*
* 该函数动态分配内存以创建一个sqlink列表,并对其进行初始化。
* 列表初始化后,最后一个元素的索引被设置为-1,表示列表为空。
*
* @return 初始化后的sqlink列表指针。如果内存分配失败,返回NULL。
*/
sqlink list_init()
{
sqlink L;
// 为sqlink列表分配内存
L = (sqlink)malloc(sizeof(sqlist));
// 检查内存分配是否成功
if (L = NULL)
{
printf("list malloc error\n");
return NULL;
}
// 清零列表内存,确保所有字段都被初始化为0
memset(L, 0, sizeof(sqlist));
// 初始化列表的last字段为-1,表示列表为空
L->last = -1;
return L;
}
/**
* 清空链表
*
* 本函数用于清空一个链表,将链表的所有元素置为空,并将链表的尾指针重置为-1。
* 如果链表为空,则函数会打印提示信息并返回-1;否则,函数会成功清空链表并返回0。
*
* @param L 链表的头指针
* @return 如果链表为空,则返回-1;否则返回0。
*/
int list_clear(sqlink L)
{
/* 检查链表是否为空 */
if (L == NULL)
{
/* 如果链表为空,则打印提示信息并返回错误码 */
printf("list is null\n");
return -1;
}
/* 将链表头部结构体的所有字节置为0,以清空链表 */
memset(L, 0, sizeof(sqlist));
/* 将链表的尾指针重置为-1,表示链表为空 */
L->last = -1;
/* 清空操作成功,返回0 */
return 0;
}
/**
* 检查链表是否为空
*
* 本函数通过检查链表头指针是否为NULL来确定链表是否为空。如果链表为空,即头指针为NULL,
* 则函数会打印一条消息并返回-1;如果链表不为空,则返回0。
*
* @param L 链表的头指针
* @return 如果链表为空,则返回-1;否则返回0。
*/
int list_empty(sqlink L)
{
// 检查链表头指针是否为NULL
if (L == NULL)
{
// 打印消息并返回,表示链表为空
printf("list is null\n");
return -1;
}
// 返回0,表示链表不为空
return 0;
}
/**
* 求链表长度
*
* @param L 链表的首节点指针
* @return 如果链表为空,则返回-1;否则返回L->last+1
*/
int list_length(sqlink L)
{
if (L == NULL)
{
printf("list is null\n");
return -1;
}
return (L->last + 1);
}
/**
* 在有序链表中定位元素的位置。
*
* 本函数通过遍历链表寻找给定元素,如果找到,则返回其位置;如果链表为空,或未找到元素,则返回-1。
* 请注意,这个函数假设链表是按照数据值的顺序排列的。
*
* @param L 链表的头指针,类型为sqlink。
* @param e 需要查找的元素,类型为data_t。
* @return 如果找到元素,则返回其在链表中的位置(从0开始);如果链表为空或未找到元素,则返回-1。
*/
int list_locate(sqlink L, data_t e)
{
/* 检查链表是否为空 */
if (L == NULL)
{
printf("list is null\n");
return -1;
}
/* 遍历链表,查找元素 */
for (int i = 0; i <= L->last; i++)
{
/* 如果找到匹配的元素,则返回其位置 */
if (L->data[i] == e)
{
return i;
}
}
/* 如果遍历结束仍未找到元素,则返回-1 */
return -1;
}
/**
* 在单向链表中插入一个元素
* @param L 链表的头指针
* @param pos 插入位置的索引
* @param e 要插入的元素
* @return 如果插入成功,返回0;如果链表已满,返回-1;如果位置错误,返回-1。
*/
int list_insert(sqlink L, int pos, data_t e)
{
/* 检查链表是否已满 */
if (L->last == N - 1)
{
printf("list is full");
return -1;
}
/* 检查插入位置是否有效 */
if (0 <= pos && pos <= L->last)
{
/* 将插入位置之后的元素向后移动 */
for (int i = L->last; i >= pos; i--)
{
L->data[i + 1] = L->data[i];
}
/* 在指定位置插入新元素 */
L->data[pos] = e;
L->last++;
}
else
{
printf("pos is error");
return -1;
}
return 0;
}
/**
* 函数功能:显示链表中的所有元素
* 参数 L:链表的头指针
* 返回值:如果链表为空或未初始化,则返回-1;否则返回0。
*/
int list_show(sqlink L)
{
/* 检查链表是否为空或未初始化 */
if (L == NULL || L->last == -1)
{
/* 如果是,则输出提示信息并返回-1 */
printf("list is null\n");
return -1;
}
/* 遍历链表,打印每个元素 */
for (int i = 0; i <= L->last; i++)
{
printf("%d ", L->data[i]);
}
puts("");
return 0;
}
/**
* 删除链表
*
* 本函数用于删除一个链表。链表的头指针通过参数 L 传递进来。
* 如果链表不存在(即 L 为 NULL),则函数不进行任何操作并返回 -1。
* 如果链表存在,则释放链表的内存空间,并将 L 设置为 NULL,表示链表已被删除。
*
* @param L 链表的头指针
* @return 如果链表不存在返回 -1,否则返回 0 表示删除成功。
*/
int list_free(sqlink L)
{
if (L == NULL)
{
return -1;
}
free(L);
L = NULL;
return 0;
}
/**
* 删除单链表中指定位置的元素
*
* @param L 单链表的头指针
* @param pos 要删除的元素的位置
* @return 如果删除成功返回0,如果链表为空或位置不正确返回-1。
*
* 该函数首先检查链表是否为空,然后检查指定的位置是否有效。
* 如果位置有效,它会通过移动元素将指定位置的元素删除。
* 最后,它会更新链表的长度。
*/
int list_delete(sqlink L, int pos)
{
/* 检查链表是否为空 */
if (L->last == -1)
{
printf("list is null\n");
return -1;
}
/* 检查指定的位置是否有效 */
if (pos < 0 || pos > L->last)
{
printf("pos is error\n");
return -1;
}
/* 将指定位置后的元素向前移动以覆盖删除的元素 */
for (int i = pos; i < L->last; i++)
{
L->data[i] = L->data[i + 1];
}
/* 减少链表的长度 */
L->last--;
return 0;
}
/**
* 合并两个有序链表
* @param L1 第一个有序链表的头结点
* @param L2 第二个有序链表的头结点
* @return 返回0,表示合并操作完成
*
* 该函数的目的是将第二个有序链表L2中的所有元素合并到第一个有序链表L1中。
* 合并过程中,保证L1仍然保持有序。函数通过遍历L2中的每个元素,然后在L1中查找
* 相同的元素。如果L1中不存在相同的元素,则将该元素插入到L1的末尾。
*/
int list_merge(sqlink L1, sqlink L2)
{
int i = 0; // 初始化索引变量i,用于遍历L2链表中的元素
// 遍历L2链表,直到最后一个元素
while (i <= L2->last)
{
// 在L1中查找当前元素是否存在
int ret = list_locate(L1, L2->data[i]);
// 如果L1中不存在当前元素,则将其插入到L1的末尾
if (ret == -1)
{
list_insert(L1, L2->data[i], L1->last + 1);
}
i++; // 移动到下一个元素
}
return 0; // 合并操作完成,返回0
}
/**
* 清理列表中重复的元素。
*
* 该函数遍历列表,如果发现有重复的元素,则删除重复元素,保持列表中元素的唯一性。
*
* @param L 指向sqlink列表的指针。
* @return 始终返回0,目前未使用返回值来指示函数执行状态。
*/
int list_purge(sqlink L)
{
// 如果列表为空,则直接返回,无需进行清理。
if (L->last == 0)
return 0;
// 双重循环遍历列表,外层循环遍历每个元素,内层循环比较该元素与后面元素的重复情况。
for (int i = 0; i < L->last; i++)
{
for (int j = i + 1; j < L->last; j++)
{
// 如果发现重复元素,则删除重复元素,并调整内层循环的索引。
if (L->data[i] == L->data[j])
{
list_delete(L, j);
j--;
}
}
}
return 0;
}
/**
* 修改列表中指定位置的元素
*
* 本函数用于修改单向链表中指定位置的元素。如果位置无效(小于0或大于链表长度),函数将不进行任何修改并返回-1。
* 否则,函数将用新的元素e替换链表中索引为i的元素,并返回0表示修改成功。
*
* @param L 链表的头指针,类型为sqlink。
* @param i 需要修改的元素的位置索引,从0开始计数。
* @param e 新的元素值,类型为data_t。
* @return 如果修改成功,返回0;如果位置索引无效,返回-1。
*/
int list_alter(sqlink L, int i, data_t e)
{
/* 检查索引是否有效 */
if (i < 0 || i > L->last)
return -1;
/* 替换链表中指定位置的元素 */
L->data[i] = e;
return 0;
}