双向循环链表(C++实现)

数据结构(面向对象方法与C++语言描述)(第2版)双向循环链表内容整理

双向循环链表

双向链表又称双链表。使用双向链表(doubly linked list)的目的是为了解决在链表中访问直接前驱和直接后继的问题。因为在双向链表中每个节点都有两个链指针,一个指向节点的直接前驱,一个指向节点的直接后继,这样不论是向前驱方向搜索还是向后继方向搜索,其时间开销都只有O(1)。

在双向链表的每个结点中应有两个链接指针作为它的数据成员:lLink指示它的前驱节点,rLink指示它的后继结点。因此,双向链表的每个结点至少有3个域:
在这里插入图片描述

lLink又称为左链指针,rLink又称为右链指针。双向链表常采用附加头结点的循环链表方式。一个双向链表有一个附加头结点,由链表的头指针first指示,它的data域或者不存放数据,或者存放一个特殊要求的数据;它的lLink指向双向链表的尾指针(最后一个节点),它的rLink指向双向链表的首元结点(第一个结点)。链表的首元结点的左链指针lLink和尾指针的右链指针rLink都指向附加头结点。

在这里插入图片描述

假设指针 p 指向双向循环链表的某一结点,那么,p->lLink 指示 p 所指结点的前驱结点,p->lLink->rLink 中存放的是 p 所指结点的前驱结点的后继结点的地址,即 p 所指结点本身;同样地,p->rLink指示 p 所指结点的后继结点,p->rLink->lLink 也指向 p所指结点本身。因此有 p == p->lLink->rLink == p->rLink->lLink,见图。

在这里插入图片描述

代码实现

环境:vs2019
头文件:DblList.h
源文件:main.cpp

DblList.h代码:

#pragma once

#include <iostream>

template<typename T>
struct DblNode {
	T data;														//链表结点数据
	DblNode<T>* lLink, * rLink;									//链表前驱(左链)、后继(右链)指针
	DblNode(T value = 0, DblNode<T>* left = nullptr, DblNode<T>* right = nullptr)
		:data(value), lLink(left), rLink(right) {}				//构造函数
};

template<typename T>
class DblList {
public:
	DblList(T uniqueVal);										//构造函数:建立附加头结点
	DblList(DblList<T>& L);										//拷贝构造
	~DblList();													//析构函数:释放所用存储
	void makeEmpty();											//将链表置空
	int Length()const;											//计算双链表的长度
	bool IsEmpty() { return first->rlink == first; }			//判双链表空否
	DblNode<T>* getHead()const { return first; }				//取附加头结点地址
	void setHead(DblNode<T>* ptr) { first = ptr; }				//设置附加头结点地址
	DblNode<T>* Search(const T& x);								//在链表中沿后继方向寻找等于给定值x的结点
	DblNode<T>* Locate(int i, int d);							//在链表中定位序号为i(>=0)的结点,d=0按前驱方向,d≠0按后继方向
	bool Insert(int i, const T& x, int d);						//在第i个结点后插入一个包含值x的新结点,d=0按前驱方向,d≠0按后继方向
	bool Remove(int i, T& x, int d);							//删除第i个结点,x返回其值,d=0按前驱方向,d≠0按后继方向
	void output(int d);											//输出,d=0按前驱方向,d≠0按后继方向
private:
	DblNode<T>* first;
};

template<typename T>
inline DblList<T>::DblList(T uniqueVal)
{
	//构造函数:建立双向循环链表的附加头结点,它包含了一个用于某些特定情况的值。
	first = new DblNode<T>(uniqueVal);
	if (first == nullptr)
	{
		std::cerr << "存储分配出错!" << std::endl;
		exit(1);
	}
	first->rLink = first->lLink = first;
}

template<typename T>
inline DblList<T>::DblList(DblList<T>& L)
{
	//拷贝构造函数
	T value;
	DblNode<T>* srcptr = L.getHead();
	DblNode<T>* destptr = first = new DblNode<T>;
	while (srcptr->rLink != L.first)							//逐个结点复制
	{
		value = srcptr->rLink->data;
		destptr->rLink = new DblNode<T>(value);
		destptr->rLink->lLink = destptr;
		destptr = destptr->rLink;
		srcptr = srcptr->rLink;
	}
	destptr->rLink = first;
	first->lLink = destptr;
}

template<typename T>
inline DblList<T>::~DblList()
{
	makeEmpty();
	if (first != nullptr)
	{
		delete first;
		first = nullptr;
	}
}

template<typename T>
inline void DblList<T>::makeEmpty()
{
	//将链表置位空表
	DblNode<T>* q;
	while (first->rLink != first)
	{
		q = first->rLink;
		first->rLink = q->rLink;
		delete q;
	}
	first->lLink = first;
	first->rLink = first;
}

template<typename T>
inline int DblList<T>::Length() const
{
	//计算带附加头接地那的双向循环链表的长度,通过函数返回。
	DblNode<T>* current = first->rLink;
	int count = 0;
	while (current != first)
	{
		current = current->rLink;
		count++;
	}
	return count;
}

template<typename T>
inline DblNode<T>* DblList<T>::Search(const T& x)
{
	//在带附加头结点的双向循环链表中寻找其值等于x的节点,若找到,则函数返回该结点地址,
	//否则函数返回nullptr
	DblNode<T>* current = first->rLink;
	while (current != first && current->data != x)
	{
		current = current->rLink;
	}
	if (current != first) return current;						//搜索成功
	else return nullptr;
}

template<typename T>
inline DblNode<T>* DblList<T>::Locate(int i, int d)
{
	//在带附加头结点的双向循环链表中按d所指方向寻找第i个结点的地址。若d=0,在前驱方向
	//寻找第i个结点,若d≠0,在后继方向寻找第i个结点
	if (first->rLink == first || i == 0) return first;
	DblNode<T>* current;
	if (d == 0) current = first->lLink;							//搜索方向
	else current = first->rLink;
	for (int j = 0; j < i; j++)									//逐个结点检测
	{
		if (current == first) break;							//链太短退出搜索
		else if (d == 0) current = current->lLink;
		else current = current->rLink;
	}
	if (current != first) return current;						//搜索成功
	else return nullptr;										//搜索失败
}

template<typename T>
inline bool DblList<T>::Insert(int i, const T& x, int d)
{
	//建立一个包含有值x的新结点,并将其按d指定的方向插入到第i个结点之后。
	DblNode<T>* current = Locate(i, d);							//查找第i个结点
	if (current == nullptr) return false;						//i不合理,插入失败
	DblNode<T>* newNode = new DblNode<T>(x);
	if (newNode == nullptr)
	{
		std::cerr << "存储分配失败!" << std::endl;
		exit(1);
	}
	if (d == 0)													//前驱方向插入
	{
		newNode->lLink = current->lLink;
		current->lLink = newNode;
		newNode->lLink->rLink = newNode;
		newNode->rLink = current;
	}
	else														//后继方向插入
	{
		newNode->rLink = current->rLink;
		current->rLink = newNode;
		newNode->rLink->lLink = newNode;
		newNode->lLink = current;
	}
	return true;
}

template<typename T>
inline bool DblList<T>::Remove(int i, T& x, int d)
{
	//在带附加头结点的双向循环链表中按照d所指方向删除第i个结点
	DblNode<T>* current = Locate(i, d);							//查找第i个结点
	if (current == nullptr) return false;						//i不合理,删除失败
	current->rLink->lLink = current->lLink;						//从lLink链中摘下
	current->lLink->rLink = current->rLink;						//从rLink链中摘下
	x = current->data;
	delete current;												//删除
	return true;												//删除成功
}

template<typename T>
inline void DblList<T>::output(int d)
{
	//双向循环链表的输出函数:将循环链表中按d指定的方向输出到屏幕上。
	//若d=0,从头结点向前输出,若d≠0,从头结点向后输出。
	std::cout << "链表中的元素为: ";
	DblNode<T>* current;
	if (d == 0)	current = first->lLink;
	else current = first->rLink;
	while (current != first)
	{
		std::cout << current->data << " ";
		if (d == 0)	current = current->lLink;
		else current = current->rLink;
	}
	std::cout << std::endl;
}

main.cpp代码:

#include "DblList.h"

int main()
{
	DblList<int> L1(0);
	//后插法:在头结点前驱结点插入
	L1.Insert(0, 10, 0);
	L1.Insert(0, 20, 0);
	L1.Insert(0, 30, 0);
	std::cout << "逆向输出链表元素" << std::endl;
	L1.output(0);
	std::cout << "正向输出链表元素" << std::endl;
	L1.output(1);

	L1.makeEmpty();
	//前插法:在头结点后继结点插入
	L1.Insert(0, 11, 1);
	L1.Insert(0, 22, 1);
	L1.Insert(0, 33, 1);
	std::cout << "逆向输出链表元素" << std::endl;
	L1.output(0);
	std::cout << "正向输出链表元素" << std::endl;
	L1.output(1);

	DblList<int> L2(L1);
	std::cout << "逆向输出链表元素" << std::endl;
	L1.output(0);
	std::cout << "正向输出链表元素" << std::endl;
	L1.output(1);

	return 0;
}

控制台界面:
在这里插入图片描述

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值