c++封装模板单链表

基于C++ 模板类的模板链表(单链表)

首先定义节点类型

  • 具有一个data数据 next指针 以及一个像类一样的构造函数,用于方便的创建节点类型
template <class _Ty>
struct Node
{
	_Ty			data;
	Node<_Ty>* next;
	Node<_Ty>(const _Ty& data) :data(data)
	{
		next = nullptr;
	}
};

创建链表类

  • 为方便操作创建两个指针,分别指向链表首元节点末尾节点

  • 并且记录链表节点的数量 方便操作

    private:
    	Node<_T>* pFront;	//指向头节点的指针
    	Node<_T>* pEnd;		//指向尾节点的指针
    	int		cursize;	//链表节点个数
    
  • 模板类要具有类的操作

    1. 构造函数

      用于简单的初始化数据,这样就不必再写一个初始化函数了

      //构造函数  数据初始化
      My_List()
      	{
      		pFront = pEnd = nullptr;
      		cursize = 0;
      	}
      
    2. 析构函数

      用于在最后释放链表,删除节点,而且自动调用

      ~My_List();
      
      template<class _T>
      inline My_List<_T>::~My_List()
      {
      	/*
      	析构函数  释放链表
      	*/
      	Node<_T>* pTemp = nullptr;
      	while (pFront)
      	{
      		pTemp = pFront->next;
      		delete pFront;
      		pFront = pTemp;
      	}
      }
      
  • 链表的基本操作

    • 创建节点

      ​ 创建节点,注意此处可以直接调用struct定义的构造函数来创建新的节点

      template<class _T>
      inline Node<_T>* My_List<_T>::Create_Node(const _T& New_data)
      {
          //创建节点
      	Node<_T>* pNew = new Node<_T>(New_data);
      	if (pNew == nullptr)
      	{
      		//节点创建失败
      		return nullptr;
      	}
      	pNew->next = nullptr;
      	return pNew;
      }
      
      
    • 链表的尾插法

      ​ pEnd的next指向pNew,pNew更新为pEnd
      在这里插入图片描述

      template<class _T>
      inline void My_List<_T>::push_back(const _T& Insert_data)
      {
          //调用创建节点的函数
      	Node<_T>* pNew =Create_Node(Insert_data);
      	if (pNew == nullptr)
      	{
      		cout << "节点开辟失败!\n";
      		return;
      	}
      
      	if (isempty())
      	{
              //如果为空,直接插入
      		pFront = pEnd = pNew;
      	}
      	else
      	{
              //如果不为空,尾指针的next指向新节点,
              //更新尾指针位置
      		pEnd->next = pNew;
      		pEnd = pNew;
      	}
      	cursize++;
      	return;
      }
      
    • 链表的头插法

      ​ 新节点的next指针指向pFront,pNew更新为pFront
      在这里插入图片描述

      //链表头插
      template<class _T>
      inline void My_List<_T>::push_head(const _T& Insert_data)
      {
      	Node<_T>* pNew = Create_Node(Insert_data);
      	if (pNew == nullptr)
      	{
      		cout << "节点开辟失败!\n";
      		return;
      	}
      
      	if (isempty())
      	{
      		pFront = pEnd = pNew;
      	}
      	else
      	{
      		pNew->next = pFront;
      		pFront = pNew;
      	}
      	cursize++;
      	return;
      }
      
    • 在任意位置插入链表

      ​ 在链表不为空的情况下,如果输入的pos位置不合理,则自动插入到较合理的地方

      1. 如果插入的地方是1,则需要更新头节点,原理同头插法
      2. 如果插入的是任意合理的地方,则需要到达对应位置节点的前一个节点,则让pNew的next指向pTemp的next,pTemp的next指向pNew,实现节点的连接
        在这里插入图片描述
      //往pos位置插入节点
      template<class _T>
      inline void My_List<_T>::Insert_Node(const _T& Insert_data,int pos)
      {
      	if (isempty())
      	{
      		cout << "链表为空!\n" << endl;
      		return;
      	}
      
      	if (pos <= 0)
      	{
      		cout << "插入位置无效,执行头插操作\n";
      		push_head(Insert_data);
      	}
      	else if (pos > cursize)
      	{
      		cout << "插入位置无效,执行尾插操作\n";
      		push_back(Insert_data);
      	}
      	else
      	{
      	Node<_T>* pNew=Create_Node(Insert_data);
      		Node<_T>* pTemp = pFront;
      		//插入第一个位置
      		if (pos == 1)
      		{
      			pNew->next = pFront;
      			pFront = pNew;
      		}
      		else
      		{
      			//找到待插入的前一个位置
      			for (int i = 0; i < pos - 2; i++)
      			{
      				pTemp = pTemp->next;
      			}
      			pNew->next = pTemp->next;
      			pTemp->next = pNew;	
      		}
      		cursize++;
      	}
      	return;
      }
      
      
    • 链表节点的查找

      ​ 查找链表节点,常用作删除节点的前置操作,传入要查找的节点的数据,依次遍历链表,当某个节点的data等于FindData时,返回这个pFind指针,没有找到则返回nullptr

      template<class _T>
      inline Node<_T>* My_List<_T>::Find_Node(const _T& Find_data)
      {
      	Node<_T>* pFind = pFront;
      	while (pFind)
      	{
      		if (pFind->data == Find_data)
      		{
      			return pFind;
      		}
      		pFind = pFind->next;
      	}
      	return nullptr;
      }
      
    • 链表节点的删除

      ​ 利用查找函数查找节点,定义临时pTemp,让其到达待删除节点的前一个节点,pTemp的next指向pDel的next ,从而实现跳过pDel ,接着delete pDel 即可做到删除节点

    在这里插入图片描述

    template<class _T>
    inline void My_List<_T>::Delete_Node(const _T& Delete_data)
    {
    	if (isempty())
    	{
    		cout << "链表为空\n";
    		return;
    	}
    	
    	Node<_T>* pDel = Find_Node(Delete_data);
    	if (pDel == nullptr)
    	{
    		cout << "未查找到要删除的节点!\n";
    	}
    	else
    	{
    		Node<_T>* pTemp = pFront;
    		while (pTemp->next!=pDel)
    		{
    			pTemp = pTemp->next;
    		}
    		pTemp->next = pDel->next;
    		delete pDel;
    		cursize--;
    	}
    	return;
    }
    
    • 遍历整个链表

      ​ 遍历没啥好说的

      template<class _T>
      inline void My_List<_T>::Travel_Node() const
      {
      	Node<_T>* pTemp = pFront;
      	while (pTemp)
      	{
      		cout << pTemp->data << " ";
      		pTemp = pTemp->next;
      	}
      	cout << '\n';
      }
      
    • 其他操作 (万金油)

      ​ 链表的操作基本已经结束,以下为访问链表的其他操作

      	//显示链表的节点个数
      	int size()const;
      	//判断链表是否为空
      	bool isempty()const;
      	//返回链表的头节点的值
      	_T front()const;
      	//返回链表的尾节点的值
      	_T back()const;
      
      	template<class _T>
      inline int My_List<_T>::size() const
      {
      	return cursize;
      }
      
      template<class _T>
      inline bool My_List<_T>::isempty() const
      {
      	return cursize == 0;
      }
      
      template<class _T>
      inline _T My_List<_T>::front() const
      {
      	return pFront->data;
      }
      
      template<class _T>
      inline _T My_List<_T>::back() const
      {
      	return pEnd->data;
      }
      
      

测试模板链表

​ 模板链表适用于其他任意类型 包括int string double 甚至是我们定义的类 ,只要其定义了合适的重载运算符,如<<

在这里插入图片描述


#ifndef MY_LISTL_H_
#define MY_LIST_H_

#include <iostream>
using namespace std;

template <class _Ty>
struct Node
{
	_Ty			data;
	Node<_Ty>* next;
	Node<_Ty>(const _Ty& data) :data(data)
	{
		next = nullptr;
	}
};

template <class _T>
class My_List
{
private:
	Node<_T>* pFront;
	Node<_T>* pEnd;
	int		cursize;	//链表节点个数
public:
	My_List()
	{
		pFront = pEnd = nullptr;
		cursize = 0;
	}
	~My_List();
	//创建节点
	Node<_T>* Create_Node(const _T& New_data);
	//链表尾插法
	void push_back(const _T& Insert_data);
	//链表头插法
	void push_head(const _T& Insert_data);
	//插入节点
	void Insert_Node(const _T& Insert_data,int pos);
	//链表节点的查找
	Node<_T>* Find_Node(const _T& Find_data);
	//链表的删除节点
	void Delete_Node(const _T& Delete_data);
	//遍历链表
	void Travel_Node()const;
	//显示链表的节点个数
	int size()const;
	//判断链表是否为空
	bool isempty()const;
	//返回链表的头节点的值
	_T front()const;
	//返回链表的尾节点的值
	_T back()const;
};

#endif

template<class _T>
inline My_List<_T>::~My_List()
{
	/*
	析构函数  释放链表
	*/
	Node<_T>* pTemp = nullptr;
	while (pFront)
	{
		pTemp = pFront->next;
		delete pFront;
		pFront = pTemp;
	}
}

template<class _T>
inline Node<_T>* My_List<_T>::Create_Node(const _T& New_data)
{
	Node<_T>* pNew = new Node<_T>(New_data);
	if (pNew == nullptr)
	{
		//节点创建失败
		return nullptr;
	}
	pNew->next = nullptr;
	return pNew;
}

//链表尾插
template<class _T>
inline void My_List<_T>::push_back(const _T& Insert_data)
{
	Node<_T>* pNew = Create_Node(Insert_data);
	if (pNew == nullptr)
	{
		cout << "节点开辟失败!\n";
		return;
	}

	if (isempty())
	{
		pFront = pEnd = pNew;
	}
	else
	{
		pEnd->next = pNew;
		pEnd = pNew;
	}
	cursize++;
	return;
}

//链表头插
template<class _T>
inline void My_List<_T>::push_head(const _T& Insert_data)
{
	Node<_T>* pNew = Create_Node(Insert_data);
	if (pNew == nullptr)
	{
		cout << "节点开辟失败!\n";
		return;
	}

	if (isempty())
	{
		pFront = pEnd = pNew;
	}
	else
	{
		pNew->next = pFront;
		pFront = pNew;
	}
	cursize++;
	return;
}

//往pos位置插入节点
template<class _T>
inline void My_List<_T>::Insert_Node(const _T& Insert_data,int pos)
{
	if (isempty())
	{
		cout << "链表为空!\n" << endl;
		return;
	}

	if (pos <= 0)
	{
		cout << "插入位置无效,执行头插操作\n";
		push_head(Insert_data);
	}
	else if (pos > cursize)
	{
		cout << "插入位置无效,执行尾插操作\n";
		push_back(Insert_data);
	}
	else
	{
		Node<_T>* pNew = Create_Node(Insert_data);
		Node<_T>* pTemp = pFront;
		//插入第一个位置
		if (pos == 1)
		{
			pNew->next = pFront;
			pFront = pNew;
		}
		else
		{
			//找到待插入的前一个位置
			for (int i = 0; i < pos - 2; i++)
			{
				pTemp = pTemp->next;
			}
			pNew->next = pTemp->next;
			pTemp->next = pNew;	
		}
		cursize++;
	}
	return;
}


template<class _T>
inline Node<_T>* My_List<_T>::Find_Node(const _T& Find_data)
{
	Node<_T>* pFind = pFront;
	while (pFind)
	{
		if (pFind->data == Find_data)
		{
			return pFind;
		}
		pFind = pFind->next;
	}
	return nullptr;
}

template<class _T>
inline void My_List<_T>::Delete_Node(const _T& Delete_data)
{
	if (isempty())
	{
		cout << "链表为空\n";
		return;
	}
	
	Node<_T>* pDel = Find_Node(Delete_data);
	if (pDel == nullptr)
	{
		cout << "未查找到要删除的节点!\n";
	}
	else
	{
		Node<_T>* pTemp = pFront;
		while (pTemp->next!=pDel)
		{
			pTemp = pTemp->next;
		}
		pTemp->next = pDel->next;
		delete pDel;
		cursize--;
	}
	return;
}

template<class _T>
inline void My_List<_T>::Travel_Node() const
{
	Node<_T>* pTemp = pFront;
	while (pTemp)
	{
		cout << pTemp->data << " ";
		pTemp = pTemp->next;
	}
	cout << '\n';
}

template<class _T>
inline int My_List<_T>::size() const
{
	return cursize;
}

template<class _T>
inline bool My_List<_T>::isempty() const
{
	return cursize == 0;
}

template<class _T>
inline _T My_List<_T>::front() const
{
	return pFront->data;
}

template<class _T>
inline _T My_List<_T>::back() const
{
	return pEnd->data;
}


#include "My_List.h"
int main()
{
#if 1
	My_List<int> a;
	for (int i = 0; i < 5; i++)
	{
		a.push_back(i);
	}
	for (int i = 5; i < 10; i++)
	{
		a.push_head(i);
	}
	a.Travel_Node();

	a.Delete_Node(99);
	a.Travel_Node();
	a.Insert_Node(99, 1);
	cout << a.front() << endl;
	a.Travel_Node();
    
	return 0;
}
posted @ 2022-07-31 19:59  hugeYlh  阅读(23)  评论(0编辑  收藏  举报  来源