单链表的操作比较简单灵活,存放在地址任意的存储单元中
单链表是用每一个结点存放一个数据,然后将所有的结点连接起来
当然,每个结点的位置都是随机的,并不会连续
每个结点也是在堆上申请的
//一个结点中包含数据和指向下一个结点的指针
typedef struct sListNode
{
DataType data;
struct sListNode *pNext;
}sListNode;
在单链表中,我们实现了一下的基本操作
//单链表的初始化
void sListInit(sListNode **ppHead);
//单链表的销毁
void sListDestory(sListNode **ppHead);
//单链表的打印
void sListPrint(sListNode *pHead);
//单链表的插入:尾插、头插、插入指定位置
void sListPushBack(sListNode **ppHead, DataType data);
void sListPushFront(sListNode **ppHead, DataType data);
void sListInsert(sListNode **ppHead, sListNode *pPos, DataType data);
//单链表的查询
sListNode *sListFind(sListNode *ppHead, DataType data);
//单链表的删除:头删、尾删、删除指定位置、删除指定数据、删除所有指定数据
void sListPopFront(sListNode **ppHead);
void sListPopBack(sListNode **ppHead);
void sListErase(sListNode **ppHead, sListNode *pPos);
void sListRemove(sListNode **ppHead, DataType data);
void sListRemoveAll(sListNode **ppHead, DataType data);
1)初始化
void sListInit(sListNode **ppHead)
{
assert(ppHead != NULL);
//开始链表中没有数据,那么该链表指向空
*ppHead = NULL;
}
2)销毁
void sListDestory(sListNode **ppHead)
{
assert(ppHead != NULL);
sListNode *pFollow = NULL;
//依次遍历链表,将链表中的每个结点均删除
while (*ppHead != NULL)
{
//删除当前结点前得先记录下一个结点的位置
pFollow = (*ppHead)->pNext;
free(*ppHead);
*ppHead = pFollow;
}
}
3)打印
void sListPrint(sListNode *pHead)
{
//依次遍历每一个结点将其打印
while (pHead != NULL)
{
printf("%d -> ", pHead->data);
pHead = pHead->pNext;
}
printf("NULL\n");
}
4)插入
//写一个函数进行结点的创建
sListNode *createNewNode(int data)
{
sListNode *spList = (sListNode *)malloc(sizeof(sListNode));
if (spList != NULL)
{
spList->data = data;
spList->pNext = NULL;
return spList;
}
return NULL;
}
void sListInsert(sListNode **ppHead, sListNode *pPos, DataType data)
{
/* 限制:pPos一定存在于链表中 */
assert(ppHead != NULL);
sListNode *newNode = createNewNode(data);//创建一个新结点
sListNode *psList = *ppHead;
if (newNode != NULL)
{
//特殊情况:*ppHead就是想找的位置,即pPos就是第一个结点的位置
//也就是头插了
if (pPos == *ppHead)
{
sListPushFront(ppHead, data);
return;
}
while (psList->pNext != pPos)
{
psList = psList->pNext;
}
//当前结点的下一个结点就是指定位置了
newNode->pNext = pPos;
psList->pNext = newNode;
//以上两步操作可将结点插入指定位置的前一个位置(注意顺序不能改变)
}
}
void sListPushFront(sListNode **ppHead, DataType data)
{
assert(ppHead != NULL);
sListNode *newNode = createNewNode(data);
if (newNode != NULL)
{
//特殊情况:*ppHead为NULL
if (*ppHead == NULL)
{
*ppHead = newNode;
return;
}
newNode->pNext = *ppHead;
*ppHead = newNode;
}
}
void sListPushBack(sListNode **ppHead, DataType data)
{
assert(ppHead != NULL);
sListNode *psList = *ppHead;
sListNode *newNode = createNewNode(data);
if (newNode != NULL)
{
//特殊情况:*ppHead为NULL
if (psList == NULL)
{
*ppHead = newNode;
return;
}
//循环找到最后一个结点的位置在其后插入结点
while (psList->pNext)
{
psList = psList->pNext;
}
psList->pNext = newNode;
}
}
5)查询(找到返回该节点的地址,没找到返回NULL)
sListNode *sListFind(sListNode *pHead, DataType data)
{
assert(pHead != NULL);
sListNode *psList;
psList = pHead;
while (psList != NULL)
{
if (psList->data == data)
{
return psList;
}
psList = psList->pNext;
}
return NULL;
}
6)删除
void sListRemoveAll(sListNode **ppHead, DataType data)
{
assert(ppHead != NULL);
assert(*ppHead != NULL);
//先找到第一个指定数据的结点位置
sListNode *psList = sListFind(*ppHead, data);
while (psList != NULL)
{
//删除指定数据,查找下一个指定数据
sListErase(ppHead, psList);
psList = sListFind(*ppHead, data);
}
}
void sListRemove(sListNode **ppHead, DataType data)
{
assert(ppHead != NULL);
assert(*ppHead != NULL);
sListNode *psList = sListFind(*ppHead, data);
sListErase(ppHead, psList);
}
void sListErase(sListNode **ppHead, sListNode *pPos)
{
assert(ppHead != NULL);
assert(*ppHead != NULL);
sListNode *psList = *ppHead;
//特殊情况:首结点就是pPos,或者pPos为NULL
if (psList == pPos)
{
//此时相当于头删
sListPopFront(ppHead);
return;
}
else if (pPos == NULL)
{
//空位置不存在删除直接返回
return;
}
//找到指定位置
while (psList->pNext != pPos)
{
psList = psList->pNext;
}
//将链表与指定位置的下一个结点连接起来
psList->pNext = pPos->pNext;
//释放指定位置的结点
free(pPos);
}
void sListPopBack(sListNode **ppHead)
{
assert(ppHead != NULL);
//当前不许至少有一个结点让你删
assert(*ppHead != NULL);
sListNode *psList = *ppHead;
//特殊情况:只有一个结点
if (psList->pNext == NULL)
{
free(psList);
*ppHead = NULL;
return;
}
//找到最后一个结点
while (psList->pNext->pNext != NULL)
{
psList = psList->pNext;
}
free(psList->pNext);
psList->pNext = NULL;
}
void sListPopFront(sListNode **ppHead)
{
assert(ppHead != NULL);
assert(*ppHead != NULL);
sListNode *psList = *ppHead;
*ppHead = (*ppHead)->pNext;
free(psList);
}
总结:单链表的操作就更加灵活了。切记单链表中对结点的操作顺序有的是绝对不能改变的,否则会丢失结点造成一系列问题。还有,如果你想知道一个结点就得先知道它前一个结点,你想看最后一个结点就得遍历整个链表,这样相对于繁琐,所以又提出了带头循环双链表的概念(下篇博客中^-^)