双向循环链表的C++实现

双向循环链表的C++实现

 

        双向循环链表与双向链表相比,多了一对指针,从链表尾的下一个结点指向首元素,以及从链表头的上一个结点指向链表尾。

 

        双向循环链表的结构如图所示:

 

        双向循环链表的结点定义,与双向链表的结点定义一致:

#ifndef SIMPLENODE_HPP
#define SIMPLENODE_HPP
template<typename T>        //再定义一个模板  写双链表 
class DoubleNode
{
public:
	T element;
	DoubleNode* prev;
	DoubleNode* next;
	DoubleNode(const T& theElement,DoubleNode* thePrev=NULL,DoubleNode* theNext=NULL)
		       :element(theElement),prev(thePrev),next(theNext) {}
	//这里已经创建了一个双链表 
}; 
#endif

 

       

        在组成一个循环双向链表后,还需要补充一些功能,以便后续实现,模仿之前的双向链表:

        1.定义这个双向链表类的构造函数及析构函数

        2.清空链表内所有元素

        3.给出元素位置再返回对应结点

        4.返回链表内部元素个数

        5.判断这个链表是否为空

        6.返回链表首尾的元素值

        7.查找元素是否在此链表内,如果在则返回所在位置

        8.从首端到尾端输出链表上的各元素,以及反序输出链表上各元素

        9.对链表插入元素以及删除元素

 

 

        双向循环链表的类框架如下:

#ifndef CIRCLEDOUBLELINKLIST_HPP
#define CIRCLEDOUBLELINKLIST_HPP
#include<iostream>
#include"simplenode.hpp"
//这里默认位置pos的开始是从1开始,而不是从0开始 
template<class T>
class CircleDoubleLinkList          //双链表类的定义
{
private:
	DoubleNode<T>* head;           //链表头指针
	DoubleNode<T>* tail;           //链表尾指针
	int size;             //元素个数
	DoubleNode<T>* GetPointAt(int pos) 
	{...}
public:
	CircleDoubleLinkList():head(),tail(),size(0) {}
	~CircleDoubleLinkList() {Clear();}
	void Clear()
	{...}
	int Size() {...}        //返回元素个数
	bool isempty()  {...}  //返回链表是否为空 
	//-----------------------------------------------------
        T GetHeadVal() 
	{...} 
	T GetTailVal()
	{...}
	int Find(T val)
	{...}
	void ShowAllVal()
	{...}
	void ReverseShowAllVal()
	{...}
	void AddBack(T val)
	{
		DoubleNode<T>* pNode=new DoubleNode<T>(val);
		if (isempty())
		{...}
		else
		{...}
		size++;
	}
	void AddFront(T val)
	{
		DoubleNode<T>* pNode=new DoubleNode<T>(val);
		if (isempty())
		{...}
		else
		{...}
		size++;
	}
	bool AddAt(T val,int pos)
	{
		DoubleNode<T>* pNode=NULL;
		if (pos<=0 || pos>size)
		{...}
		if (pos==size)
			AddBack(size);
		else if (pos==1)
			AddFront(1);
		else
		{...}
		size++;
		return true;
	}
	bool RemoveBack()
	{
		return RemoveAt(size);
	}
	bool RemoveFront()
	{
		return RemoveAt(1);
	}
	bool RemoveAt(int pos)
	{
		DoubleNode<T>* pNode=NULL;
		if (isempty())
		{...}
		if (pos<=0 || pos>size)
		{...}
		if (size==1)
		{
			Clear();
		}
		if (pos==1)
		{...}
		else
		{
			DoubleNode<T>* pPreNode=GetPointAt(pos-1);
			pNode=pPreNode->next;
			if (pos==size)
			{...}
			else
			{...}
			delete pNode;
		}
		size--;
		return true;
	}
};
#endif


        给出类框架后,就该一个个给出对应的实现了。除了部分细节需要修改,其他的与双向链表的实现毫无二致:

        1.构造函数与析构函数:

	CircleDoubleLinkList():head(),tail(),size(0) {}
	~CircleDoubleLinkList() {Clear();}

 

        2.清空链表内所有元素:

	void Clear()
	{
		//从链表头到链表尾的方式逐个删除 
		const int nums=Size();
		if (!isempty())
		{
			for (int k=1;k<=nums;++k)
			{
				DoubleNode<T>* temp=head->next;
				delete head;
				head=temp;
				size--;
			}
		}
		//如果链表本来就为空,就没必要再进for循环了  
	}

 

        3.给出元素位置再返回对应结点:

	DoubleNode<T>* GetPointAt(int pos) 
	{
		DoubleNode<T>* pNode=NULL;
		if (pos<=0 || pos>size)
			std::cout<<"out of range."<<std::endl;   //链表当前位置越界,异常
		else
		{
			pNode=head;            //当前位置满足条件,则一开始在链表头			
			for (int i=1;i<=pos-1;++i)   
				pNode=pNode->next;
		} 
		return pNode;
	}

        要注意的是,遍历元素位置时避免pNode所获得的值越界,队首元素进不了for循环,改变不了pNode;队尾元素如果设置成i=1;i<=pos;++i,pNode会读取队尾的下一个值,那个值越界。最后就会返回一个未定义的元素。

        4.返回首尾端元素、查找元素、正序反序输出链表所有元素、返回元素个数、判断链表是否为空:

        T GetHeadVal() 
	{
		if (isempty())
		{
			std::cout<<"the link list is empty"<<std::endl;
			return NULL;
		}
		return head->element;
	} 
	T GetTailVal()
	{
		if (isempty())
		{
			std::cout<<"the link list is empty"<<std::endl;
			return NULL;
		}
		return tail->element;
	}
	int Find(T val)
	{
		int pos=1;    //从1号位,也就是链表首开始
		DoubleNode<T>* findNode=head;
		do
		{
			if (findNode->element==val)
				return pos;
			findNode=findNode->next;
			pos++;
		}
		while (findNode!=head);
		std::cout<<"we can't find it.return -1"<<std::endl;
		return -1;
	}
	void ShowAllVal()
	{
		DoubleNode<T>* findNode=head;
		do
		{
			std::cout<<findNode->element<<" ";
			findNode=findNode->next;
		}
		while (findNode!=head);
		std::cout<<"最后一个元素的下一个元素是"<<findNode->element<<std::endl; 
		std::cout<<std::endl; 
	}
	void ReverseShowAllVal()
	{
		DoubleNode<T>* findNode=tail;
		do
		{
			std::cout<<findNode->element<<" ";
			findNode=findNode->prev; 
		}
		while (findNode!=tail);
		std::cout<<"第一个元素的上一个元素是"<<findNode->element<<std::endl;
		std::cout<<std::endl;
	}
	int Size() {return size;}        //返回元素个数
	bool isempty()  {return size==0?true:false; }  //返回链表是否为空 

        由于此时,链表已成环状,所以要用一个循环进行控制,如果满足条件或者已经遍历一圈,要注意跳出,以免陷入死循环。

        5.链表中加入元素:

        这里分四种情况讨论:

                ①插入元素位置越界                               

                ②在尾部插入元素                                   

                ③在头部插入元素                           

                ④在其他位置插入元素                   

        若插入元素成功,记得元素个数+1。这里用图示例空链表插入元素,其他情况读者们可以自己试着画图:

 

         代码如下:

	void AddBack(T val)
	{
		DoubleNode<T>* pNode=new DoubleNode<T>(val);
		if (isempty())
		{
			head=pNode;
			tail=pNode;
			tail->next=head;
			head->prev=tail;
		}
		else
		{
			tail->next=pNode;
			pNode->prev=tail;
			tail=pNode;
			tail->next=head;
			head->prev=tail;
		}
		size++;
	}
	void AddFront(T val)
	{
		DoubleNode<T>* pNode=new DoubleNode<T>(val);
		if (isempty())
		{
			head=pNode;
			tail=pNode;
			tail->next=head;
			head->prev=tail;
		}
		else
		{
			head->prev=pNode;
			pNode->next=head;
			head=pNode;
			tail->next=head;
			head->prev=tail;
		}
		size++;
	}
	bool AddAt(T val,int pos)
	{
		DoubleNode<T>* pNode=NULL;
		if (pos<=0 || pos>size)
		{
			std::cout<<"out of range"<<std::endl;
			return false;
		}
		if (pos==size)
			AddBack(size);
		else if (pos==1)
			AddFront(1);
		else
		{
			DoubleNode<T>* pPreNode=GetPointAt(pos-1);
			pNode=new DoubleNode<T>(val);
			pNode->next=pPreNode->next;
			pNode->prev=pPreNode;
			pPreNode->next->prev=pNode;  //..
			pPreNode->next=pNode;
		}
		size++;
		return true;
	}

 

        6.链表中删除元素:

        这里分五种情况讨论:

                ①链表为空                                

                ②删除元素位置越界                               

                ③在尾部删除元素

                ④在头部删除元素

                ⑤在其他位置删除元素

        若删除元素成功,记得元素个数-1。这里用图示例非空链表首部删除元素,其他情况读者们可以自己试着画图:

 


        代码如下: 

	bool RemoveBack()
	{
		return RemoveAt(size);
	}
	bool RemoveFront()
	{
		return RemoveAt(1);
	}
	bool RemoveAt(int pos)
	{
		DoubleNode<T>* pNode=NULL;
		if (isempty())
		{
			std::cout<<"the link list is empty"<<std::endl;
			return false;
		}
		if (pos<=0 || pos>size)
		{
			std::cout<<"out of range"<<std::endl;
			return false;
		}
		if (size==1)
		{
			Clear();
		}
		if (pos==1)
		{
			pNode=head;
			head=head->next;
			head->prev=tail;
			tail->next=head;
			delete pNode;
		}
		else
		{
			DoubleNode<T>* pPreNode=GetPointAt(pos-1);
			pNode=pPreNode->next;
			if (pos==size)
			{
				pPreNode->next=head;
				tail=pPreNode;
				head->prev=tail;
				tail->next=head;
			}
			else
			{
				pPreNode->next=pNode->next;
				pNode->next->prev=pPreNode;
			}
			delete pNode;
		}
		size--;
		return true;
	}

 

          上述实现放入之前所说的类框架后,就形成了自制的简易双向循环链表。

          当然了,还有更加具有技术含量的双向循环链表,那就是STL中的<list>。详情可坐等后续博文更新。

 

 

 

 

参考文献:

1.《算法导论》2nd   Thomas H.Cormen , Charles E.Leiserson , Ronald L.Rivest , Cliford Stein 著,潘金贵、顾铁成、李成法、叶懋译
2.《数据结构与算法分析 C++描述》 第3版  Mark Allen Weiss著,张怀勇等译
3.《C++标准模板库 -自修教程及参考手册-》  Nicolai M.Josuttis著,侯捷/孟岩译

4.http://blog.csdn.net/mourinho_my_idol/article/details/12676549

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
双向循环链表是一种链式存储结构,每个节点包含一个指向前驱节点和后继节点的指针。在C++语言中,可以通过定义一个双向循环链表类来实现该数据结构。 以下是一个简单的双向循环链表类的实现: ```cpp class Node { public: int value; Node* prev; Node* next; Node(int v) : value(v), prev(nullptr), next(nullptr) {} }; class DoublyLinkedList { public: DoublyLinkedList() : head(nullptr), tail(nullptr), size(0) {} ~DoublyLinkedList() { while (head != nullptr) { Node* temp = head; head = head->next; delete temp; } tail = nullptr; size = 0; } bool empty() const { return size == 0; } int length() const { return size; } void push_front(int v) { Node* newNode = new Node(v); if (empty()) { head = tail = newNode; } else { newNode->next = head; head->prev = newNode; head = newNode; } tail->next = head; head->prev = tail; size++; } void push_back(int v) { if (empty()) { push_front(v); } else { Node* newNode = new Node(v); newNode->prev = tail; tail->next = newNode; tail = newNode; tail->next = head; head->prev = tail; size++; } } void pop_front() { if (size == 1) { delete head; head = tail = nullptr; size = 0; } else if (!empty()) { Node* temp = head; head = head->next; head->prev = tail; tail->next = head; delete temp; size--; } } void pop_back() { if (size == 1) { pop_front(); } else if (!empty()) { Node* temp = tail; tail = tail->prev; tail->next = head; head->prev = tail; delete temp; size--; } } private: Node* head; Node* tail; int size; }; ``` 该类包含两个私有成员变量 `head` 和 `tail`,分别指向链表的头节点和尾节点,以及一个整型变量 `size` 表示链表的长度。类的公有成员函数包括: - `empty()`:判断链表是否为空。 - `length()`:返回链表的长度。 - `push_front(int v)`:在链表头部插入一个值为 `v` 的节点。 - `push_back(int v)`:在链表尾部插入一个值为 `v` 的节点。 - `pop_front()`:删除链表头部的节点。 - `pop_back()`:删除链表尾部的节点。 注意,在插入第一个节点时,需要将 `head` 和 `tail` 都指向该节点,并且将该节点的 `next` 和 `prev` 指针都指向自身。在删除最后一个节点时,需要特别处理,即将 `head` 和 `tail` 都置为 `nullptr`。 可以使用以下代码进行测试: ```cpp #include <iostream> int main() { DoublyLinkedList list; std::cout << "Empty: " << list.empty() << std::endl; std::cout << "Length: " << list.length() << std::endl; list.push_back(1); list.push_front(2); list.push_back(3); std::cout << "Empty: " << list.empty() << std::endl; std::cout << "Length: " << list.length() << std::endl; list.pop_front(); list.pop_back(); std::cout << "Empty: " << list.empty() << std::endl; std::cout << "Length: " << list.length() << std::endl; return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值