本篇主要实现了带有头结点的双向循环链表的基本操作,其中包括增删改查以及清空销毁、判空、求结点个数等等。
头文件
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),
头删后,
尾删后,
此时再次插入两个数字,
清空链表,
再插入两个数字,查找并修改其中的一个数字,
最后销毁链表并退出。
难点剖析
下面通过两张图对删除和插入操作加以分析。
1.在任意结点前插入数据
需要注意的是,pPrevPos是pPos结点的前驱。
2.在任意结点前删除数据
需要注意的是,pPrevNode是pPos结点的前驱,pNextNode是pPos结点的后继。
源代码
1.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;
}
2.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;
}