帶頭循環雙鏈表,顧名思義“帶頭”就是說你能知道頭結點的位置,“循環”就是說這個鏈表是循環的,“雙鏈表”就是說這個鏈表除了有一個指針指向下一個結點還有一個指針指向前一個結點
typedef int DataType;
typedef struct dList{
DataType data;
struct dList *pPrev;
struct dList *pNext;
}dList;
在雙鏈表中我們實現了以下的基本操作
//創建一個新結點
dList *dListCreateNewNode(DataType data);
//雙鏈表的初始化
void dListInit(dList **ppHead);
//雙鏈表的打印
void dListPrint(dList *pHead);
//雙鏈表的插入:尾插、頭插、插入任一位置
void dListPushBack(dList *pHead, DataType data);
void dListPushFront(dList *pHead, DataType data);
void dListInsert(dList *pHead, dList *pos, DataType data);
//雙鏈表的刪除:尾刪、頭刪、刪除任一位置
void dListPopBack(dList *pHead);
void dListPopFront(dList *pHead);
void dListErase(dList *pHead, dList *pos);
//雙鏈表的清理
void dListClear(dList *pHead);
//雙鏈表的銷燬
void dLisrDestroy(dList **ppHead);
1)申請結點
dList *dListCreateNewNode(DataType data)
{
dList *node = (dList *)malloc(sizeof(dList));
assert(node);
node->data = data;
node->pNext = NULL;
node->pPrev = NULL;
return node;
}
2)初始化(此時申請的這一個節點很重要,它的pNext將指向第一個數據結點,pPrev將指向最後一個數據結點)
void dListInit(dList **ppHead)
{
assert(ppHead != NULL);
//創建一個結點,用其指向首結點(結點裏面的數據無意義)
(*ppHead) = dListCreateNewNode(0);
//讓結點的兩個指針暫且指針自己
(*ppHead)->pNext = *ppHead;
(*ppHead)->pPrev = *ppHead;
}
3)打印
void dListPrint(dList *pHead)
{
assert(pHead != NULL);
dList *pdList = pHead->pNext;
//依次遍歷鏈表將每個結點打印
while (pdList != pHead){
printf("%d -> ", pdList->data);
pdList = pdList->pNext;
}
printf("NULL\n");
}
4)插入
void dListPushBack(dList *pHead, DataType data)
{
assert(pHead != NULL);
#if 0
//創建一個結點
dList *pdList = dListCreateNewNode(data);
//讓申請的結點的pNext指向頭結點,pPrev指向原來的最後一個節點
pdList->pNext = pHead;
pdList->pPrev = pHead->pPrev;
//讓頭結點的pPrev指向現在最後一個結點,讓原來的最後一個結點的pNext指向現在的最後一個節點
pHead->pPrev = pdList;
pdList->pPrev->pNext = pdList;
#endif
dListInsert(pHead, pHead, data);
}
void dListPushFront(dList *pHead, DataType data)
{
assert(pHead != NULL);
#if 0
dList *pdList = dListCreateNewNode(data);
//讓新的結點的pNext指向原來的第一個結點,pPrev指向頭結點
pdList->pNext = pHead->pNext;
pdList->pPrev = pHead;
//頭結點的pNext指向現在的第一個結點,讓原來的第一個結點的pPrev指向現在的第一個結點
pHead->pNext = pdList;
pdList->pNext->pPrev = pdList;
#endif
dListInsert(pHead, pHead->pNext, data);
}
void dListInsert(dList *pHead, dList *pos, DataType data)
{
assert(pHead != NULL);
dList *pdList = dListCreateNewNode(data);
//讓新結點的pNext指向指定結點,pPrev指向指定結點的前一個結點
pdList->pNext = pos;
pdList->pPrev = pos->pPrev;
//指定結點的pPrev指向新結點,指定結點的pNext指向新結點
pos->pPrev = pdList;
pdList->pPrev->pNext = pdList;
}
5)刪除
void dListPopBack(dList *pHead)
{
assert(pHead != NULL);
//刪除結點得保證現在至少有一個數據結點
assert(pHead->pNext != pHead);
#if 0
//記錄要刪除的結點
dList *pos = pHead->pPrev;
//將最後一個結點的前一個結點的pNext指向頭結點
//將頭結點的pPrev指向最後一個結點的前一個節點
pHead->pPrev->pPrev->pNext = pHead;
pHead->pPrev = pHead->pPrev->pPrev;
//釋放最後一個結點
free(pos);
#endif
dListErase(pHead, pHead->pPrev);
}
void dListPopFront(dList *pHead)
{
assert(pHead != NULL);
assert(pHead->pNext != pHead);
#if 0
dList *pos = pHead->pNext;
//將頭結點的pNext指向第一個結點的下一個結點
//將第一個結點的下一個節點的pPrev指向頭結點
pHead->pNext = pHead->pNext->pNext;
pHead->pNext->pNext->pPrev = pHead;
free(pos);
#endif
dListErase(pHead, pHead->pNext);
}
void dListErase(dList *pHead, dList *pos)
{
assert(pHead != NULL);
assert(pHead->pNext != pHead);
//指定結點的前一個結點的pNext指向指定結點的下一個結點
//指定結點的下一個節點的pPrev指向指定結點的前一個節點
pos->pPrev->pNext = pos->pNext;
pos->pNext->pPrev = pos->pPrev;
free(pos);
}
6)清理(清理的結果是將所有的數據結點都刪掉,只留下頭結點)
void dListClear(dList *pHead)
{
assert(pHead);
dList *pdList = pHead->pNext;
dList *pos;
while ( pdList != pHead){
pos = pdList;
pdList = pdList->pNext;
free(pos);
}
pHead->pNext = pHead;
pHead->pPrev = pHead;
}
7)銷燬(清理所有申請的結點)
void dLisrDestroy(dList **ppHead)
{
dListClear(*ppHead);
free(*ppHead);
(*ppHead) = NULL;
}
總結:雙鏈表的每個結點都知道它的前一個結點是誰後一個結點是誰,查找就相對容易,當然如果對鏈表進行改動就相對麻煩了些,單雙鏈表的選擇還需視情況而定