单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。 (百度百科)
用一张图来表示一下单链表这种数据结构:
这是一个带头结点的非空单链表
由一个指针指向单链表的头结点,头结点里由两个部分组成:
1.头结点的数据data
2.头结点的下一个结点的地址(也就是图中的0xCC)
单链表就是这样一种结构,将所有的数据放在一张线性表中。单链表也分为好几种形式:带头结点的空链表、不带头结点的空链表、带头结点的非空单链表,不带头结点的非空单链表。数据结构都和上图类似,在这不一一介绍了。
下面,我们具体了解下怎么完成一个单链表的基本操作(单链表的初始化、销毁、头插、尾插等等)
具体步骤及解释都在代码里注释。
单链表的初始化:
typedef int DataType;
typedef struct SListNode{ //这里我们定义一个结构体,因为每一个结点都有两个字段
DataType data; //第一个字段是该结点所存的数据
struct SListNode *pNext; //第二个字段保存了下个结点的地址
} SListNode;
void SListNodeInit(SListNode **ppFirst)
{
assert(ppFirst != NULL);
*ppFirst = NULL; //单链表的初始化很简单,就是让头结点指向空,让链表成为一个空链表就ok
}
单链表的销毁:
void SListNodeDestroy(SListNode **ppFirst)
{
assert(ppFirst != NULL);
SListNode *pNode, pNext;
pNode = *ppFirst;
while (pNode != NULL){ //这里用一个循环去遍历整个链表,然后依次free掉
pNode = pNode->pNext;
free(pNode); /*因为我们每个结点的空间是用malloc函数在堆上创建的,所以在销毁
pNode =pNode -> pNext; 的时候,必须free掉,以免造成内存泄露*/
}
*ppFirst = NULL;
}
结点的创建:
SListNode *CreateNewNode(int data)
{
SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));
assert(pNewNode);
pNewNode->data = data; /* 新结点的数据
pNewNode->pNext = NULL; 新结点的下一个结点的地址为NULL*/
return pNewNode;
}
尾插
void SListNodePushBack(SListNode **ppFirst,DataType data)
{
assert(ppFirst != NULL);
SListNode *pNewNode = CreateNewNode(data); //创建一个新结点
if(*ppFirst == NULL){ /*如果头结点为空,只需要让创建的新结点
*ppFirst = pNewNode; 成为头结点
} */
SListNode *pNode; /*如果头结点不为空,需要定义一个指针让它从头开
*ppFirst = pNode; 始遍历链表,直到找到最后一个结点
while(pNode->pNext != NULL){ */
pNode = pNode->pNext;
}
pNode->pNext = pNewNode; /*把新结点插入到最后一个结点的下一个结点
}
头插:
void SListNodePushFront(SListNode **ppFirst,DataType data)
{
assert(ppFirst != NULL);
SListNode *pNewNode = CreateNewNode(data); //创建一个新结点
pNewNode->pNext = *ppFirst; //新结点的pNext指向头结点
*ppFirst = pNewNode; //此时,让新结点成为头结点
}
插入到指定结点pPOS前,pPOS肯定在链表里
void SListNodeInsert(SListNode **ppFirst,SListNode *pPos,DataType data)
{
assert(ppFirst != NULL);
SListNode *pNode; /*定义一个指针初始化为指向头结点
pNode == *ppFirst;
if(pPos == *ppFirst){ 如果此时头结点就是pPOS,只需调用一
SListNodePushFront(ppFirst,data); 次头插函数即可
return;
}
while(pNode->pNext != pPos){ 如果头结点不是pPOS,需要让先前定义
pNode = pNode->pNext; 的指针从头开始遍历链表,直到找到
} pPos的前一个结点为止
SListNode *pNewNode = CreateNewNode(data); 创建一个新结点
pNode->pNext = pNewNode; 将新结点插入到pPOS之前
pNewNode->pNext = pPos; 新结点的pNext指向pPOS
} */
头删:
void SListNodePopFront(SListNode **ppFrist)
{
assert(ppFirst != NULL); /* 保证不为空链表
assert(*ppFirst != NULL); */
SListNode *pOldFirst = *ppFirst; /* 先将先前的头结点指针保存起来
*ppFirst = (*ppFirst)->pNext; 让头结点指针指向头结点的下一个结点
free(pOldFirst); free掉之前保存的pOldFirst
} */
尾删:
void SListNodePopBack(SListNode **ppFirst)
{
assert(ppFirst != NULL); /* 保证不为空链表
assert(*ppFirst != NULL); */
if((*ppFirst)->pNext == NULL){ /* 如果头结点的下一个结点是NULL
free(*ppFirst); 将头结点释放掉
*ppFirst = NULL; 再让ppFirst指向NULL,以免变成野指针
return;
}
SListNode *pNode = *ppFirst; 如果头结点的下一个结点不为NULL
while(pNode->pNext->pNext != NULL){ 遍历链表,找到最后一个结点
pNode = pNode->pNext;
}
free(pNode->pNext); 释放掉该结点
pNode->pNext = NULL; 将指针指向NULL */
}
根据结点地址删除,结点肯定在链表里:
void SListNodeErase(SListNode **ppFirst,SListNode *pPos)
{
assert(ppFirst != NULL); /* 保证不为空链表
assert(*ppFirst != NULL); */
if((*ppFirst) == pPos){ /* 如果头结点就是pPOS,只需调用一次头删即
SListNodePopFront(ppFirst); 可
return;
}
SListNode *pCur = *ppFirst; 定义一个指针指向头结点,然后遍历链表
while(pCur->pNext != pPos){ 找到pPos,此时pCur的pNext应该是pPOS
pCur = pCur->pNext; 我们要删除pPos处的结点,需要将pCur的
} pNext指向pPos的pNext,然后释放掉pPos
pCur->pNext = pPos->pNext; 处的结点就可以
free(pPos); */
}
查找:
SListNode *Find(SListNode *pFirst,DataType data)
{
SListNode *pNode = pFirst;
for(;pNode;pNode = pNode->pNext){ //遍历链表,找到了就返回找到的结点
if(pNode->data == data){
return pNode;
}
}
return NULL; //没找到,就返回NULL
}
根据数据删除,删除遇到的第一个结点:
void SListNodeRemove(SListNode **ppFirst,DataType data)
{
SListNode *pFound = Find(*ppFirst,data); //定义一个变量去接收Find函数的返回值
if(pFound != NULL){ /*若要删除的数据在链表中存放,就调用
SListNodeErase(ppFirst,pFound); 根据结点地址删除的函数即可
} */
}
根据数据删除,删除遇到的所有结点:
void SListNodeRemoveAll(SListNode **pPFirst,DataType data)
{
SListNode *pNode = *ppFirst;
SListNode *pNext;
while(pNode->pNext){
if(pNode->pNext->data == data){ /*若头结点的下一个结点就为要
pNext = pNode->pNext; 删除的结点,就用pNext将其
pNode->pNext = pNode->pNext->pNext; 保存,pNode的pNext指向
free(pNext); pNext的pNext,最后释放掉
} pNext
else{
pNode = pNode->pNext; 若不是,就让pNode逐个向后
} 遍历,直到找到要删除的结点
}
if((*ppFirst)->data == data){ 特殊情况:如果头结点就是要
SListNodePopFront(ppFirst); 删除的结点,直接头删即可
}
}
到这里,单链表的所有基本操作都完成了
下面是完整代码:
#include<stdio.h>
#include<stlib.h>
#include<assert.h>
typedef int DataType;
typedef struct SListNode{
DataType data;
struct SListNode *pNext;
} SListNode;
void SListInit(SListNode **ppFirst)
{
assert(ppFirst != NULL);
*ppFirst = NULL;
}
void SListDestroy(SListNode **ppFirst)
{
assert(ppFirst != NULL);
SListNode *pNode,*pNext;
pNode = *ppFirst;
while(pNode != NULL){
pNode = pNode->pNext;
free(pNode);
pNode = pNext;
}
*ppFirst = NULL;
}
SListNode *CreateNewNode(DataType data)
{
SListNode *pNewNode = (SListNode *)malloc(sizeof(SListNode));
assert(pNewNode);
pNewNode->data = data;
pNewNode->pNext = NULL;
return pNewNode;
}
void SListNodePushBack(SListNode **ppFirst,DataType data)
{
assert(ppFirst != NULL);
SListNode *pNewNode = CreateNewNode(data);
if(*ppFirst == NULL){
*ppFirst = pNewNode;
}
SListNode *pNode;
*ppFirst = pNode;
while(pNode->pNext != NULL){
pNode = pNode->pNext;
}
pNode->pNext = pNewNode;
}
void SListNodePushFront(SListNode **ppFirst,DataType data)
{
assert(ppFirst != NULL);
SListNode *pNewNode = CreateNewNode(data);
pNewNode->pNext = *ppFirst;
*ppFirst = pNewNode;
}
void SListNodeInsert(SListNode **ppFirst,SListNode *pPos,DataType data)
{
assert(ppFirst != NULL);
SListNode *pNode;
pNode == *ppFirst;
if(pPos == *ppFirst){
SListNodePushFront(ppFirst,data);
return;
}
while(pNode->pNext != pPos){
pNode = pNode->pNext;
}
SListNode *pNewNode = CreateNewNode(data);
pNode->pNext = pNewNode;
pNewNode->pNext = pPos;
}
void SListNodePopFront(SListNode **ppFrist)
{
assert(ppFirst != NULL);
assert(*ppFirst != NULL);
SListNode *pOldFirst = *ppFirst;
*ppFirst = (*ppFirst)->pNext;
free(pOldFirst);
}
void SListNodePopBack(SListNode **ppFirst)
{
assert(ppFirst != NULL);
assert(*ppFirst != NULL);
if((*ppFirst)->pNext == NULL){
free(*ppFirst);
*ppFirst = NULL;
return;
}
SListNode *pNode = *ppFirst;
while(pNode->pNext->pNext != NULL){
pNode = pNode->pNext;
}
free(pNode->pNext);
pNode->pNext = NULL;
}
void SListNodeErase(SListNode **ppFirst,SListNode *pPos)
{
assert(ppFirst != NULL);
assert(*ppFirst != NULL);
if((*ppFirst) == pPos){
SListNodePopFront(ppFirst);
return;
}
SListNode *pCur = *ppFirst;
while(pCur->pNext != pPos){
pCur = pCur->pNext;
}
pCur->pNext = pPos->pNext;
free(pPos);
}
137
SListNode *Find(SListNode *pFirst,DataType data)
{
SListNode *pNode = pFirst;
for(;pNode;pNode = pNode->pNext){
if(pNode->data == data){
return pNode;
}
}
return NULL;
}
void SListNodeRemove(SListNode **ppFirst,DataType data)
{
SListNode *pFound = Find(*ppFirst,data);
if(pFound != NULL){
SListNodeErase(ppFirst,pFound);
}
}
void SListNodeRemoveAll(SListNode **pPFirst,DataType data)
{
SListNode *pNode = *ppFirst;
SListNode *pNext;
while(pNode->pNext){
if(pNode->pNext->data == data){
pNext = pNode->pNext;
pNode->pNext = pNode->pNext->pNext;
}
else{
pNode = pNode->pNext;
}
}
if((*ppFirst)->data == data){
SListNodePopFront(ppFirst);
}
}
void Print(SListNode *pFirst)
{
SListNode *pNode;
for(pNode = pFirst;pNode;pNode = pNode->pNext){
printf("%d -> ",pNode->data);
}
printf("\n");
}
void Test()
{
SListNode *pFirst;
SListInit(&oFirst);
assert(pFirst != NULL);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,2);
SListNodePushBack(&pFirst,1);
SListNodePushFront(&pFirst,10);
SListNodePushFront(&pFirst,11);
Print(pFirst);
SListNode *pFound = Find(pFirst,10);
if(pFound == NULL){
printf("没找到\n");
}
else{
printf("%d\n",pFound->data);
SListNodeInsert(&pFirst,pFound,100);
SListNodeErase(&pFirst,pFound);
}
Print(pFirst);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,4);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,1);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,5);
SListNodePushBack(&pFirst,3);
SListNodePushBack(&pFirst,3);
SListNodeRemoveAll(&pFirst,3);
Print(pFirst);
}
int main( void )
{
Test();
return 0;
}