目录
链表
1、概念
链表是一种物理存储结构上非连续(非顺序)的数据结构,链表中数据元素的逻辑顺序时通过链表中的指针链接次序实现的。
2、分类
- 单向链表或双向链表
- 带头链表或不带头链表
- 循环链表或非循环链表
在我们具体操作中,常用的链表有两类,分别是无头单向非循环链表和带头双向循环链表,这两者分别时结构最简单和结构最复杂的两种链表。
对于无头单向非循环链表,一般很少用于直接存储数据,因为其结构太过简单。实际中经常时用来作为其他数据结构的子结构来使用。
对于带头双向循环链表,一般会用来单独存储数据内容,因为其结构复杂,有较高的操作性和实用性。
无头单向非循环链表的C语言实现
1、功能介绍
- 单链表尾部插入元素
- 单链表头部插入元素
- 删除单链表尾部元素
- 删除单链表头部元素
- 查找单链表中的元素
- 单链表某位置插入元素
- 单链表删除某位后元素
- 打印单链表中的内容
- 删除单链表的内容
2、头文件内容
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//不带头节点非循环的单链表
typedef int SLTDateType;
//单链表的定义
typedef struct SListNode
{
SLTDateType data;//节点中的值域,即节点对应元素内容
struct SListNode* next;//下一个节点的地址
}SListNode;
//单链表的常规操作内容
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表的头删
void SListPopFront(SListNode** pplist);
// 单链表的查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 单链表的打印
void SListPrint(SListNode* plist);
// 单链表的销毁
void SListDestroy(SListNode* plist);
3、头文件中功能实现
3.1、动态申请一个节点
SListNode* BuySListNode(SLTDateType x)//动态申请一个节点,链表的初始化
{
SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));//开辟单链表存储空间
if (NewNode == NULL)
{
printf("内存开辟失败\n");
exit(0);
}
NewNode->data = x;
NewNode->next = NULL;
return NewNode;
}
3.2、单链表的尾插
// 单链表的尾插
//pplist指向的是单链表中的节点,而节点作为指针的存在,所以pplist以二级指针的形式指向节点来对节点的指向进行修改
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);//判断链表是否存在
//此时为空链表,直接将pplist指向最新插入的节点即可
if (*pplist == NULL)
{
*pplist = BuySListNode(x);
}
//链表不为空
else
{
//寻找原链表中的最后一个节点
SListNode* cur = *pplist;
while (cur->next)
{
cur = cur->next;
}
cur->next = BuySListNode(x);//插入新节点
}
}
3.3、单链表的头插
//单链表的头插
void SListPushFront(SListNode **pplist, SLTDateType x)
{
assert(pplist);
SListNode* NewNode = BuySListNode(x);
if (*pplist == NULL)//链表为空,直接插入
{
*pplist = NewNode;
}
else
{
NewNode->next = *pplist;//用新节点指向链表首位
*pplist = NewNode;//头插节点赋值
}
//实际上可以不对链表进行判空处理,因为具体操作内容相同
//未保证代码逻辑的严谨性,在此做分情况讨论,便于理解
}
3.4、单链表的尾删
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
assert(pplist);//保证pplist指向实参plist
if (*pplist == NULL)//空链表
{
return;
}
else if ((*pplist)->next == NULL)//链表仅存在一个节点
{
free(*pplist);
*pplist = NULL;
}
else//法一:链表中至少存在两个节点
{
SListNode* cur = *pplist;
while (cur->next->next)//cur指向倒数第二,最后元素释放,并置空
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
/* 法二:
else
{
SListNode* cur = *pplist;
SListNode* pr = NULL;
while (cur->next) //cur指向倒数第一,并通过pr保存cur前一个节点
{
pr = cur;
cur = cur->next;
}
free(cur);
pr->next = NULL;
}
*/
}
3.5、单链表的头删
// 单链表的头删
void SListPopFront(SListNode** pplist)
{
assert(pplist);
if (*pplist == NULL)//空链表
{
printf("链表为空,无法删除\n");
return;
}
else
{
SListNode* cur = *pplist;//保存头指针
*pplist = cur->next;//通过头指针保存第二节点内容
free(cur);//删除头节点内容
}
}
3.6、单链表的查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* cur = plist;
while (cur)//遍历链表进行一一比对查找
{
if (x == cur->data)
{
return cur;
}
cur = cur->next;
}
3.7、单链表在pos位置之后插入x
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
//新节点难以插入到pos之前,因为是单链表,无法知道pos前一位置内容
//并且没有提供plist,则无法通过遍历得到pos前一位置内容
SListNode* NewNode = BuySListNode(x);
if (pos == NULL)//判空
{
return;
}
else
{
NewNode->next = pos->next;
pos->next = NewNode;
}
}
3.8、单链表删除pos位置之后的值
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
if (pos == NULL)//判空
{
return;
}
SListNode* cur = NULL;
cur = pos->next;
if (cur == NULL)//无法删除最后一位
{
return;
}
//pos->data = cur->data;//让pos位置元素等于后一位元素,可以满足删除pos位置元素
pos->next = cur->next;//让pos指向下下一位元素,空出下一位元素
free(cur);//让pos下一位元素制空,即删除
}
3.9、单链表的打印
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
3.10、 单链表的销毁
void SListDestroy(SListNode** pplist)
{
assert(pplist);
SListNode* cur = *pplist;
while (cur)
{
*pplist = cur->next;
free(cur);
cur = *pplist;
}
*pplist = NULL;
}
4、主函数(测试用程序)
int main()
{
SListNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListPushBack(&plist, 5);
SListPrint(plist);
printf("-------------\n");
SListPopBack(&plist);
SListPrint(plist);
printf("-------------\n");
SListPushFront(&plist, 5);
SListPrint(plist);
printf("-------------\n");
SListPopFront(&plist);
SListPrint(plist);
printf("-------------\n");
SListInsertAfter(SListFind(plist,2), 5);
SListPrint(plist);
printf("-------------\n");
SListEraseAfter(SListFind(plist, 2));
SListPrint(plist);
printf("-------------\n");
SListDestroy(&plist);
SListPrint(plist);
system("pause");
return 0;
}