本篇主要實現了帶有頭結點的雙向循環鏈表的基本操作,其中包括增刪改查以及清空銷燬、判空、求結點個數等等。
頭文件
DoubleLinkList.h
# ifndef __DOUBLELINKLIST_H__
# define __DOUBLELINKLIST_H__
# include <stdio.h>
# include <assert.h>
# include <stdlib.h>
enum OPTION
{
QUIT, // 退出
ADD, // 添加
DETELE, // 刪除
SEARCH, // 查找
MODIFY, // 修改
CLEAR, // 清空
NUM_NODE, // 結點數
IS_EMPTY, // 判空
DESTORY, // 銷燬
SHOW // 顯示
};
enum ADD_DATA
{
EXIT_ADD, // 退出添加
PUSH_FRONT, // 頭插
PUSH_BACK, // 尾插
PUSH_INSERT // 任意插
};
enum DELETE_DATA
{
EXIT_DELETE, // 退出刪除
POP_FRONT, // 頭刪
POP_BACK, // 尾刪
POP_ERASE, // 任意刪
REMOVE_FIRST, // 只刪除第一個要刪除的
REMOVE_ALL // 刪除所有要刪除的
};
typedef int DataType;
typedef struct DListNode
{
DataType data; // 數據
struct DListNode * pPrev; // 指向上一個結點
struct DListNode * pNext; // 指向下一個結點
}DListNode, * pDListNode;
void DListInit(pDListNode * pHead); // 初始化
void DListPrint(pDListNode pHead); // 打印鏈表內容
void DListPushFront(pDListNode pHead, DataType data); // 頭插
void DListPushBack(pDListNode pHead, DataType data); // 尾插
void DListInsert(pDListNode pHead, pDListNode pPos, DataType data); // 給定結點插入,插入到結點前
void DListAdd(pDListNode pHead); // 雙鏈表添加數據
void DListPopFront(pDListNode pHead); // 頭刪
void DListPopBack(pDListNode pHead); // 尾刪
void DListErase(pDListNode pHead, pDListNode pPos); // 給定結點刪除
void DListSearch(pDListNode pHead); // 查找輸入數據,如果找到該數據則提示找到,反之則提示沒找到
void DListModify(pDListNode pHead); // 修改雙鏈表數據
void DListRemove(pDListNode pHead, DataType data); // 按值刪除,只刪遇到的第一個
void DListRemoveAll(pDListNode pHead, DataType data); // 按值刪除,刪除所有的
void DListDel(pDListNode pHead); // 雙鏈表刪除數據
void DListIsEmpty(pDListNode pHead); // 判空
void DListSize(pDListNode pHead); // 結點數
void DListClear(pDListNode pHead); // 清空
void DListDestroy(pDListNode * pHead); // 銷燬
# endif // __DOUBLELINKLIST_H__
操作說明
首先,分別使用頭插、尾插、任意插三種方式添加 6 個數字,這裏需要說明的是,由於循環鏈表最終形成了一個環,這裏通過兩邊的數字爲 0 來模擬環的實現,實際上就是 頭結點,這裏的 0 毫無實際意義。
接下來刪除第一個 1,
然後刪除所有的 1,
任意刪後,
此時判斷鏈表是否爲空,
計算鏈表的結點數(ps: 包含頭結點,並且頭結點數記爲 1),
頭刪後,
尾刪後,
此時再次插入兩個數字,
清空鏈表,
再插入兩個數字,查找並修改其中的一個數字,
最後銷燬鏈表並退出。
難點剖析
下面通過兩張圖對刪除和插入操作加以分析。
- 在任意結點前插入結點
需要注意的是:pPrevPos 是 pPos 結點的前驅。
- 在任意結點前刪除結點
需要注意的是:pPrevNode 是 pPos 結點的前驅,pNextNode 是 pPos 結點的後繼。
源代碼
DoubleLinkList.c
#define _CRT_SECURE_NO_WARNINGS 1
/*
* Copyright (c) 2018, code farmer from sust
* All rights reserved.
*
* 文件名稱:DoubleLinkList.c
* 功能:有頭結點的循環雙鏈表基本操作內部實現細節
*
* 當前版本:V1.0
* 作者:sustzc
* 完成日期:2018年5月29日13:53:31
*/
# include "DoubleLinkList.h"
/*
* 函數名稱:CreateNewNode
*
* 函數功能:創建新節點
*
* 入口參數:data
*
* 出口參數:void
*
* 返回類型:pDListNode
*/
static pDListNode CreateNewNode(DataType data)
{
pDListNode pNewNode = (pDListNode)malloc(sizeof(DListNode));
if (NULL == pNewNode)
{
perror("內存分配失敗!\n");
exit(-1);
}
else
{
pNewNode->data = data;
pNewNode->pPrev = NULL;
pNewNode->pNext = NULL;
}
return pNewNode;
}
/*
* 函數名稱:DListInit
*
* 函數功能:帶有頭結點的循環雙鏈表的初始化
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListInit(pDListNode * pHead)
{
int no_data = 0;
assert(NULL != pHead);
//頭結點的數據無效(ps:這裏用0表示)
*pHead = CreateNewNode(no_data);
(*pHead)->pPrev = *pHead;
(*pHead)->pNext = *pHead;
return;
}
/*
* 函數名稱:DListPrint
*
* 函數功能:打印鏈表存儲的內容(ps:本身有頭結點,因此傳一級指針即可)
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListPrint(pDListNode pHead)
{
pDListNode pCur = NULL;
assert(NULL != pHead);
printf("鏈表內容爲:\n");
printf("%2d -> ", pHead->data);
for (pCur = pHead->pNext; pHead != pCur; pCur = pCur->pNext)
{
printf("%2d -> ", pCur->data);
}
printf("%2d\n", pHead->data);
return;
}
/*
* 函數名稱:AddMenu
*
* 函數功能:添加數據菜單顯示
*
* 入口參數:void
*
* 出口參數:select
*
* 返回類型:int
*/
int AddMenu(void)
{
int select = 0;
printf("************************************\n");
printf("******* 添加數據 ********\n");
printf("******* 1. 頭插 2. 尾插 *******\n");
printf("******* 3. 任意插 0. 退出 *******\n");;
printf("************************************\n");
printf("select>");
assert(1 == scanf("%d", &select));
return select;
}
/*
* 函數名稱:DListPushFront
*
* 函數功能:頭插
*
* 入口參數:pHead, data
*
* 出口參數:void
*
* 返回類型:void
*/
void DListPushFront(pDListNode pHead, DataType data)
{
pDListNode pNewNode = NULL;
pDListNode pFirst = NULL;
assert(NULL != pHead);
pNewNode = CreateNewNode(data);
pFirst = pHead->pNext;
pHead->pNext = pNewNode;
pNewNode->pPrev = pHead;
pNewNode->pNext = pFirst;
pFirst->pPrev = pNewNode;
return;
}
/*
* 函數名稱:DListPushBack
*
* 函數功能:尾插
*
* 入口參數:pHead, data
*
* 出口參數:void
*
* 返回類型:void
*/
void DListPushBack(pDListNode pHead, DataType data)
{
pDListNode pNewNode = NULL;
pDListNode pLast = NULL;
assert((NULL != pHead));
pNewNode = CreateNewNode(data);
pLast = pHead->pPrev;
pLast->pNext = pNewNode;
pNewNode->pPrev = pLast;
pNewNode->pNext = pHead;
pHead->pPrev = pNewNode;
return;
}
/*
* 函數名稱:DListInsert
*
* 函數功能:給定結點插入,插入到結點前
*
* 入口參數:pHead, pPos, data
*
* 出口參數:void
*
* 返回類型:void
*/
void DListInsert(pDListNode pHead, pDListNode pPos, DataType data)
{
pDListNode pNewNode = NULL;
pDListNode pPrevPos = NULL;
assert((NULL != pHead) && (NULL != pPos));
pNewNode = CreateNewNode(data);
pPrevPos = pPos->pPrev;
pPrevPos->pNext = pNewNode;
pNewNode->pPrev = pPrevPos;
pNewNode->pNext = pPos;
pPos->pPrev = pNewNode;
return;
}
/*
* 函數名稱:DListAdd
*
* 函數功能:雙鏈表的添加
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListAdd(pDListNode pHead)
{
DataType data = 0;
int select = AddMenu();
assert(NULL != pHead);
switch (select)
{
case EXIT_ADD:
{
printf("退出添加!\n");
break;
}
case PUSH_FRONT:
{
printf("請輸入要添加的數據:>");
assert(1 == scanf("%d", &data));
DListPushFront(pHead, data);
printf("頭插成功!\n");
break;
}
case PUSH_BACK:
{
printf("請輸入要添加的數據:>");
assert(1 == scanf("%d", &data));
DListPushBack(pHead, data);
printf("尾插成功!\n");
break;
}
case PUSH_INSERT:
{
pDListNode pCur = pHead->pNext->pNext;
printf("請輸入要添加的數據:>");
assert(1 == scanf("%d", &data));
DListInsert(pHead, pCur, data);
printf("任意插成功!\n");
break;
}
default:
{
printf("輸入有誤!\n");
break;
}
}
return;
}
/*
* 函數名稱:DelMenu
*
* 函數功能:刪除數據菜單顯示
*
* 入口參數:void
*
* 出口參數:select
*
* 返回類型:int
*/
int DelMenu(void)
{
int select = 0;
printf("************************************************************************\n");
printf("******* 刪除數據 ********\n");
printf("******* 1. 頭刪 2. 尾刪 ********\n");
printf("******* 3. 任意刪 4. 只刪除要刪數據中的第一個 ********\n");;
printf("******* 5. 刪除所有要刪除的數據 0. 退出 ********\n");
printf("************************************************************************\n");
printf("select>");
assert(1 == scanf("%d", &select));
return select;
}
/*
* 函數名稱:DListPopFront
*
* 函數功能:頭刪
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListPopFront(pDListNode pHead)
{
pDListNode pFirst = NULL;
pDListNode pFirstNext = NULL;
assert(NULL != pHead);
pFirst = pHead->pNext;
pFirstNext = pHead->pNext->pNext;
pHead->pNext = pFirstNext;
pFirstNext->pPrev = pHead;
free(pFirst);
return;
}
/*
* 函數名稱:DListPopBack
*
* 函數功能:尾刪
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListPopBack(pDListNode pHead)
{
pDListNode pLast = NULL;
pDListNode pLastPrev = NULL;
assert(NULL != pHead);
pLast = pHead->pPrev;
pLastPrev = pHead->pPrev->pPrev;
pLastPrev->pNext = pHead;
pHead->pPrev = pLastPrev;
free(pLast);
return;
}
/*
* 函數名稱:DListPopBack
*
* 函數功能:給定結點刪除
*
* 入口參數:pHead, pPos
*
* 出口參數:void
*
* 返回類型:void
*/
void DListErase(pDListNode pHead, pDListNode pPos)
{
pDListNode pPrevNode = NULL;
pDListNode pNextNode = NULL;
assert((NULL != pHead) && (NULL != pPos));
pPrevNode = pPos->pPrev;
pNextNode = pPos->pNext;
pPrevNode->pNext = pNextNode;
pNextNode->pPrev = pPrevNode;
free(pPos);
return;
}
/*
* 函數名稱:DListFind
*
* 函數功能:按值查找,返回第一個找到的結點指針,如果沒找到,返回 NULL
*
* 入口參數:pHead, data
*
* 出口參數:pCur or NULL
*
* 返回類型:pDListNode
*/
pDListNode DListFind(pDListNode pHead, DataType data)
{
pDListNode pCur = NULL;
assert(NULL != pHead);
for (pCur = pHead->pNext; pHead != pCur; pCur = pCur->pNext)
{
if (data == pCur->data)
{
return pCur;
}
else
{
;
}
}
return NULL;
}
/*
* 函數名稱:DListSearch
*
* 函數功能:查找輸入數據,如果找到該數據則提示找到,反之則提示沒找到
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListSearch(pDListNode pHead)
{
DataType data = 0;
pDListNode pPosNode = NULL;
assert(NULL != pHead);
printf("請輸入要查找的數據:>");
assert(1 == scanf("%d", &data));
pPosNode = DListFind(pHead, data);
if (NULL != pPosNode)
{
printf("找到了%d\n", data);
}
else
{
printf("沒找到%d\n", data);
}
return;
}
/*
* 函數名稱:DListModify
*
* 函數功能:修改雙鏈表數據
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListModify(pDListNode pHead)
{
pDListNode pPosNode = NULL;
DataType old_data = 0;
DataType new_data = 0;
assert(NULL != pHead);
printf("請輸入要修改的數據:>");
assert(1 == scanf("%d", &old_data));
pPosNode = DListFind(pHead, old_data);
if (NULL != pPosNode)
{
printf("請輸入修改後的數據:>");
assert(1 == scanf("%d", &new_data));
pPosNode->data = new_data;
printf("修改成功!\n");
}
else
{
printf("沒找到%d\n", old_data);
}
return;
}
/*
* 函數名稱:DListRemove
*
* 函數功能:按值刪除,只刪遇到的第一個
*
* 入口參數:pHead, data
*
* 出口參數:void
*
* 返回類型:void
*/
void DListRemove(pDListNode pHead, DataType data)
{
pDListNode pPosNode = DListFind(pHead, data);
if (NULL != pPosNode)
{
DListErase(pHead, pPosNode);
}
else
{
printf("要刪除的數據不存在!\n");
}
return;
}
/*
* 函數名稱:DListRemoveAll
*
* 函數功能:按值刪除,刪除所有的
*
* 入口參數:pHead, data
*
* 出口參數:void
*
* 返回類型:void
*/
void DListRemoveAll(pDListNode pHead, DataType data)
{
pDListNode pDel = pHead->pNext;
pDListNode pNextNode = NULL;
pDListNode pPrevNode = NULL;
while (pHead != pDel)
{
if (data == pDel->data)
{
pNextNode = pDel->pNext;
pPrevNode = pDel->pPrev;
//將pDel這個節點空出來
pPrevNode->pNext = pNextNode;
pNextNode->pPrev = pPrevNode;
free(pDel);
//pDel從pNextNode開始向後遍歷
pDel = pNextNode;
}
else
{
pDel = pDel->pNext;
}
}
return;
}
/*
* 函數名稱:DListDel
*
* 函數功能:雙鏈表的刪除
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListDel(pDListNode pHead)
{
DataType data = 0;
int select = DelMenu();
assert(NULL != pHead);
switch (select)
{
case EXIT_DELETE:
{
printf("退出刪除!\n");
break;
}
case POP_FRONT:
{
DListPopFront(pHead);
printf("頭刪成功!\n");
break;
}
case POP_BACK:
{
DListPopBack(pHead);
printf("尾刪成功!\n");
break;
}
case POP_ERASE:
{
DListErase(pHead, pHead->pNext->pNext);
printf("任意刪成功!\n");
break;
}
case REMOVE_FIRST:
{
printf("請輸入要刪除的數據:>");
assert(1 == scanf("%d", &data));
DListRemove(pHead, data);
printf("刪除第一個要刪除的數據成功!\n");
break;
}
case REMOVE_ALL:
{
printf("請輸入要刪除的數據:>");
assert(1 == scanf("%d", &data));
DListRemoveAll(pHead, data);
printf("要刪除的數據刪除成功!\n");
break;
}
default:
{
printf("輸入有誤!\n");
break;
}
}
return;
}
/*
* 函數名稱:DListIsEmpty
*
* 函數功能:雙鏈表是否爲空
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListIsEmpty(pDListNode pHead)
{
pDListNode pNextNode = NULL;
pDListNode pPrevNode = NULL;
assert(NULL != pHead);
pPrevNode = pHead->pPrev;
pNextNode = pHead->pNext;
if ((pHead != pPrevNode) && (pHead != pNextNode))
{
printf("鏈表不爲空!\n");
}
else
{
printf("鏈表爲空!\n");
}
return;
}
/*
* 函數名稱:DListSize
*
* 函數功能:雙鏈表的結點數(ps:本身有頭結點,因此傳一級指針即可)
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListSize(pDListNode pHead)
{
//頭結點也算一個結點
int count = 1;
pDListNode pCur = NULL;
assert(NULL != pHead);
for (pCur = pHead->pNext; pHead != pCur; pCur = pCur->pNext)
{
count++;
}
printf("雙鏈表的結點數爲: %d\n", count);
return;
}
/*
* 函數名稱:DListClear
*
* 函數功能:清空雙鏈表,頭結點還在
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListClear(pDListNode pHead)
{
pDListNode pNode = NULL;
pDListNode pNextNode = NULL;
assert(NULL != pHead);
for (pNode = pHead->pNext; pHead != pNode; pNode = pNextNode)
{
pNextNode = pNode->pNext;
free(pNode);
}
pHead->pPrev = pHead;
pHead->pNext = pHead;
printf("清空雙鏈表成功!\n");
return;
}
/*
* 函數名稱:DListDestroy
*
* 函數功能:銷燬雙鏈表,頭結點不在
*
* 入口參數:pHead
*
* 出口參數:void
*
* 返回類型:void
*/
void DListDestroy(pDListNode * pHead)
{
pDListNode pNode = NULL;
pDListNode pNextNode = NULL;
assert(NULL != pHead);
for (pNode = (*pHead)->pNext; (*pHead) != pNode; pNode = pNextNode)
{
pNextNode = pNode->pNext;
free(pNode);
}
free(*pHead);
*pHead = NULL;
printf("銷燬雙鏈表成功!\n");
return;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
/*
* Copyright (c) 2018, code farmer from sust
* All rights reserved.
*
* 文件名稱:test.c
* 功能:測試帶有頭結點的循環雙鏈表基本操作
*
* 當前版本:V1.0
* 作者:sustzc
* 完成日期:2018年5月29日13:55:40
*/
# include "DoubleLinkList.h"
/*
* 函數名稱:MainMenu
*
* 函數功能:雙鏈表主菜單顯示
*
* 入口參數:void
*
* 出口參數:choose
*
* 返回類型:int
*/
int MainMenu(void)
{
int choose = 0;
printf("**************************************\n");
printf("******* 歡迎操作雙鏈表 *******\n");
printf("******* 1. 添加 2. 刪除 *******\n");
printf("******* 3. 查找 4. 修改 *******\n");
printf("******* 5. 清空 6. 數量 *******\n");
printf("******* 7. 判空 8. 銷燬 *******\n");
printf("******* 9. 顯示 0. 退出 *******\n");
printf("**************************************\n");
printf("choose>");
assert(1 == scanf("%d", &choose));
return choose;
}
/*
* 函數名稱:main
*
* 函數功能:主程序
*
* 入口參數:void
*
* 出口參數:0
*
* 返回類型:int
*/
int main(void)
{
int choose = 0;
pDListNode dlnode = NULL;
//初始化雙鏈表
DListInit(&dlnode);
do
{
choose = MainMenu();
switch(choose)
{
case QUIT:
printf("退出雙鏈表!\n");
break;
case ADD:
{
DListAdd(dlnode);
DListAdd(dlnode);
DListAdd(dlnode);
}
break;
case DETELE:
DListDel(dlnode);
break;
case SEARCH:
DListSearch(dlnode);
break;
case MODIFY:
DListModify(dlnode);
break;
case CLEAR:
DListClear(dlnode);
break;
case NUM_NODE:
DListSize(dlnode);
break;
case IS_EMPTY:
DListIsEmpty(dlnode);
break;
case DESTORY:
DListDestroy(&dlnode);
break;
case SHOW:
DListPrint(dlnode);
break;
default:
printf("輸入有誤,請重新輸入!\n");
break;
}
}while(choose);
return 0;
}