双链表

单链表是最基本最简单的结构了,用处也蛮多的吧,尤其是后面在层序结构的各种树与图结构的时候大量使用链表而且还很多是单链表形式的。学习双向链表还是由约瑟夫问题引入的呢,在单链表的删除操作时需要用并排的两个指针同步向后移动,为避免这个问题双向链表就派上用场了。双向链表顾名思义是每个结点有两个方向了,那么在结点里面不仅仅要保存一个后向指针还要保存一个前向指针了。

  1. #pragma once
  2. #include<iostream>
  3. using namespace std;
  4. template<class T>
  5. class LinkNode //节点类
  6. {
  7. public: //全为公共的方便操作
  8. T data; //模板类型的一个数据
  9. LinkNode<T> *next; //该结点的一个指针,用于指向后继
  10. LinkNode<T> *prior;//用于指向前驱
  11. public:
  12. LinkNode(LinkNode<T> *ptr = NULL)//只初始化指针的构造函数,并使指针域为空
  13. { next = ptr; prior = ptr; }
  14. LinkNode(const T& item,LinkNode<T> *ptr = NULL)//数据与指针一同初始化的构造函数,依旧指针域默认参数为空
  15. { data = item; next = ptr; prior = ptr; }
  16. ~LinkNode(){}
  17. };
#pragma once
#include<iostream>
using namespace std;
template<class T>
class LinkNode //节点类
{
public:           //全为公共的方便操作
	T data; //模板类型的一个数据
	LinkNode<T> *next; //该结点的一个指针,用于指向后继
	LinkNode<T> *prior;//用于指向前驱
public:
	LinkNode(LinkNode<T> *ptr = NULL)//只初始化指针的构造函数,并使指针域为空
	{ next = ptr; prior = ptr; }

	LinkNode(const T& item,LinkNode<T> *ptr = NULL)//数据与指针一同初始化的构造函数,依旧指针域默认参数为空
	{ data = item; next = ptr; prior = ptr; }

	~LinkNode(){}
};

双向链表在插入的时候搭链是一个很有讲究的过程,某些步骤是有严格顺序的:

  1. newNode->prior = current; //双向链表插入的关键四步,最后一句必须最后执行
  2. newNode->next = current->next;
  3. current->next->prior = newNode;
  4. current->next = newNode;
	newNode->prior = current; //双向链表插入的关键四步,最后一句必须最后执行
	newNode->next = current->next;
	current->next->prior = newNode;
	current->next = newNode;
大致上就是先搞定新结点的prior和next指针,然后是待插位置的后一个结点的prior和前一个结点的next了。

双向链表的删除直接是先保存待删除结点指针然后覆盖就行了,其他的操作基本可以看作是单链表了。

  1. #pragma once
  2. //#include"LinearList.h"
  3. #include"LinkNode.h"
  4. #include<iostream>
  5. using namespace std;
  6. template<class T>//继承的过程中,加上此句派生类依然为类模板
  7. class List
  8. {
  9. protected:
  10. LinkNode<T>* first;//封装
  11. public:
  12. List();
  13. //List(const T& x);//感觉没用到过
  14. List(List<T>& L);
  15. ~List();
  16. void makeEmpty();//依次销毁每个结点的空间
  17. int Length() const;//搜索当前表节点数
  18. LinkNode<T>* getHead() const;//返回头指针
  19. LinkNode<T>* Search(T x) const;//搜索并返回指针
  20. LinkNode<T>* Locate(int i)const;//定位并返回指针
  21. bool GetData(int i,T& x)const;//返回第i个结点的元素值以引用的形式
  22. bool SetData(int i,T& x);//修改第i个结点的元素值
  23. bool Insert(int i,T& x);//在第i个节点后面插入新节点元素值
  24. LinkNode<T>* Remove(int i,T& x);//删除第i个结点
  25. bool isEmpty() const;//判表空
  26. void Sort(); //排序
  27. void input(T endTag);//建立表并输入
  28. void output();//输出表
  29. void operator = (List<T>& L);//复制重载
  30. };
  31. template<class T>
  32. List<T>::List()
  33. {
  34. first = new LinkNode<T>;//构造函数中开辟头结点
  35. }
  36. template<class T>
  37. List<T>::List(List<T>& L)
  38. {
  39. LinkNode<T> *srcptr = L.getHead() ->next; //获取头指针 用于遍历
  40. LinkNode<T> *destptr = first = new LinkNode<T>;//建立头结点 初始化
  41. LinkNode<T> *newNode;
  42. T value;
  43. while(srcptr != NULL)//直到最后一个结点的尾指针为空结束
  44. {
  45. value = srcptr->data;
  46. newNode = new LinkNode<T>(value);
  47. newNode->prior = destptr;//往新链表的后面插入
  48. destptr->next = newNode;
  49. srcptr = srcptr->next;//后移
  50. destptr = destptr->next;
  51. }
  52. }
  53. template<class T>
  54. List<T>::~List()
  55. {}
  56. template<class T>
  57. void List<T>::makeEmpty() //全部销毁指针资源
  58. {
  59. LinkNode<T> *current = first->next;
  60. LinkNode<T> *del;
  61. while(current != NULL)
  62. {
  63. del = current;
  64. current = current->next;
  65. delete del;
  66. }
  67. }
  68. template<class T>
  69. int List<T>::Length() const
  70. {
  71. int count = 0;
  72. LinkNode<T> *current = first;//头指针副本用于遍历
  73. while(current->next != NULL)//与单链表的遍历控制条件相同,当前结点后后继是否为空
  74. {
  75. current = current->next;
  76. count++ ;
  77. }
  78. return count;
  79. }
  80. template<class T>
  81. LinkNode<T>* List<T>::getHead() const
  82. {
  83. return first;
  84. }
  85. template<class T>
  86. LinkNode<T>* List<T>::Search(T x) const
  87. {
  88. LinkNode<T> *current = first->next;
  89. while(current != NULL)//此时的控制条件为某个结点的next知否为空
  90. {
  91. if(current->data == x)
  92. return current;//如果查询到直接函数返回,不用无用操作(如果链表中有多个x元素值,则以第一个为准返回)
  93. current = current->next;
  94. }
  95. return NULL;
  96. }
  97. template<class T>
  98. LinkNode<T>* List<T>::Locate(int i)const
  99. {
  100. if(i < 0) //定位可以是第0个,也就是头结点指针
  101. return NULL;
  102. LinkNode<T> *current = first;int k=0;
  103. while(current != NULL && k < i)//双条件控制后者其更多的作用
  104. {
  105. current = current->next;
  106. k++;
  107. }
  108. //for(int k=0 ;k < i ;k++)//遍历到第i个
  109. // current = current->next;
  110. return current;//指向第i个元素的指针
  111. }
  112. template<class T>
  113. bool List<T>::GetData(int i,T& x)const //参数中有下标值的一般先判断其下标值是否合法
  114. {
  115. if(i <= 0)
  116. return false;
  117. LinkNode<T> *current = Locate(i);
  118. if(current == NULL)
  119. return false;
  120. x = current->data;
  121. retrun true;
  122. }
  123. template<class T>
  124. bool List<T>::SetData(int i,T& x)
  125. {
  126. if(i <= 0)
  127. return false;
  128. LinkNode<T> *current = Locate(i);
  129. if(current == NULL)
  130. return false;
  131. current->data = x;
  132. return true;
  133. }
  134. template<class T>
  135. bool List<T>::Insert(int i,T& x)
  136. {
  137. if(i < 0 )//可以插入在第一个结点之前,有头结点的好处代码一致
  138. return false;
  139. LinkNode<T> *current = Locate(i);
  140. return false;
  141. LinkNode<T> *newNode = new LinkNode<T>(x);
  142. if(newNode == NULL)
  143. return false;
  144. newNode->prior = current; //双向链表插入的关键四步,顺序不能变,最后一句必须最后执行
  145. newNode->next = current->next;
  146. current->next->prior = newNode;
  147. current->next = newNode;
  148. return true;
  149. }
  150. template<class T>
  151. LinkNode<T>* List<T>::Remove(int i,T& x)
  152. {
  153. if(i <= 0)
  154. return NULL;
  155. LinkNode<T> *current = Locate(i);//和单链表不同的是,双向链表有前指针可以指向前驱,所以定位到第i个就好
  156. if(current ==NULL)
  157. return NULL;
  158. current->next->prior = current->prior;//双向链表删除操作较为简单明了
  159. current->prior->next = current->next;//重新搭链
  160. x = current->data;
  161. delete current;//销毁指针资源
  162. }
  163. template<class T>
  164. bool List<T>::isEmpty()const
  165. {
  166. return ((first->next == NULL) ?true : false);
  167. }
  168. template<class T>
  169. void List<T>::input(T endTag)
  170. {
  171. LinkNode<T> *newNode,*last = first;
  172. T value;
  173. cin>>value;
  174. while(value != endTag)
  175. {
  176. newNode = new LinkNode<T>(value);
  177. newNode->prior = last;
  178. last->next = newNode;
  179. last = newNode;
  180. cin>>value;
  181. }
  182. }
  183. template<class T>
  184. void List<T>::output()//输出
  185. {
  186. cout<<"双向链表输出如下:"<<endl;
  187. LinkNode<T> *current = first->next;
  188. int count = 0;
  189. while(current != NULL)
  190. {
  191. cout<<"#"<<count+1<<":"<<current->data<<endl;
  192. current = current->next;
  193. count++;
  194. }
  195. }
  196. template<class T>
  197. void List<T>::Sort()//最小选择 排序
  198. {
  199. LinkNode<T> *current1,*current2;//下面连续取后继指针把外层循环控制在倒数第二个结点
  200. for(current1 = first->next ; current1->next != NULL ; current1 = current1->next)
  201. {
  202. for(current2 = current1->next ; current2 != NULL ; current2 = current2->next)
  203. {
  204. if(current1->data > current2->data)
  205. {
  206. T temp;
  207. temp = current1->data;
  208. current1->data = current2->data;
  209. current2->data = temp;
  210. }
  211. }
  212. }
  213. }
  214. template<class T>
  215. void List<T>::operator= (List<T> &L)
  216. {
  217. makeEmpty();//先全部销毁指针资源
  218. LinkNode<T> *srcptr = L.getHead() ->next; //获取头指针 用于遍历
  219. LinkNode<T> *destptr = first; //= new LinkNode<T>;//不用于赋值初始化,只用于复制,不用建立新头结点
  220. LinkNode<T> *newNode;
  221. T value;
  222. while(srcptr != NULL)//直到最后一个结点的尾指针为空结束
  223. {
  224. value = srcptr->data;
  225. newNode = new LinkNode<T>(value);
  226. newNode->prior = destptr;//往新链表的后面插入
  227. destptr->next = newNode;
  228. srcptr = srcptr->next;//后移
  229. destptr = destptr->next;
  230. }
  231. }
#pragma once
//#include"LinearList.h"
#include"LinkNode.h"
#include<iostream>
using namespace std;
template<class T>//继承的过程中,加上此句派生类依然为类模板
class List
{
protected:
	LinkNode<T>* first;//封装
public:
	List();
	//List(const T& x);//感觉没用到过
	List(List<T>& L);
	~List();
	void makeEmpty();//依次销毁每个结点的空间
	 
	int Length() const;//搜索当前表节点数
	LinkNode<T>* getHead() const;//返回头指针
    LinkNode<T>* Search(T x) const;//搜索并返回指针
	LinkNode<T>* Locate(int i) const;//定位并返回指针

	bool GetData(int i,T& x) const;//返回第i个结点的元素值以引用的形式
	bool SetData(int i,T& x);//修改第i个结点的元素值
	bool Insert(int i,T& x);//在第i个节点后面插入新节点元素值
	LinkNode<T>* Remove(int i,T& x);//删除第i个结点

	bool isEmpty() const;//判表空

	void Sort(); //排序
	void input(T endTag);//建立表并输入
	void output();//输出表
	void operator = (List<T>& L);//复制重载
};

template<class T>
List<T>::List()
{
	first = new LinkNode<T>;//构造函数中开辟头结点
}

template<class T>
List<T>::List(List<T>& L)
{
	LinkNode<T> *srcptr = L.getHead() ->next; //获取头指针 用于遍历
	LinkNode<T> *destptr = first = new LinkNode<T>;//建立头结点 初始化
	LinkNode<T> *newNode;
	T value;
	while(srcptr != NULL)//直到最后一个结点的尾指针为空结束
	{
		value = srcptr->data;
		newNode = new LinkNode<T>(value);

		newNode->prior = destptr;//往新链表的后面插入
		destptr->next = newNode;

		srcptr = srcptr->next;//后移
		destptr = destptr->next;
	}
}
template<class T>
List<T>::~List()
{}

template<class T>
void List<T>::makeEmpty() //全部销毁指针资源
{
	LinkNode<T> *current = first->next;
	LinkNode<T> *del;
	while(current != NULL)
	{
		del = current;
		current = current->next;
		delete del;
	}
}

template<class T>
int List<T>::Length() const
{
	int count = 0;
	LinkNode<T> *current = first;//头指针副本用于遍历
	while(current->next != NULL)//与单链表的遍历控制条件相同,当前结点后后继是否为空
	{
		current = current->next;
		count++ ;
	}
	return count;
}

template<class T>
LinkNode<T>* List<T>::getHead() const
{
	return first;
}
template<class T>
LinkNode<T>* List<T>::Search(T x) const
{
	LinkNode<T> *current = first->next;
	while(current != NULL)//此时的控制条件为某个结点的next知否为空
	{
		if(current->data == x)
			return current;//如果查询到直接函数返回,不用无用操作(如果链表中有多个x元素值,则以第一个为准返回)
		current = current->next;
	}
	return NULL;
}
template<class T>
LinkNode<T>* List<T>::Locate(int i) const
{
	if(i < 0)     //定位可以是第0个,也就是头结点指针
		return NULL;
	LinkNode<T> *current = first;int k=0;
	while(current != NULL && k < i)//双条件控制后者其更多的作用
	{
		current = current->next;
		k++;
	}
	//for(int k=0 ;k < i ;k++)//遍历到第i个
	//	current = current->next;

	return current;//指向第i个元素的指针
}
template<class T>
bool List<T>::GetData(int i,T& x) const   //参数中有下标值的一般先判断其下标值是否合法
{
	if(i <= 0)
		return false;
	LinkNode<T> *current = Locate(i);
	if(current == NULL)
		return false;
	x = current->data;
	retrun true;
}

template<class T>
bool List<T>::SetData(int i,T& x)
{
	if(i <= 0)
		return false;
	LinkNode<T> *current = Locate(i);
	if(current == NULL)
		return false;
	current->data = x;
	return true;
}
template<class T>
bool List<T>::Insert(int i,T& x)
{
	if(i < 0 )//可以插入在第一个结点之前,有头结点的好处代码一致
		return false;
	LinkNode<T> *current = Locate(i);
	return false;
	LinkNode<T> *newNode = new LinkNode<T>(x);
	if(newNode == NULL)
		return false;

	newNode->prior = current; //双向链表插入的关键四步,顺序不能变,最后一句必须最后执行
	newNode->next = current->next;
	current->next->prior = newNode;
	current->next = newNode;

	return true;
}
template<class T>
LinkNode<T>* List<T>::Remove(int i,T& x)
{
	if(i <= 0)
		return NULL;
	LinkNode<T> *current = Locate(i);//和单链表不同的是,双向链表有前指针可以指向前驱,所以定位到第i个就好
	if(current ==NULL)
		return NULL;
	current->next->prior = current->prior;//双向链表删除操作较为简单明了
	current->prior->next = current->next;//重新搭链
	x = current->data;

	delete current;//销毁指针资源
}

template<class T>
bool List<T>::isEmpty() const
{
	return ((first->next == NULL) ? true : false);
}

template<class T>
void List<T>::input(T endTag)
{
	LinkNode<T> *newNode,*last = first;
	T value;
	cin>>value;
	while(value != endTag)
	{
		newNode = new LinkNode<T>(value);

		newNode->prior = last;
		last->next = newNode;

		last = newNode;
		cin>>value;
	}
}

template<class T>
void List<T>::output()//输出
{
	cout<<"双向链表输出如下:"<<endl;
	LinkNode<T> *current = first->next;
	int count = 0;
	while(current != NULL)
	{
		cout<<"#"<<count+1<<":"<<current->data<<endl;
		current = current->next;
		count++;
	}
}
template<class T>
void List<T>::Sort()//最小选择 排序
{
	LinkNode<T> *current1,*current2;//下面连续取后继指针把外层循环控制在倒数第二个结点
	for(current1 = first->next ; current1->next != NULL ; current1 = current1->next)
	{
		for(current2 = current1->next ; current2 != NULL ; current2 = current2->next)
		{
			if(current1->data > current2->data)
			{
				T temp;
				temp = current1->data;
				current1->data = current2->data;
				current2->data = temp;
			}
		}
	}
}
template<class T>
void List<T>::operator= (List<T> &L)
{
	makeEmpty();//先全部销毁指针资源

	LinkNode<T> *srcptr = L.getHead() ->next; //获取头指针 用于遍历
	LinkNode<T> *destptr = first;   //= new LinkNode<T>;//不用于赋值初始化,只用于复制,不用建立新头结点
	LinkNode<T> *newNode;
	T value;
	while(srcptr != NULL)//直到最后一个结点的尾指针为空结束
	{
		value = srcptr->data;
		newNode = new LinkNode<T>(value);

		newNode->prior = destptr;//往新链表的后面插入
		destptr->next = newNode;

		srcptr = srcptr->next;//后移
		destptr = destptr->next;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值