【数据结构】带有头结点的双向循环链表的基本操作

  本篇主要实现了带有头结点的双向循环链表的基本操作,其中包括增删改查以及清空销毁、判空、求结点个数等等。

头文件

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,

扫描二维码关注公众号,回复: 2167724 查看本文章

  任意删后,

  此时判断链表是否为空,

  计算链表的结点数(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;
}

猜你喜欢

转载自blog.csdn.net/sustzc/article/details/80580463