数据结构_双向循环链表的基本实现

有了以上单链表的基础后,实现双向带头循环链表也不是很复杂的。双向循环链表相对于单链表效率高。其本质是以空间换取了时间。
//DblCrcultList.h
#pragma once

typedef struct DbListNode{
	int iData;				//数据域
	DbListNode* pPre;		//前驱指针域
	DbListNode* pNext;		//后继指针域
	DbListNode()
	{
		iData = 0;
		pPre = nullptr;
		pNext = nullptr;
	}
}DbListNode;

//双向循环链表类
class DbCrcultList
{
public:
	DbCrcultList();
	~DbCrcultList();
public:
	//链表结构初始化
	DbListNode* InitDbCrcultList();
	//销毁整个链表
	DbListNode* DestroyDbCrcultList(DbListNode* pHead);
	//申请链表节点所需的内存区域
	DbListNode* BuyNodeFromMem();
	//尾插元素
	DbListNode* PushBackIntoList(DbListNode* pHead, int iData);
	//头插元素
	DbListNode* PushFrontIntoList(DbListNode* pHead, int iData);
	//头删元素
	DbListNode* PopFrontFromList(DbListNode* pHead);
	//尾删元素
	DbListNode* PopBackFromList(DbListNode* pHead);
	//打印链表的数据
	void PrintDbListData(DbListNode* pHead);
};
//DblCrcultList.cpp
#include "DblCrcultList.h"
#include <iostream>

DbCrcultList::DbCrcultList()
{
}

DbCrcultList::~DbCrcultList()
{
}

DbListNode* DbCrcultList::InitDbCrcultList()
{
	//双向循环链表初始化,实际就是创建一个空的链表。该链表目前只有一个头节点,
	//约定头节点的数据域为-1
	//且该节点的前驱指针指向自身,后继指针也指向自身
	DbListNode* pHead = BuyNodeFromMem();
	pHead->iData = -1;
	pHead->pNext = pHead;
	pHead->pPre = pHead;
	return pHead;
}

DbListNode* DbCrcultList::DestroyDbCrcultList(DbListNode* pHead)
{
	if (pHead == nullptr || pHead->pNext == pHead)
	{
		delete pHead;
		pHead = nullptr;
		std::cout << "链表销毁完成!" << std::endl;
		return pHead;
	}

	DbListNode* pCur = pHead->pNext;
	DbListNode* pDel = nullptr;
	while (pCur->pNext != pHead)
	{
		pDel = pCur;
		pCur = pCur->pNext;
		delete pDel;
		pDel = nullptr;
	}
	delete pCur;
	pCur = nullptr;
	std::cout << "链表销毁完成!" << std::endl;

	return nullptr;
}

DbListNode * DbCrcultList::BuyNodeFromMem()
{
	//向内存中申请一块内存,若返回值为空,则代表内存申请失败。
	DbListNode* pHead = new DbListNode();
	return pHead;
}

DbListNode* DbCrcultList::PushBackIntoList(DbListNode * pHead, int iData)
{
	//向双向循环链表尾部插入节点其实与单链表尾插很类似,即:

	//先向操作系统申请一块内存
	//把原链表的尾指针指向新节点
	//新节点的前驱指针只想原链表尾节点
	//新节点的后继指针指向链表头节点
	//原链表的头节点指向新节点

	if (pHead == nullptr)
	{
		return pHead;
	}

	DbListNode* pNewNode = BuyNodeFromMem();
	pNewNode->iData = iData;

	//先记录原链表的末尾节点
	//再将原末尾节点的后继指针指向新节点
	//再将新节点的前驱指针指向原末尾节点
	//再将头节点的前驱指针指向新的末尾节点
	//最后将新末尾节点的后继指针指向头节点
	DbListNode* pOldTailNode = pHead->pPre;
	pOldTailNode->pNext = pNewNode;
	pNewNode->pPre = pOldTailNode;
	pHead->pPre = pNewNode;
	pNewNode->pNext = pHead;
	return pHead;
}

DbListNode* DbCrcultList::PushFrontIntoList(DbListNode* pHead, int iData)
{
	if (pHead == nullptr)
	{
		return nullptr;
	}

	//头插元素,即:
	//把新元素的前驱指针指向头节点
	//把新元素的后继指针指向头节点的原后继节点
	//把头节点的原后继节点的前驱指针指向新元素
	//把头节点的后继指针指向新元素

	DbListNode* pNewNode = BuyNodeFromMem();
	pNewNode->iData = iData;

	if (pNewNode)
	{
		pNewNode->pPre = pHead;
		pNewNode->pNext = pHead->pNext;
		pHead->pNext->pPre = pNewNode;
		pHead->pNext = pNewNode;
	}
	return pHead;
}

DbListNode* DbCrcultList::PopFrontFromList(DbListNode* pHead)
{
	if (pHead == nullptr || pHead->pNext == pHead)
	{
		return pHead;
	}

	//头删元素,即:
	//先将需要删除的元素记录下来
	//再把头节点的后继指针指向头节点原后继的后继
	//再把头节点的原后继的前驱指向头节点本身
	//删除掉目标元素
	//被删除的元素的地址置空
	DbListNode* pDelNode = pHead->pNext;

	pHead->pNext = pHead->pNext->pNext;
	pHead->pNext->pPre = pHead;
	delete pDelNode;
	pDelNode = nullptr;

	return pHead;
}

DbListNode* DbCrcultList::PopBackFromList(DbListNode* pHead)
{
	if (pHead == nullptr || pHead->pNext == pHead)
	{
		return pHead;
	}

	//尾删元素,即:
	//先将需要删除的元素记录下来
	//再把头节点的前驱指针指向目标节点的前驱节点
	//再把目标节点的前驱节点的后继节点指向头节点
	//删除掉目标元素
	//被删除的元素的地址置空

	DbListNode* pDelNode = pHead->pPre;
	pHead->pPre = pDelNode->pPre;
	pDelNode->pPre->pNext = pHead;
	delete pDelNode;
	pDelNode = nullptr;

	return pHead;
}

void DbCrcultList::PrintDbListData(DbListNode * pHead)
{
	if (pHead == nullptr || pHead->pNext == pHead)
	{
		std::cout << "链表为空,打印失败!" << std::endl;
		return;
	}

	//打印链表的思路如下:
	//维护一个指向头节点的指针变量pCur,从头指针的位置开始遍历,
	//如果pCur的后继节点指向了头节点,则代表整个链表遍历完成
	DbListNode* pCur = pHead->pNext;
	while (pCur != nullptr && pCur != pHead)
	{
		std::cout << pCur->iData << "-->";
		pCur = pCur->pNext;
	}
}

//main.cpp
#include "DblCrcultList.h"
#include <iostream>

int main()
{
	DbCrcultList* pList = new DbCrcultList();
	if (pList)
	{
		//初始化链表
		DbListNode* pHead = pList->InitDbCrcultList();
		if (pHead)
		{
			//尾插链表
			std::cout << "尾插:" << std::endl;
			pList->PushBackIntoList(pHead, 5);
			pList->PushBackIntoList(pHead, 6);
			pList->PushBackIntoList(pHead, 7);
			pList->PushBackIntoList(pHead, 8);
			pList->PushBackIntoList(pHead, 9);
			pList->PrintDbListData(pHead);
			std::cout << std::endl;

			//头插链表
			std::cout << "头插:" << std::endl;
			pList->PushFrontIntoList(pHead, 1);
			pList->PushFrontIntoList(pHead, 2);
			pList->PushFrontIntoList(pHead, 3);
			pList->PushFrontIntoList(pHead, 4);
			pList->PrintDbListData(pHead);
			std::cout << std::endl;


			//头删链表
			std::cout << "头删:" << std::endl;
			pList->PopFrontFromList(pHead);
			pList->PopFrontFromList(pHead);
			pList->PopFrontFromList(pHead);
			pList->PopFrontFromList(pHead);
			pList->PopFrontFromList(pHead);
			pList->PrintDbListData(pHead);
			std::cout << std::endl;

			//尾删链表
			std::cout << "尾删:" << std::endl;
			pList->PopBackFromList(pHead);
			pList->PopBackFromList(pHead);
			pList->PrintDbListData(pHead);
			std::cout << std::endl;

			//销毁链表
			std::cout << "销毁整个链表:" << std::endl;
			pList->DestroyDbCrcultList(pHead);
		}
	}
	return 0;
}

运行结果:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值