zoukankan      html  css  js  c++  java
  • C++实现双向循环链表

    本次博文是关于利用C++模板的方式实现的双向循环链表以及双向循环链表的基本操作,在之前的博文C++语言实现双向链表中,已经给大家分析了双向循环链表的结构,并以图示的方式给大家解释了双向循环链表的基本操作。本篇文章利用C++实现了双向循环链表的基本操作,其中包括:

    双向循环链表 实现的功能
    头部插入结点建立链表 尾部插入结点建立链表
    实现指定位置插入结点 查找给定数值是否存在
    删除指定位置的结点 修改指定位置的结点
    双向链表的长度 打印双向链表

    定义双向链表的结点

    双向循环链表的结点由三部分构成,用于指向当前节点的直接前驱节点指针域,用于存储数据元素数据域 ,以及用于指向当前节点的直接后继节点指针域

    在之前的C++语言实现双向链表时已经给大家解释了封装的结点的特点,不需要作太大的改变,我们需要封装一个结点类,定义了结点的三个要素,并利用构造函数实现初始化,另外,考虑到在双向循环链表中要用到结点类,所以将双向链表类定义为结点的友元类。

    template<class T> 
    class doubleCircularLinkedList;//声明一下双向循环链表,以免定义友元时报错
    template <class T>
    class doubleCircularLinkedListNode
    {
    	private:
    		doubleCircularLinkedListNode<T> *prior;//双向结点前驱指针指向该结点的前驱结点
    		T data;//储存结点数据
    		doubleCircularLinkedListNode<T> *next;//双向结点的后驱指针指向该结点的后继结点
    		//将双向循环链表类定义为结点的友元类
    		friend  class doubleCircularLinkedList<T>;
    	public:
    		//结点的无参构造函数,将结点指针域初始化为NULL
    		doubleCircularLinkedListNode()
    		{
    			prior = NULL;
    			next = NULL;
    		}
    		//结点的有参构造函数,初始化指针域和数据域
    		doubleCircularLinkedListNode(T _data,doubleCircularLinkedListNode<T> *_prior = NULL,doubleCircularLinkedListNode<T> *_next = NULL)
    		{
    			prior = _prior;//初始化前驱指针
    			data = _data;//初始化数据域
    			next = _next;//初始化后继指针
    		}
    		~doubleCircularLinkedListNode()
    		{
    			prior = NULL;
    			next = NULL;
    		}
    };
    

    双向链表的基本操作

    本次实现的操作跟双向链表实现的操作基本一样,实现了双向循环链表头部插入结点, 尾部插入结点,指定位置插入结点建立链表, 查找给定数值的指定位置,删除指定位置的结点,修改指定位置的结点,双向循环链表的长度,打印双向循环链表,接下来逐一进行讲解实现:

    头部插入结点建立链表

    实现双向循环链表的头部插入结点,之前的双向链表因为在头部和尾部的指针都是指向NULL的,所以需要分情况来处理,然而双向循环链表没有元素时这两个指针都是指向自身的,因此并不需要分情况处理,都需要修改四个指针。
    因此,头部插入结点实现如下:

    template<class T>
    bool doubleCircularLinkedList<T>::insertNodeByhead(T item)
    {
    	//创建一个新的结点
    	doubleCircularLinkedListNode<T>* newNode = new doubleCircularLinkedListNode<T>(item);
    	if (newNode == NULL){
    		cout << "内存分配失败,新结点无法创建" << endl;
    		return false;
    	}
    	else{
    		newNode->prior = headNode;
    		newNode->next = headNode->next;
    		headNode->next->prior=newNode;
    		headNode->next = newNode;
    		return true;
    	}
    }
    
    

    尾部插入结点建立链表

    在尾部插入结点,当然第一步需要找到最后一个结点,然后在其后进行插入,调整四个指针即可。

    template<class T>
    bool doubleCircularLinkedList<T>::insertNodeBytail(T item)
    {
    	//创建一个新的结点
    	doubleCircularLinkedListNode<T>* newNode = new doubleCircularLinkedListNode<T>(item);
    	if (newNode == NULL){
    		cout << "内存分配失败,新结点无法创建" << endl;
    		return false;
    	}
    	//首先找到最后一个结点
    	doubleCircularLinkedListNode<T>* lastNode = headNode;
    	while(lastNode->next != headNode)
    	{
    		lastNode = lastNode->next;//没找到就一直循环
    	}
    	//找到之后调整四个指针
    	headNode->prior = newNode;
    	newNode->next = headNode;
    	lastNode->next = newNode;
    	newNode->prior = lastNode;
    	return true;
    }
    
    

    实现指定位置插入结点

    在指定位置插入只需要两步走,首先也是找到指定的位置,然后就是插入新结点的指针的调整,中间插入是最复杂的,都需要调整四个指针,最后让新结点与前继结点建立关系,实现新结点的插入。

    template<class T>
    bool doubleCircularLinkedList<T>::insertNode(T item,int n)
    {
    	if(n<1){
    		cout<<"输入的非有效位置!"<<endl;
    		return false;
    	}
    	doubleCircularLinkedListNode<T>* pMove = headNode;//创建一个新的指针,设置为游标指针
    	//首先找到插入位置
    	for(int i=1;i<n;i++)
    	{
    		pMove = pMove->next;
    		if(pMove == NULL&& i<=n)
    		{
    			cout<<"插入位置无效!"<<endl;
    			return false;
    		}
    	}
    	//创建一个新的结点
    	doubleCircularLinkedListNode<T>* newNode = new doubleCircularLinkedListNode<T>(item);
    	if (newNode == NULL){
    		cout << "内存分配失败,新结点无法创建" << endl;
    		return false;
    	}
    	//插入新的结点
    	newNode->next = pMove->next;   
    	if (pMove->next != headNode)
    	{
    		pMove->next->prior = newNode;
    	}
    	newNode->prior = pMove;
    	pMove->next = newNode;
    	return true;
    }
    
    

    查找给定数值是否存在

    查找给定元素,也就是一个遍历双向循环链表的过程,从头结点的下一个结点开始遍历,毕竟第一个头结点是没有储存数据项的。

    template<class T>
    bool doubleCircularLinkedList<T>::findData(T item)
    {
    	doubleCircularLinkedListNode<T> *pMove = headNode->next; //设置游标指针
    	doubleCircularLinkedListNode<T> *pMoveprior = headNode;//指定结点前一个结点的指针
    	//找到指定位置
    	while(pMove->data != item)
    	{
    		pMoveprior = pMove;
    		pMove = pMoveprior->next;
    		//如果没有找到特殊处理
    		if(pMove == headNode)
    		{
    			return false;
    		}
    	}
    	return true;
    }
    

    删除指定位置的结点

    删除指定的结点,第一步查找到删除的结点,需要定义一个删除指针临时指向将要删除的结点,最后指针处理删除之后别忘了释放该结点空间。

    template<class T>
    bool doubleCircularLinkedList<T>::deleteData(int n)
    {
    	if (n<1||n>getLength())
    	{
    		cout << "输入非有效位置" << endl;
    		return false;
    	}
    	doubleCircularLinkedListNode<T> * pMove = headNode;//设置游标指针
    	doubleCircularLinkedListNode<T> * pDelete;             
    	//查找删除结点的位置
    	for (int i = 1; i <= n; i++)
    	{
    		pMove = pMove->next;                                         //游标指针后移
    	}
    	//删除结点
    	pDelete = pMove;      
    	pMove->prior->next = pDelete->next;
    	pMove->next->prior = pDelete->prior;
    	delete pDelete;//释放空间
    	return true;
    }
    
    

    修改指定位置的结点

    修改指定位置的结点数据,当然还是得找到指定位置,然后对其进行修改,修改之后将原来的数据以引用的形式返回。

    template<class T>
    bool doubleCircularLinkedList<T>::changeListElements(int n,T item,T &x)
    {
    	if (n<1||n>getLength())
    	{
    		cout << "输入非有效位置" << endl;
    		return false;
    	}
    	doubleCircularLinkedListNode<T> *pMove = headNode->next;  //设置游标指针
    	for(int i=1;i<n;i++)//找到指定位置1
    	{
    		pMove = pMove->next;
    	}
    	x = pMove->data;
    	pMove->data = item;
    	return true;
    }
    
    

    双向循环链表的长度

    计算双向链表的长度的函数,在双向链表的私有成员封装了一个变量length,以此来记录双向链表的长度,遍历双向链表,逐一进行计算结点数就是双向链表的长度。

    template<class T>
    int doubleCircularLinkedList<T>::getLength()
    {
    	doubleCircularLinkedListNode<T> *pMove = headNode->next;  //设置游标指针
    	int length=0;
    	//遍历链表,计算结点数
    	while(pMove != headNode)
    	{
    		pMove = pMove->next;  //游标指针后移
    		length++;       //计算length
    	}
    	return length;
    }
    
    

    打印双向循环链表

    template<class T>
    void doubleCircularLinkedList<T>::printLinkedlist()
    {
    	//从第二个结点开始打印,表头不含数据
    	doubleCircularLinkedListNode<T>* pMove = headNode->next;
    	while(pMove !=headNode)//如果pMove->next != headNode这样写,最后一个结点是不会打印的
    	{
    		cout<<pMove->data<<" ";
    		pMove = pMove->next;//移动指针
    	}
    	cout<<endl;
    }
    
    

    以上就是我简要的给大家分享的C++实现双向循环链表,因为实现了双向链表,所以基本上实现思路差不多,唯一的不同就是在循环一词不同,这一不同就是头结点的前驱指针和尾结点的后驱指针指向不同,要是还是不太清楚的可以去那篇博客看看。本次的完整代码已经全部上传到github! (C++实现双向循环链表),还想了解其他的数据结构实现的可以去我的博客,我们一起讨论啊,一起进步!

  • 相关阅读:
    JS数组方法汇总 array
    jQuery性能优化
    js禁止保存网页的一些代码
    XML LINQ简介
    一个滑动条的DIV+CSS+JS实例
    JS的正则表达式
    jQuery中$.ajax的实现
    MSSQL 2005删除所有表的语句[转]
    Jquery Toggle with Images
    SQLIte default date CURRENT_TIMESTAMP
  • 原文地址:https://www.cnblogs.com/xiangjunhong/p/12482456.html
Copyright © 2011-2022 走看看