版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
链表概念
链表是一种物理存储结构上非连续、非顺序的存储结构,属于线性表的一种,其数据元素的逻辑顺序是通过链表中的指针接次序实现的
链表的分类
链表从结构上,可分为:
- 单链表、双向链表
- 带头单链表、不带头单链表
- 循环单链表、非循环单链表
综上:
我们可得 8 种链表的构造方法,接下来,我们将对单向无头非循环链表的构造方法及基本操作进行描述
首先要定义两个结构体,一个代表以 _head 为第一个结点的结构体,另一个结构体定义每个结点包含的元素:_next 即指向下一个元素的指针,_data 即该节点数据
typedef int DataType;
typedef struct SingleListNode
{
DataType _data;
struct SingleListNode* _next;
}SingleListNode;
typedef struct SingleList
{
SingleListNode* _head;
}SingleList;
链表的增删查改操作
一 . 增删查改前期步骤
- 建初始化函数
给链表的第一个结点赋空即可
void SingleListInit(SingleList* sl)
{
//next, data
assert(sl);
sl->_head = NULL;
}
- 建创建结点的函数
链表特点为插入一个数据,则开辟一个结点,开辟步骤为:
传入结点数值 x ,先为新结点 newNode 开辟对应空间,使新结点数值为 x ,新结点的 next 指向空
SingleList* CreatNode(DataType x)
{
SingleListNode* newNode = (SingleListNode*)malloc(sizeof(SingleListNode));
newNode->_data = x;
newNode->_next = NULL;
}
- 建销毁函数
使 cur 为链表的第一个结点,next 为 cur 的下一个结点,释放 cur ,再循环依次释放链表元素,再使 _head 指向空
void SingleListDestroy(SingleList* sl)
{
assert(sl);
SingleListNode* cur = sl->_head;
while (cur)
{
SingleListNode* next = cur->_next;
free(cur);
cur = next;
}
sl->_head = NULL;
}
- 输出顺序表
void SingleListPrint(SingleList* sl)
{
assert(sl);
SingleListNode* cur = sl->_head;
while (cur)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
printf("\n");
}
二 . 增加数据操作
- 在尾部插入数据
如果链表为空,则只需将插入元素赋给 _head 即可,其他情况时,应遍历找到最后一个结点,再最后一个结点后创建新结点
void SingleListPushBack(SingleList* sl, DataType x)
{
assert(sl);
//空链表
if (sl->_head == NULL)
{
sl->_head = CreatNode(x);
}
//遍历链表
else
{
SingleListNode* cur = sl->_head;
while (cur->_next)
{
cur = cur->_next;
}
//找到最后一个结点
cur->_next = CreatNode(x);
}
}
- 在头部插入数据
使 cur 为第一个元素直接在 cur 前创建新结点即可,使新结点的 _next 指向 cur
void SingleListPushFront(SingleList* sl,DataType x)
{
assert(sl);
SingleListNode* cur = sl->_head;
SingleListNode* newNode = CreatNode(x);
newNode->_next = cur;
}
- 在任意数据前实现插入操作
传入插入元素 src 及数据 x 使得在 x 前插入元素 src ,首先新建 src 的结点,如果第一个结点值为 x ,则进行头插操作,其他情况先找到数值为 src的结点 cur ,再使 newNode 及 cur 的 next 更改指向
void SingleListInsertFront(SingleList* sl, DataType x, DataType src)
{
assert(sl);
SingleListNode * cur;
SingleListNode * newNode = CreatNode(src);
if (sl->_head->_data == x)
{
SingleListPushFront(sl, src);
return;
}
for (cur = sl->_head; cur->_next; cur = cur->_next)
{
if (cur->_next->_data == x)
{
break;
}
}
newNode->_next = cur->_next;
cur->_next = newNode;
}
- 在任意位置后实现插入操作
传入插入位置 pos,创建新结点,使 pos 的 next 指向新结点,新结点的 next 指向原先的 next
另:若想在 pos 前插入数据,可以先实现上述步骤,再将 pos 与新结点位置交换
void SingleListInsertAfter(SingleListNode* pos, DataType x)
{
assert(pos);
SingleListNode* newNode = CreatNode(x);
SingleListNode* next = pos->_next;
pos->_next = newNode;
newNode->_next = next;
}
三 . 删除数据操作
- 在尾部删除数据
首先遍历出尾部的结点,释放尾部节点,更改 prev 的指向,若删除为第一个结点,则使 _head 为空即可
void SingleListPopBack(SingleList* sl)
{
assert(sl);
SingleListNode* cur = sl->_head;
SingleListNode* prev = NULL;
while (cur->_next)
{
prev = cur;
cur = cur->_next;
}
free(cur);
cur = NULL;
if (prev == NULL)
{
//删除的是头节点
sl->_head = NULL;
}
else
{
prev->_next = NULL;
}
}
- 在头部删除数据
清空第一个元素,更改 _head,如果链表为空,则直接返回
void SingleListPopFront(SingleList* sl)
{
assert(sl);
SingleList *next, *cur;
if (sl->_head == NULL)
return;
cur = sl->_head;
next = sl->_head->_next;
free(cur);
cur = NULL;
sl->_head = next;
}
- 在任意位置实现删除操作
传入插入位置 pos,删除在后面的元素,先判断 pos 是否存在 _next,重新链接 pos 的 _next及新元素的 _next
void SingleListEraseAfter(SingleListNode* pos)
{
assert(pos);
SingleListNode* next = pos->_next;
if (pos->_next == NULL)
return;
else
{
SingleListNode* next2 = next->_next;
pos->_next = next2;
free(next);
next == NULL;
}
}
四 . 查找数据操作
- 一般查找元素位置操作
挨个查找元素,若找到,则返回结点,否则返回空
SingleListNode* SingleListFind(SingleList* sl, DataType x)
{
assert(sl);
SingleListNode * cur;
for (cur = sl->_head; cur; cur = cur->_next)
{
if (cur->_data == x)
{
return cur;
}
}
return NULL;
}
上述即为几种关于该种链表的基本增删查改操作的代码思想,关于单链表还涉及很多知识点和题目,十分重要,之后的博客会分享一些常见的关于单链表的进阶问题