目录
前言
链表的概念
单向链表节点的语法结构
单向链表的实现
创建链表节点
尾部插入的实现
尾部删除的实现。
头部插入的实现
第一种写法
第二种写法
头部删除的实现
第一种写法
第二种写法
随机删除
随机插入 (插入在目标节点前)
打印整个链表
修改节点
总结
这篇文章是无头单项非循环链表,后面会跟一篇有头双向循环链表的,还希望能得到同志们的支持。
这是我的数据结构和算法专栏,希望各位同志、大佬能够点点赞
https://blog.csdn.net/zhengzjm/category_12170458.html
概念:链表是一种 物理存储结构上非连续 、非顺序的存储结构,数据元素的 逻辑顺序 是通过链表 中的指针链接 次序实现的 。
由上图可知,链表的元素(节点)由两个部分组成,一部分是数据域,用来存放数据,另一部分是指针域,用来连接链表。
将链表节点想象成一个个纸片,由指针将他们串起来。
typedef int SLTDataType;//链表数据域的数据类型,方便直接修改
typedef struct SListNode
{
SLTDataType data;//数据域
SListNode* next;//指针域,用来连接节点(链表元素)
}SListNode;
我们理想中的链表示意图:
创建节点就是开辟一个链表节点类型的空间,形参为需要添加的数据,返回值为新开辟的这块空间的地址。
SListNode* BuyNode(SLTDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
newnode->data = x;
newnode->next = NULL;
return newnode;
}
先判断节点是否存在,不存在则让指向首节点的指针指向新开辟的空间,存在则利用循环找到末节点,将新开辟的节点同末节点连接起来。
void PushBack(SListNode**pplist,SLTDataType x)
{
SListNode* cur = *pplist;
//如果指向第一个链表的指针为空指针,说明该链表尚无节点。
if (*pplist == NULL)
{
*pplist = BuyNode(x);
}
else
{
while (cur->next!=NULL)//找到末端链表
{
cur=cur->next;
}
cur->next = BuyNode(x);
}
}
有几种情况:没有节点、只有一个节点、多个节点。使用if进行判断。
对于多个节点,我们使用两个指针,一个是prev,一个是tail。tail用来找末端节点(侦察兵),prev用来指向tail指向节点的上一个节点,使用while循环来寻找末端节点,找到时先释放掉末端节点上一节点的next所指向的节点(tail所指向的节点\prev所指向的节点中的next所指向的节点),再将prev所指向的节点中的next指针改为空指针。
void PopBack(SListNode** pplist)
{
if (*pplist == NULL)
{
perror("PopBack");
return;
}
else if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* prev = NULL;
SListNode* tail = *pplist;
while (tail->next!=NULL)
{
prev = tail;
tail = tail->next;
}
free(prev->next);
prev->next = NULL;
}
}
用指针first将原首节点的地址存放起来,令pplist指向新首节点,新首节点中的next指向原首节点。
void PushFront(SListNode** pplist, SLTDataType x)
{
SListNode* first = *pplist;
*pplist = BuyNode(x);
(*pplist)->next= first;
}
先创建新节点,令新节点的next指向原首节点,再令pplist指向新首节点。
void PushFront(SListNode** pplist, SLTDataType x)
{
SListNode* newnode = BuyNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
先将原首节点地址存起来,然后令pplist指向新的首节点,释放掉原首节点。
void PopFront(SListNode** pplist)
{
SListNode* first = *pplist;
*pplist = (*pplist)->next;
free(first);
}
用next保存新首节点的地址,释放掉原首节点,令pplist指向新首节点。
void PopFront(SListNode** pplist)
{
SListNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
同样需要侦察兵cur,用来寻找我们要删除的目标节点,prev用来指向目标节点的上一节点,如果要删除的节点是首节点,我们之间复用上面的头部删除就行了。另外的情况通过while寻找,当找到时跳出循环,将目标节点的上一节点同目标节点的下一节点连接,释放掉目标节点。
void SListErase(SListNode** pplist, SLTDataType pos)
{
SListNode* prev = NULL;
SListNode* cur = *pplist;
if (pos == (*pplist)->data)
{
PopFront(pplist);
}
else
{
while (cur->data != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = cur->next;
free(cur);
}
}
void Insert(SListNode** pplist, SLTDataType pos,SLTDataType x)
{
SListNode* tmp = 0;
SListNode* prev = NULL;
SListNode* cur = *pplist;
if (pos == (*pplist)->data)
{
PushFront(pplist, pos);
}
else
{
while (cur->data != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = BuyNode(x);
prev->next->next = cur;
}
}
void Print(SListNode* phead)
{
SListNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
就是查找,找到了就改。
void Modify(SListNode** pplist, SLTDataType pos, SLTDataType x)
{
SListNode* cur = *pplist;
while (cur->data!=pos)
{
cur = cur->next;
}
cur->data = x;
}
介绍链表的概念、单链表节点的语法结构、链表节点的头插、尾插、头删、尾删、链表节点的随机插入与随机删除以及随机修改。