1.单链表
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
链表的结构是多种多样的,可以分为单向或双向,带头结点或不带头结点,循环或者非循环的。今天我们来实现一个单向不带头结点不循环的单链表。
2.代码实现
SingleList.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SListDataType;
typedef struct SingleListNode
{
SListDataType val;
struct SingleListNode* next;
}SListNode;
SListNode* BuySListNode(SListDataType data);//创建节点
void SListPushBack(SListNode** pplist, SListDataType data);
void SListPopBack(SListNode** pplist);
void SListPushFront(SListNode** pplist, SListDataType data);
void SListPopFront(SListNode** pplist);
void SListPrint(SListNode* plist);
SListNode* SListFind(SListNode* plist, SListDataType data);
void SListAfterInsert(SListNode* pos, SListDataType data);//在pos位置之后插入
void SListBeforeInsert(SListNode** pplist, SListNode* pos, SListDataType data);//在pos位置之前插入
void SListErase(SListNode** pplist, SListNode** ppos);//删除pos位置的元素
void SListAfterErase(SListNode* plist, SListNode* pos);//删除pos位置下一个的元素
SingleList.c
#include "SingleList.h"
SListNode* BuySListNode(SListDataType data)
{
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
if (newNode == NULL)
{
printf("malloc fail\n");
exit(1);
}
newNode->val = data;
newNode->next = NULL;
return newNode;
}
void SListPushBack(SListNode** pplist, SListDataType data)
{
assert(pplist);
if (*pplist == NULL)
{
//第一次插入
*pplist = BuySListNode(data);
return;
}
SListNode* cur = *pplist;
while (cur->next)
{
cur = cur->next;
}
cur->next = BuySListNode(data);
}
void SListPopBack(SListNode** pplist)
{
assert(pplist);
SListNode* prev = NULL;
SListNode* cur = *pplist;
if (cur == NULL)
{
printf("链表中无节点\n");
return;
}
else if (cur->next == NULL)
{
//只有一个节点
free(cur);
cur = NULL;
*pplist = NULL;
}
else
{
while (cur->next)
{
prev = cur;
cur = cur->next;
}
free(cur);
cur = NULL;
prev->next = NULL;
}
}
void SListPushFront(SListNode** pplist, SListDataType data)
{
assert(pplist);
SListNode* cur = *pplist;
if (NULL == cur)
{
//第一次插入,调用尾插即可
SListPushBack(pplist, data);
return;
}
else
{
SListNode* prev = BuySListNode(data);
prev->next = cur;
*pplist = prev;
}
}
void SListPopFront(SListNode** pplist)
{
assert(pplist);
SListNode* cur = *pplist;
if (cur == NULL)
{
printf("链表中无节点\n");
return;
}
else if (cur->next == NULL)
{
//链表中只有一个节点,调用尾删即可
SListPopBack(pplist);
return;
}
else
{
SListNode* next = cur->next;
free(cur);
*pplist = next;
}
}
SListNode* SListFind(SListNode* plist, SListDataType data)
{
SListNode* cur = plist;
while (cur)
{
if (cur->val == data)
{
printf("找到了\n");
return cur;
}
cur = cur->next;
}
printf("没找到\n");
return NULL;
}
void SListAfterInsert(SListNode* pos, SListDataType data)
{
assert(pos);
SListNode* cur = pos;
SListNode* next = cur->next;
SListNode* newNode = BuySListNode(data);
cur->next = newNode;
newNode->next = next;
}
void SListBeforeInsert(SListNode** pplist, SListNode* pos, SListDataType data)
{
//首先,这里不建议在pos前面的位置插入,因为是单链表,pos的前一个位置我们是不清楚的,除非再遍历一遍。
//此外,比如说链表中只有一个元素,在这个元素前面插入,还涉及到改变头结点的指向,这就需要传入二级指针
assert(pplist && pos);
SListNode* cur = *pplist;
SListNode* prev = NULL;
if (cur == pos)
{
//在第一个元素前面插入,此时相当于头插,直接调用头插
SListPushFront(pplist, data);
return;
}
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
SListNode* newNode = BuySListNode(data);
prev->next = newNode;
newNode->next = cur;
}
void SListAfterErase(SListNode* plist, SListNode* pos)
{
assert(plist && pos);
if (pos->next == NULL)
{
//pos就是最后一个元素
printf("pos后面没有元素可删除\n");
return;
}
SListNode* next = pos->next->next;
free(pos->next);
pos->next = next;
}
void SListErase(SListNode** pplist, SListNode** ppos)
{
//首先这里不推荐删除pos位置的元素,比较麻烦,因为单链表我们不清楚pos位置前面的元素。建议删除pos位置后面的那个元素
//注意,这里一定要传两个二级指针
//1.第一个参数:因为如果pos位置是第一个节点的位置,那么涉及到头结点指向的改变
//2.第二个参数:删除pos位置的节点要free(pos),此后pos应该置为空指针。由于形参只是实参的一份临时拷贝
//如果传一级指针,我们只是将这个临时拷贝置为了空指针,而真正的pos仍然指向已经被free了的空间,这显然不合适
//另外,我们再函数中创建了变量cur也指向了pos所指向的空间,那么释放掉这部分空间之后需不需要将cur置为NULL呢?
//并不需要,因为cur是在函数体内创建的一个变量,在函数调用结束后,随函数栈帧销毁而销毁了
assert(pplist && ppos);
SListNode* cur = *pplist;
SListNode* prev = NULL;
if (cur == *ppos)
{
//相当于头删,直接掉头删就行
SListPopFront(pplist);
*ppos = NULL;//这里不要忘了把pos置空,头删可不会帮你把pos置空
return;
}
while (cur != *ppos)
{
prev = cur;
cur = cur->next;
}
//在删除当前节点之前,我们先把当前节点的下一个节点记录下来
SListNode* next = cur->next;
free(cur);
*ppos = NULL;
prev->next = next;
}
void SListPrint(SListNode* plist)
{
while (plist)
{
printf("%d ", plist->val);
plist = plist->next;
}
printf("\n");
}