【数据结构】线性表的链式存储(链表)API及实现
数据类型及API声明
线性表的链式存储是指每个结点都含有一个指针域,指针域指向下一个结点,这样每个节点包含了自身信息和下一个结点的位置,像链条一样连在一起,线性表的链式存储就是我们常说的链表。一般来说,我们都会给链表加一个表头,表头的指针域指向链表的第一个元素(链表的0号位置),在表头中可以存储链表长度信息。
//声明一个链表类型,他可以根据需要转为我们需要的类型
typedef void LinkedList;
//链表结点
typedef struct LinkedListNode LinkedListNode;
//链表表头
typedef struct LinkedListHead LinkedListHead;
//创建线性表并返回线性表句柄
LinkedList* LinkedList_Create();
//销毁线性表(释放内存)
void LinkedList_Destroy(LinkedList* lList);
//清空线性表(不释放内存)
void LinkedList_Clear(LinkedList* lList);
//返回线性表长度(元素个数)
int LinkedList_Length(LinkedList* lList);
//插入元素
int LinkedList_Insert(LinkedList* lList, LinkedListNode* node, int pos);
//返回pos处的元素
LinkedListNode* LinearList_Get(LinkedList* lList, int pos);
//删除一个结点并返回结点值
LinkedListNode* LinearList_Delete(LinkedList* lList, int pos);
链表的API实现
链表结点结构体
链表的结点数据类型是一个结构体,结构体内部只有一个指向本身数据类型的指针,用于存放下一个结点的位置。链表结点只起到连接的作用,它负责把插入的结点一个个链接在链表中,不包含任何业务数据。当我们需要根据自己的业务创建链表时,只需要把我们自己的数据结构体第一个域设置为该链表结点LinkedListNode类型的指针即可,也就是我们的数据结构包含链表结点,并且起始地址相同(链表结点放在数据结构第一个域),这样在做类型转换等操作时,就免去了通过偏移量寻址的操作。
struct LinkedListNode
{
LinkedListNode* next;
};
表头结构体
表头包含一个链表结构体,用于指向链表的0号结点,包含一个数据,用于指示链表长度。
struct LinkedListHead
{
LinkedListNode head;
int length;
};
创建链表并返回链表句柄
- 函数功能:创建一个链表并返回链表句柄,其实就是创建一个表头
- 函数参数:无,链式结构不需要提前分配内存,而线性表的顺序结构需要提前分配内存
- 函数返回:表头
LinkedList* LinkedList_Create()
{
LinkedList* head = NULL;
head = (LinkedList*)malloc(sizeof(LinkedListHead));
if (head == NULL)
{
printf("err: head = (LinkedList*)malloc(sizeof(LinkedListHead))\n");
return NULL;
}
memset(head, 0, sizeof(LinkedListHead));
return head;
}
销毁链表
- 函数功能:销毁线性表就是释放表头资源,销毁链表前需要逐个删除链表结点
- 函数参数:链表句柄
- 函数返回:无
void LinkedList_Destroy(LinkedList* lList)
{
LinkedListHead* pList;
if (lList == NULL)
{
printf("err: lList == NULL \n");
return;
}
pList = (LinkedListHead*)lList;
free(pList);
}
清空链表
- 函数功能:返回到链表刚创建的状态,即表头指针域指向NULL,长度置为0
- 函数参数:链表句柄
- 函数返回:无
void LinkedList_Clear(LinkedList* lList)
{
LinkedListHead* pList;
if (lList == NULL)
{
printf("err: lList == NULL \n");
return;
}
pList = (LinkedListHead*)lList;
pList->length = 0;
pList->head.next = NULL;
}
返回链表长度
- 函数功能:获取链表长度,即结点个数
- 函数参数:链表句柄
- 函数返回:节点个数
int LinkedList_Length(LinkedList* lList)
{
LinkedListHead* pList;
if (lList == NULL)
{
printf("err: lList == NULL \n");
return -1;
}
pList = (LinkedListHead*)lList;
return pList->length;
}
插入元素
- 函数功能:在链表中插入一个元素,被插入的元素已经在主调函数分配好内存,只需把链表结点LinkedListNode串接起来即可
- 函数参数:lList 链表句柄 node 待插入元素 pos 插入位置
- 函数返回:成功返回0
int LinkedList_Insert(LinkedList* lList, LinkedListNode* node, int pos)
{
int i = 0;
LinkedListHead* pList = NULL;
LinkedListNode* pCurrent = NULL;
if ((lList == NULL) || (node == NULL) || (pos < 0))
{
printf("err: (lList == NULL) || (node == NULL) || (pos < 0)\n");
return -1;
}
pList = (LinkedListHead*)lList;
pCurrent = &(pList->head);
for (i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
node->next = pCurrent->next;
pCurrent->next = node;
pList->length++;
return 0;
}
插入元素示意图
插入元素时需要借助一个辅助指针变量pCurrent,pCurrent为当前指针位置,初始状态他应该指向表头,当我们需要在pos号位置插入元素时,就将pCurrent后移pos位,比如要在2号位置插入,将pCurrent后移2次,那么pCurrent将指在1号结点处,而1号结点的next域指向2号结点,这时只需要把待插入结点插在pCurrent后面即可完成插入。插入时,应先将2号结点的位置赋给待插入结点的指针域,这样做是为了防止后续结点(2号结点)丢失,然后再把待插入结点赋给pCurrent的指针域即可完成整个插入过程。
返回指定位置元素
- 函数功能:获取一个指定位置的元素
- 函数参数:lList 链表句柄 pos 要获取的元素标号
- 函数返回:获取的结点元素
LinkedListNode* LinearList_Get(LinkedList* lList, int pos)
{
int i = 0;
LinkedListHead* pList = NULL;
LinkedListNode* pCurrent = NULL;
if ((lList == NULL) || (pos < 0))
{
printf("err: lList == NULL \n");
return NULL;
}
pList = (LinkedListHead*)lList;
pCurrent = &(pList->head);
for (i = 0; ((i < pos) && (pCurrent->next != NULL)); i++)
{
pCurrent = pCurrent->next;
}
return pCurrent->next;
}
删除结点并返回被删除结点
- 函数功能:删除一个结点并返回该结点
- 函数参数:lList 链表句柄 pos 要删除的链表结点元素
- 函数返回:被删除的结点
LinkedListNode* LinearList_Delete(LinkedList* lList, int pos)
{
int i = 0;
LinkedListHead* pList = NULL;
LinkedListNode* pCurrent = NULL;
LinkedListNode* pTemp = NULL;
if ((lList == NULL) || (pos < 0))
{
printf("err: lList == NULL \n");
return NULL;
}
pList = (LinkedListHead*)lList;
pCurrent = &(pList->head);
for (i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
pTemp = pCurrent->next;
pCurrent->next = pTemp->next;
pList->length--;
return pTemp;
}
删除元素示意图
删除结点需要借助两个辅助指针,一个pCurrent指示当前结点,其作用类似于插入节点中的pCurrent,另一个pTemp用于保存被删除结点,删除时,只需要pCurrent的指针域指向pTemp指针域的内容即可完成删除。
测试函数
#include "LinkedList.h"
typedef struct
{
LinkedListNode* node;
int len;
char* str;
}MyStr;
int main()
{
int i = 0;
LinkedList* mList = NULL;
MyStr s1, s2, s3, s4, s5;
MyStr* temp = NULL;
s1.str = "hello";
s2.str = "Linked";
s3.str = "List";
s4.str = "C";
s5.str = "!";
mList = LinkedList_Create();
LinkedList_Insert(mList, (LinkedListNode*)&s5, 0);
LinkedList_Insert(mList, (LinkedListNode*)&s4, 0);
LinkedList_Insert(mList, (LinkedListNode*)&s3, 0);
LinkedList_Insert(mList, (LinkedListNode*)&s2, 0);
LinkedList_Insert(mList, (LinkedListNode*)&s1, 0);
for (i = 0; i < 5; i++)
{
temp = (MyStr*)LinearList_Get(mList, i);
printf("%s ", temp->str);
}
printf("\n");
for (i = 0; i < 5; i++)
{
temp = (MyStr*)LinearList_Delete(mList, 0);
printf("str: %s\n", temp->str);
}
printf("len: %d\n", LinkedList_Length(mList));
LinkedList_Destroy(mList);
system("pause");
return 0;
}
具体代码资源已上传,可在下面的链接免费下载
线性表的链式存储(链表)API及实现https://download.csdn.net/download/qq_43471489/85043011