循环链表(C++实现)

  • Post author:
  • Post category:其他




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



循环链表

循环链表(circular list)是另一种形式的表示线性表的链表,它的节点结构与单链表相同,与单链表不同的是链表中表位节点的link域中不是NULL,而是存放了一个指向链表开始结点的指针。这样,只要知道表中任何一个结点的地址,就能遍历表中其他任一结点。图是循环链表的示意图。

在这里插入图片描述

设 current 是在循环链表中逐个结点检测的指针,则在判断current是否达到链表的链尾时,不是判断是否 current->link == NULL,而是判断是否 current->link == first。

循环链表的运算与单链表类似,但在涉及链头与链尾处理时稍有不同。例如,在实现循环链表的插入运算时,如果在表的最前端插入,必须改变链尾最后一个节点的 link 域的值。这就需要循链搜索找到最后一个结点。如果在类定义中给出的链表指针不是放在链头的 first 指针,而是放在链尾的 rear指针,则实现插入或删除运算就会更方便,关键运算为:

newNode->link = rear->link; rear->link = newNode;

在这里插入图片描述

循环链表与单链表一样,可以有附加头结点,这样能够简化链表操作的实现,统一空表与非空表的运算。图给出空表与非空表情形下循环链表的形态。在带附加头结点的循环链表中,链尾结点的 link 域存放的是该链表附加头结点的地址。

在这里插入图片描述



代码实现

环境:vs2019

头文件:CircList.h

源文件:main.cpp

CircList.h代码:

#pragma once

#include <iostream>

template<typename T>
struct CircLinkNode {
	T data;
	CircLinkNode<T>* link;
	CircLinkNode(CircLinkNode<T>* next = nullptr) :data(0), link(next) {}
	CircLinkNode(T d, CircLinkNode<T>* next = nullptr) :data(d), link(next) {}
};

template<typename T>
class CircList
{
public:
	CircList();												//构造函数
	CircList(const T& x);									//构造函数
	CircList(CircList<T>& L);								//拷贝构造
	~CircList();											//析构函数
	void makeEmpty();										//将链表置空
	int Length()const;										//计算循环链表长度
	bool IsEmpty() { return first->link == first ? true : false; }
															//判表空否
	CircLinkNode<T>* getHead()const;						//返回附加头结点地址
	void setHead(CircLinkNode<T>* p);						//设置附加头结点地址
	CircLinkNode<T>* Search(T x);							//搜索含数据x的元素
	CircLinkNode<T>* Locate(int i)const;					//搜索第i个元素的地址
	bool getData(int i, T& x)const;							//取出第i个元素的地址
	void setData(int i, T& x);								//用x修改第i个元素的值
	bool Insert(int i, T& x);								//在第i个元素后插入x
	bool Remove(int i, T& x);								//删除第i个元素,x返回该元素的值
	void output();											//输出
	CircList<T>& operator=(CircList<T>& L);					//重载运算符=
	void inputFront(T endTag);								//前插法建立单链表
	void inputRear(T endTag);								//后插法建立单链表
private:
	CircLinkNode<T>* first, * last;						//头指针,尾指针
};

template<typename T>
inline CircList<T>::CircList()
{
	first = new CircLinkNode<T>;
	first->link = first;
	last = first;
}

template<typename T>
inline CircList<T>::CircList(const T& x)
{
	first = new CircLinkNode<T>(x);
	first->link = first;
	last = first;
}

template<typename T>
inline CircList<T>::CircList(CircList<T>& L)
{
	//拷贝构造函数
	T value;
	CircLinkNode<T>* srcptr = L.getHead();					//被赋值表的附加头结点地址
	CircLinkNode<T>* destptr = first = new CircLinkNode<T>;
	while (srcptr->link != L.first)							//逐个结点复制
	{
		value = srcptr->link->data;
		destptr->link = new CircLinkNode<T>(value);
		destptr = destptr->link;
		srcptr = srcptr->link;
	}
	last = srcptr;
	destptr->link = first;	
}

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

template<typename T>
inline void CircList<T>::makeEmpty()
{
	//将链表置位空表
	CircLinkNode<T>* q;
	while (first->link != first)
	{
		q = first->link;
		first->link = q->link;
		delete q;
	}
	last = first;
}

template<typename T>
inline int CircList<T>::Length() const
{
	//计算带附加头结点的单链表的长度
	CircLinkNode<T>* p = first->link;
	int count = 0;
	while (p != first)										//循链扫描,寻找链尾
	{
		p = p->link;
		count++;
	}
	return count;
}

template<typename T>
inline CircLinkNode<T>* CircList<T>::getHead() const
{
	return first;
}

template<typename T>
inline void CircList<T>::setHead(CircLinkNode<T>* p)
{
	CircLinkNode<T>* current = first;
	while (current->link != first)
	{
		if (current->link == p) break;						//循链找p指针
		else current = current->link;
	}
	first = current->link;
	last = current;
}

template<typename T>
inline CircLinkNode<T>* CircList<T>::Search(T x)
{
	//在表中搜索含数据x的节点,搜索成功时函数返回该节点地址;否则返回first值。
	CircLinkNode<T>* current = first->link;
	while (current != first)
	{
		if (current->data == x) break;						//循链找含x结点
		else current = current->link;
	}
	return current;
}

template<typename T>
inline CircLinkNode<T>* CircList<T>::Locate(int i) const
{
	//定位函数。返回表中第i个元素的地址。若i<0或i超过表中结点个数,则返回first。
	if (i < 0) return first;								//i不合理
	CircLinkNode<T>* current = first->link;
	int k = 1;
	while (current != first && k < i)						//循链找第i个结点
	{
		current = current->link;
		k++;
	}
	return current;											//返回第i个结点地址,若返回first,表示i值太大
}

template<typename T>
inline bool CircList<T>::getData(int i, T& x) const
{
	//取出链表中第i个元素的值。
	if (i <= 0) return false;								//i值太小
	CircLinkNode<T>* current = Locate(i);
	if (current == first) return false;						//i值太大
	else
	{
		x = current->data;
		return true;
	}
}

template<typename T>
inline void CircList<T>::setData(int i, T& x)
{
	//给链表中的第i个元素赋值x。
	if (i <= 0) return;										//i值太小
	CircLinkNode<T>* current = Locate(i);
	if (current == first) return;							//i值太大
	else current->data = x;
}

template<typename T>
inline bool CircList<T>::Insert(int i, T& x)
{
	//将新元素x插入在链表中第i个结点之后。
	CircLinkNode<T>* current = Locate(i);
	if (current == first) return false;						//插入不成功
	CircLinkNode<T>* newNode = new CircLinkNode<T>(x);
	if (newNode == nullptr)
	{
		std::cerr << "存储分配错误!" << std::endl;
		exit(1);
	}
	newNode->link = current->link;
	current->link = newNode;
	if (newNode->link == first) last = newNode;
	return true;											//插入成功
}

template<typename T>
inline bool CircList<T>::Remove(int i, T& x)
{
	//将链表中的第i个元素删去,通过引用型参数x返回该元素的值。
	CircLinkNode<T>* current = Locate(i - 1);
	if (current == first || current->link == first)	return false;
	//删除不成功
	CircLinkNode<T>* del = current->link;					//重新拉链,将被删结点从链中摘下
	current->link = del->link;
	x = del->data;
	delete del;												//取出被删结点中的数据值
	if (current->link == first) last = current;
	return true;
}

template<typename T>
inline void CircList<T>::output()
{
	//循环链表的输出函数:将循环链表中所有数据按逻辑顺序输出到屏幕上。
	std::cout << "链表中的元素为: ";
	CircLinkNode<T>* current = first->link;
	while (current != first)
	{
		std::cout << current->data << " ";
		current = current->link;
	}
	std::cout << std::endl;
}

template<typename T>
inline CircList<T>& CircList<T>::operator=(CircList<T>& L)
{
	//重载函数:赋值操作,形式如 A = B,其中 A 是调用此操作的List对象,B 是参数表中的
	//引用型参数L结合的实参。
	makeEmpty();
	T value;
	CircLinkNode<T>* srcptr = L.getHead();					//被复制表的附加头结点地址
	CircLinkNode<T>* destptr = first = new CircLinkNode<T>;
	while (srcptr->link != L.first)							//逐个结点复制
	{
		value = srcptr->link->data;
		destptr->link = new CircLinkNode<T>(value);
		destptr = destptr->link;
		srcptr = srcptr->link;
	}
	destptr->link = first;
	last = destptr;
	return *this;											//返回操作对象地址
}

template<typename T>
inline void CircList<T>::inputFront(T endTag)
{
	//endTag是约定的输入序列结束的标志。如果输入序列是正整数,endTag可以是0或负数;
	//如果输入序列是字符,endTag可以是字符集中不会出现的字符,如"\0"。
	CircLinkNode<T>* newNode;
	T val;
	makeEmpty();
	std::cin >> val;
	while (val != endTag)
	{
		newNode = new CircLinkNode<T>(val);					//创建新结点
		if (newNode == nullptr)
		{
			std::cerr << "存储分配错误!" << std::endl;
			exit(1);
		}
		newNode->link = first->link;
		first->link = newNode;								//插入到表最前端
		if (last == first)
		{
			last = newNode;
		}
		std::cin >> val;
	}
}

template<typename T>
inline void CircList<T>::inputRear(T endTag)
{
	//endTag是约定的输入序列结束的标志
	CircLinkNode<T>* newNode;
	T val;
	makeEmpty();
	std::cin >> val;	
	while (val != endTag)									//last指向表尾
	{
		newNode = new CircLinkNode<T>(val);
		if (newNode == nullptr)
		{
			std::cerr << "存储分配错误!" << std::endl;
			exit(1);
		}		
		last->link = newNode;
		last = newNode;
		std::cin >> val;									//插入到表末端
	}
	last->link = first;
}

main.cpp代码:

#include "CircList.h"

int main()
{
	CircList<int> L1;
	CircList<int> L2;
	int val = 0;
	bool flag = false;

	std::cout << "Q:测试IsEmpty()  链表L1是否为空" << std::endl;
	flag = L1.IsEmpty();
	if (flag) std::cout << "链表L1为空" << std::endl;
	else std::cout << "链表L1不为空" << std::endl;

	std::cout << "Q:测试inputFront()  前插法建立单链表L1" << std::endl;
	L1.inputFront(-1);
	L1.output();

	std::cout << "Q:测试IsEmpty()  链表L1是否为空" << std::endl;
	flag = L1.IsEmpty();
	if (flag) std::cout << "链表L1为空" << std::endl;
	else std::cout << "链表不为空" << std::endl;

	std::cout << "Q:测试Length()  链表L1的长度为:" << std::endl;
	std::cout << L1.Length() << std::endl;

	std::cout << "Q:测试getData()  链表L1取第3个元素的值:" << std::endl;
	L1.getData(3, val);
	std::cout << val << std::endl;

	std::cout << "Q:测试setData()  链表L1修改第6个元素的值为99" << std::endl;
	val = 99;
	L1.setData(6, val);
	L1.output();

	std::cout << "Q:测试Insert()  链表L1在第8个元素后插入77" << std::endl;
	val = 77;
	L1.Insert(8, val);
	L1.output();

	std::cout << "Q:测试Remove()  链表L1删除第3个元素" << std::endl;
	L1.Remove(3, val);
	std::cout << "删除的元素为:" << val << std::endl;
	L1.output();

	std::cout << "Q:测试setHead()  链表L1设置第4个元素为头结点" << std::endl;
	CircLinkNode<int>* pnode = L1.Locate(4);
	L1.setHead(pnode);
	L1.output();

	std::cout << "Q:测试inputRear()  后插法建立单链表L2" << std::endl;
	L2.inputRear(-1);
	L2.output();

	std::cout << "Q:测试operator=()  赋值单链表L2" << std::endl;
	L2 = L1;
	L2.output();

	return 0;
}

控制台界面:

在这里插入图片描述



版权声明:本文为st66688原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。