單鏈表的操作比較簡單靈活,存放在地址任意的存儲單元中
單鏈表是用每一個結點存放一個數據,然後將所有的結點連接起來
當然,每個結點的位置都是隨機的,並不會連續
每個結點也是在堆上申請的
//一個結點中包含數據和指向下一個結點的指針
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);
}
總結:單鏈表的操作就更加靈活了。切記單鏈表中對結點的操作順序有的是絕對不能改變的,否則會丟失結點造成一系列問題。還有,如果你想知道一個結點就得先知道它前一個結點,你想看最後一個結點就得遍歷整個鏈表,這樣相對於繁瑣,所以又提出了帶頭循環雙鏈表的概念(下篇博客中^-^)