Cpp列表模板类

本文深入探讨了链表这种数据结构,包括其定义、节点的概念、节点的作用以及如何实现链表。链表由一系列节点组成,每个节点包含数据和指向前后节点的指针。文章详细介绍了链表的创建、插入、删除、查找、遍历和排序等功能,并提供了具体的C++代码实现,包括选择排序和归并排序。此外,还讨论了剔除链表中重复元素的方法。
摘要由CSDN通过智能技术生成

目录

目录

列表

节点(node)

节点是什么?

节点用来干什么?

节点类代码实现

链表:

创建列表的目的?

构造函数与析构函数 

无参构造函数

 有参构造函数

析构函数

 对外接口

size()

  返回首、末节点的物理位置

 将e作为首末节点插入 

 将e作为p的后继插入

 将e作为p的后继插入

remove(p)删除节点

 sort()排序

归并排序法

 

查找某一元素

 

遍历

剔除重复元素

完整代码如下

 

总结

      链表实质上就是链接在堆区开辟的一片数据结构。 只要明确的知道每一个节点的前驱的后继,那么链表的功能实现起来就十分容易了。



列表

    列表是由具有线性逻辑次序的一组元素构成的集合:

     L={ a0 ,a1,a2. ............an-1};

  列表是链表结构的一般推广,元素称为节点(node),分别由特定的位置链接指代。

       我们知道向量可以通过下标直接访问元素,此所谓“寻秩访问”(call-by-rank),而列表是通过元素的物理地址访问的,列表元素访问方式可称为“寻址访问”(call-by-position)。

节点(node)

节点是什么?

在定义节点类模板之前我们首先得知道节点是什么:

节点是内存中一片由用户分配的空间,这个节点只有物理地址,没有其他的名称供我们访问,因此我们在new一个节点的时候,用一个指针去维护他;

#define ListNodePosi(T) ListNode<T>*
ListNodePosi(T)x = new ListNode(e, this, succ);//(参数列表是拷贝构造函数)

x代表new出来的节点;

节点用来干什么?

我们很清楚我们创建一个节点的目的是去存放数据的,除了存放数据,还要存放什么呢?很明显,作为一种线性的数据结构,我们还应该在节点中存放前一个节点和后一个节点的地址,这样我们就很好的将他们链接起来了。

节点类代码实现

#pragma once
typedef int Rank;//秩
#define ListNodePosi(T) ListNode<T>*
	template<class T>
	class ListNode
	{
		public:
		T data;//节点所存储的数据
		ListNodePosi(T) pred;//当前节点的前一个节点地址
		ListNodePosi(T) succ;//当前节点的后一个节点地址
	public:
		ListNode();//构造函数
		ListNode(T e, ListNodePosi(T)p = nullptr, ListNodePosi(T) s = nullptr);//有参构造
	public:
		ListNodePosi(T) insertAsPred(T const& e);//在当前节点的前面插入一个数据,并返回新节点的地址
		ListNodePosi(T) insertAsSucc(T const& e);//在当前节点的后面插入一个数据,并返回新节点的地址
	};

template<class T>
	inline ListNode<T>::ListNode() { data = 0; pred = nullptr; succ = nullptr; }

	template<class T>
	inline ListNode<T>::ListNode(T e, ListNodePosi(T) p, ListNodePosi(T) s) {
		this->data = e;
		this->pred = p;
		this->succ = s;
	}

	template<class T>
	inline ListNodePosi(T) ListNode<T>::insertAsPred(T const& e) {
		ListNodePosi(T)x = new ListNode(e, pred, this);
		pred->succ = x;
		pred = x;
		return x;
	}

	template<class T>
	inline ListNodePosi(T) ListNode<T>::insertAsSucc(T const& e) {
		ListNodePosi(T)x = new ListNode(e, this, succ);
		succ->pred = x;
		succ = x;
		return x;
	}

链表:

由一系列节点按照一定的顺序连接所构成的一整条链。

图示:

创建列表的目的?

我们做一件事要有他的目的性,那我们创建列表有哪些目的呢,简单说就是列表支持的功能。

操作接口:

  • size()报告列表当前规模(节点总数)
  • first()   last()  返回首、末节点的物理位置
  • insertAsFirst(T const&e)insertAsLast(T const&e) 将e作为首末节点插入
  • insert(p,e)将e作为p的后继插入
  • insert(e,p)将e作为p的前驱插入
  • remove(p)删除节点
  • sort()排序
  • find(e)查找某一元素地址
  • teaverse()遍历
  • deduolicate()剔除重复元素

我们对外目前提供了10个接口函数,用户可以根据具体需求去调用这些函数。

具体模板如下

template<class T>
	class List
	{
	private:
		int _size;//链表规模
		ListNodePosi(T) header;//头哨兵
		ListNodePosi(T) trailer;//尾哨兵

	protected:
		void init();//初始化函数
		void copyNodes(ListNodePosi(T) p, int n);//拷贝函数
		int clear();//列表清空方法
		bool isMax(ListNodePosi(T)p1, ListNodePosi(T)p2);//判断节点数据大小
		ListNodePosi(T) selectMax(ListNodePosi(T)p, int n);//选取数据最大的节点
		ListNodePosi(T) selectMin(ListNodePosi(T)p, int n);//选取数据最小的节点
		void selectSort(ListNodePosi(T)p, int n,bool choice=true);//选择排序
		void _merge(ListNodePosi(T)& p, int n, List<T>& L, ListNodePosi(T)q, int m);//归并
		void mergeSort(ListNodePosi(T)& p, int n);//归并排序
		
	public:
		List() { init(); }//默认构造器,即只创建一个含有头哨兵和尾哨兵的空链表
		List(List<T>const& L) { copyNodes(L.first(), L._size); }//复制传入构造器的整个链表
		List(List<T>const& L, int r, int n);//复制L的某一区间
		~List();//列表析构器
	public:
		Rank size() { return this->_size; }
		ListNodePosi(T) insertAsFirst(T const& e);//头节点
		ListNodePosi(T) insertAsLast(T const& e);//尾节点
		ListNodePosi(T) insertAfter(ListNodePosi(T) p, T const& e);//e作为p的后继插入
		ListNodePosi(T) insertBefore(ListNodePosi(T)p, T const& e);//e作为p的前驱插入
		ListNodePosi(T) first()const { return this->header->succ; }//返回首节点物理位置
		ListNodePosi(T) last()const { return this->trailer->pred; }//返回尾节点物理位置
		T remove(ListNodePosi(T)p);//删除节点
		ListNodePosi(T) find(T& const e, int n, ListNodePosi(T)p)const;//查找数据所在的节点
		ListNodePosi(T) find(T const& e)const;//查找数据所在的节点
		int dedeuplicate();//提出列表中的重复元素;
		ListNodePosi(T) search(T const& e, int n,ListNodePosi(T) p)const;//排序完成后的查找
		void traverse(void(*visit)(T&));//遍历元素
		void Sort(ListNodePosi(T)p, int n);//随机选取排序方法:归并、选择
		int dedeuplicateProMax();
	private:
		T& operator[](Rank r)const;//重载下标运算符,通过秩直接访问列表节点(虽然方便,但是效率低)
	};

下面我们将介绍具体功能的实现

List类私有成员

private:
        int _size;//链表规模
        ListNodePosi(T) header;//头哨兵
        ListNodePosi(T) trailer;//尾哨兵

头尾哨兵并不参与数据的存储,我们可以假象每一个链表都有一对哨兵,控制着链表的首末,这两个哨兵会大大的方便我们以后的代码实现。

构造函数与析构函数 

无参构造函数

List() { init(); }//默认构造器,即只创建一个含有头哨兵和尾哨兵的空链表
template<class T>
	inline void List<T>::init() {
		header = new ListNode<T>;//new出来一个头哨兵节点
		trailer = new ListNode<T>;//new出来一个尾哨兵节点
		header->succ = trailer;//头哨兵的后继指向尾哨兵
		header->pred = nullptr;//头哨兵的前驱为nullptr
		trailer->pred = header;//尾哨兵的前驱为头哨兵
		trailer->succ = nullptr;//尾哨兵的后继为nullptr
		_size = 0;//记录链表规模为0
	}

new 出来两个节点交给头哨兵和尾哨兵;

让头哨兵的后继指向尾哨兵,尾哨兵的前驱指向头哨兵;

头哨兵的前驱置空,尾哨兵的后继制空

 有参构造函数

再拷贝构造之前,我们得先定义一个函数,用于复制链表的内容


	template<class T>
	inline void List<T>::copyNodes(ListNodePosi(T) p, int n) {
		init();//创建头尾哨兵节点并作初始化
		while (n--) {
			insertAsLast(p->date);//将从p起的n项以此做为尾节点插入
			p = p->succ;//每次插入完成后p都指向下一个节点
		}
	}

算法:

  1. 创建一条链表,即含有header 和trailer的哨兵节点 调用 init()函数;
  2. 当需要复制的节点个数大于零时,我们不断重复插入操作 
    insertAsLast(p->date);//将从p起的n项以此做为尾节点插入
    template<class T>//e作为尾节点插入
    	inline ListNodePosi(T) List<T>::insertAsLast(T const& e) {
    		_size++;
    		return this->trailer->insertAsPred(e);
    	}

  3. 让p指向p的后继 

复制整个链表:

List(List<T>const& L) { copyNodes(L.first(), L._size); }//复制传入构造器的整个链表

复制指定范围内的链表

template<class T>//复制L中自第r项起的n项
	inline List<T>::List(List<T> const& L, int r, int n) {
		copyNodes(L[r], n);
	}

 函数参数

  • 复制对象                   List<T> const& L
  • 从某个节点开始        int r
  • 复制多少个节点        int n

 我们发现L[r]这种形式,c++默认是不允许的,因此我们得重载运算符。

template<class T>
	T& List<T>::operator[](Rank r)const {
		ListNodePosi(T) p = this->first();//从首节点开始
		while (0 < r--) {
			p = p->succ;//找到第r个元素
		}
		return p->data;//返回第r个元素所存储的数据
	}

析构函数

template<class T>
	inline List<T>::~List() {
		clear();//清空列表
		delete header;//释放头哨兵
		delete trailer;//释放尾哨兵
	}
template<class T>//清空链表
	inline int List<T>::clear() {
		int oldSize = _size;//备份原来列表的数据量
		while (0 < _size) {
			remove(this->header->succ);//不断删除首节点
		}
		return oldSize;//返回删除列表节点的大小
	}
template<class T>//删除节点p
	inline T List<T>::remove(ListNodePosi(T) p) {
		T e = p->data;//备份p所指向的数据
		p->pred->succ = p->succ;//p的前驱的后继为p的后继
		p->succ->pred = p->pred;//p后继的前驱为p的前驱
		delete p;//释放p内存
		this->_size--;//链表规模--
		return e;//返回p节点的数据
	}

算法实现:

  1. 备份该节点所储存的信息;
  2. 更新该节点的前驱的后继为该节点的后继;
  3. 更新该节点后继的前驱为该节点的前驱;
  4. 释放该节点;
  5. 存储规模减一
  6. 返回该节点的数据

删除前 

删除后 

 对外接口

size()

这个函数实现非常容易

int size() { return this->_size; }

  返回首、末节点的物理位置

​​​​​利用哨兵节点的便利直接返回

ListNodePosi(T) first()const { return this->header->succ; }//返回首节点物理位置
ListNodePosi(T) last()const { return this->trailer->pred; }//返回尾节点物理位置

 将e作为首末节点插入 

template<class T>//e作为首节点插入
	inline ListNodePosi(T) List<T>::insertAsFirst(T const& e) {
		_size++;//规模++
		return header->insertAsSucc(e);//返回前插节点的物理位置
	}
	
	template<class T>//e作为尾节点插入
	inline ListNodePosi(T) List<T>::insertAsLast(T const& e) {
		_size++;
		return this->trailer->insertAsPred(e);
	}

 将e作为p的后继插入

template<class T>//e作为尾节点插入
	inline ListNodePosi(T) List<T>::insertAsLast(T const& e) {
		_size++;
		return this->trailer->insertAsPred(e);
	}
	

 将e作为p的后继插入

template<class T>//e作为p的后继插入
	inline ListNodePosi(T) List<T>::insertAfter(ListNodePosi(T) p, T const& e) {
		this->_size++;
		return p->insertAsSucc(e);
	}
	

remove(p)删除节点

template<class T>//删除节点p
	inline T List<T>::remove(ListNodePosi(T) p) {
		T e = p->data;//备份p所指向的数据
		p->pred->succ = p->succ;//p的前驱的后继为p的后继
		p->succ->pred = p->pred;//p后继的前驱为p的前驱
		delete p;//释放p内存
		this->_size--;//链表规模--
		return e;//返回p节点的数据
	}

算法实现:

  1. 备份该节点所储存的信息;
  2. 更新该节点的前驱的后继为该节点的后继;
  3. 更新该节点后继的前驱为该节点的前驱;
  4. 释放该节点;
  5. 存储规模减一
  6. 返回该节点的数据

 sort()排序

template<class T>
	inline void List<T>::Sort(ListNodePosi(T) p, int n) {
		switch (rand() % 2)
		{
		case 0:selectSort(p, n); break;
		case 1:mergeSort(p, n); break;
		}
	}

随机从selectSort和mergerSort两种排序算法中选取一种算法,下面我们来介绍这两种算法

selectSort(ListNodePosi(T) p, int n)

选择排序,顾名思义,先选择出链表中最大或者最小的元素,然后按照由大到小或者由小到大的顺序排列。因此我们考虑封装一个选择最大元素和最小元素的函数。

template<class T>//查询列表中最大数据
	inline ListNodePosi(T) List<T>::selectMax(ListNodePosi(T) p, int n) {
		ListNodePosi(T)max = p;//将其实最大值设为p
		for (ListNodePosi(T) cur = p; 1 < n; n--) {
			//从节点p开始往后查询n个节点
			if (isMax(cur = cur->succ, max)) {
				//isMax这个函数,第一个参数表比第二个参数所存储的数据大返回true
				max = cur;//将cur节点给max;
			}
		}
		return max;
	}
template<class T>//查询列表中最小数据
	inline ListNodePosi(T) List<T>::selectMin(ListNodePosi(T) p, int n) {
		ListNodePosi(T)min = p;
		for (ListNodePosi(T) cur = p; 1 < n; n--) {
			if (!isMax(cur = cur->succ, min)) {
				min = cur;
			}
		}
		return min;
	}
template<class T>//比较两个节点所存数据的大小
	inline bool List<T>::isMax(ListNodePosi(T) p1, ListNodePosi(T) p2) {
		if (p1->data >= p2->data)return true;
		else return false;

	}

        算法:查找最大元素

  1.  从首节点开始,先将最大值设为p
  2. 从该节点开始往后遍历n次,调用比较函数inline bool List<T>::isMax(ListNodePosi(T) p1, ListNodePosi(T) p2)
  3. 如果后面的数据比前面的大,将该值赋予max
  4. 返回max

  算法:查找最小元素

  1.  从首节点开始,先将最小值设为p
  2. 从该节点开始往后遍历n次,调用比较函数inline bool List<T>::isMax(ListNodePosi(T) p1, ListNodePosi(T) p2)
  3. 如果后面的数据比前面的小,将该值赋予min
  4. 返回min

 主排序函数:

void selectSort(ListNodePosi(T)p, int n,bool choice=true);
template<class T>
	inline void List<T>::selectSort(ListNodePosi(T) p, int n,bool choice) {
		ListNodePosi(T)head = p->pred;
		ListNodePosi(T)tail = p;
		for (int i = 0; i < n; i++) {
			tail = tail->succ;
		}
		while (1 < n) {
			if (choice) {
				ListNodePosi(T) max = selectMax(head->succ, n);
				insertBefore(tail, remove(max));
			}
			else {
				ListNodePosi(T) min = selectMin(head->succ, n);
				insertBefore(tail, remove(min));
			}
		
			tail = tail->pred;
			n--;
		}
	}

该函数默认排序方法是小到大,我们还提供了一种从大到小的方式,只需要用户输入false即可由大到小;

传入参数(从某个节点开始排列,具体排多少个节点)

算法实现:

  1. 创建一个head指针指向p的后继,tail指针指向p
  2. 让tail指向tail的后继重复n次来到末端
  3. 选取最大节点,将最大节点插入在tail的前驱
  4. n--;tail移动到自己的前驱
  5. 重复2,3步骤,直到n>z;

归并排序法

 

template<class T>
	inline void List<T>::mergeSort(ListNodePosi(T)& p, int n) {
		if (n < 2)return;
		int m = n >> 1;
		ListNodePosi(T)q = p;
		for (int i = 0; i < m; i++)q = q->succ;
		mergeSort(p, m); mergeSort(q, n - m);
		_merge(p, m, *this, q, n - m);
	}
	
template<class T>
	inline void List<T>::_merge(ListNodePosi(T)& p, int n, List<T>& L, ListNodePosi(T) q, int m) {
		ListNodePosi(T)pp = p->pred;
		while ((0 <m) && (q != p)) //q尚未出界(或在mergeSort()中,p亦尚未出界)之前
		      if ((0 < n) && (p->data <= q->data)) //若p尚未出界且v(p) <= v(q),则
			         { p = p->succ; n--; } //p直接后移,即完成归入
		       else //否则,将q转移至p之前,以完成归入
			          { insertBefore(p,L.remove((q = q->succ)->pred)); m--; }
		    p= pp->succ; //更新的首节点
	}

算法:

  1. 将一条链表按照中间拆成两部分
  2. 对左边的链表在进行拆解,对右边的链表在进行拆解,知道链表长度小于2
  3. 调用归并函数,对左右两链表间进行排列,整合成一个大链表
  4. 重复操作,直到排好序。 

 归并算法:

  1. 创建一个新指针指向左链表的前驱;
  2. 当左右链表均为出界时
  3. p未出界同时p节点存储的数据小于或者等于q节点存储的数据时p节点后移,n--
  4. 其他情况即p出界或者p数据大于q数据则将q数据作为p的前驱插入,q右移,并且删除q节点,右链表长度--
  5. 更新首节点,排好序的链表就完成了;

 

查找某一元素

template<class T>
	inline ListNodePosi(T) List<T>::find(T& const e, int n, ListNodePosi(T) p) const {
		while (0 < n--) {
			if (e == (p=p->pred)->data)return p;
		}
		return nullptr;
	}

 

从后往前遍历列表,查找到了返回该元素物理地址; 

遍历

template<class T>
	inline void List<T>::traverse(void(*visit)(T&)) {
		for (ListNodePosi(T)p = first(); p != this->trailer; p = p->succ) {
			visit(p->data);
		}
	}
template<class T>
	void show(T e) {
		std::cout << e << ' '; 
	}

	//遍历元素

剔除重复元素

template<class T>
	inline int List<T>::dedeuplicate() {
		if (this->_size < 2)return 0;//平凡列表自然无重复元素,不需要剔除
		int oldSize = this->_size;//记录原始规模大小
		ListNodePosi(T) p = this->header; Rank r = 0;
		while (trailer!=(p = p->succ)) {
			ListNodePosi(T) q = find(p->data, r, p);
			q ? remove(q) : r++;
	
		}
		return oldSize - _size; 
	}

剔除

算法实现:

  1. 记录原始链表规模
  2. 将头节点的物理地址赋给一个新节点
  3. 当新节点的物理位置不等于尾哨兵时,不断执行:在r p的范围查找p指针所指的数据,如果有相同的,那么删除这个元素 r++
  4. 返回现有规模;
template<class T>
	inline int List<T>::dedeuplicateProMax()
	{
		if (this->_size < 2)return 0;//平凡列表自然无重复元素,不需要剔除
		int oldSize = this->_size;//记录原始规模大小
		ListNodePosi(T) p = first();
		ListNodePosi(T) q;
		while (trailer != (q = p->succ))
			if (p->data != q->data)p = q;
			else remove(q);
		return oldSize - _size;
	}

 有序列表剔除

算法图解:

算法:

  1. 创建两个节点 p=first(),q
  2. q=p->succ;当q补位尾哨兵时不断执行 如果p q数据不相等 p=q;相等剔除q
  3. p q 后移动
  4. 返回当前规模, 

完整代码如下

 

#pragma once
typedef int Rank;//秩
#define ListNodePosi(T) ListNode<T>*
	template<class T>
	class ListNode
	{
		//friend class List;
	public:
		T data;
		ListNodePosi(T) pred;
		ListNodePosi(T) succ;
	public:
		ListNode() { data= 0; pred = nullptr; succ = nullptr; };
		ListNode(T e, ListNodePosi(T)p = nullptr, ListNodePosi(T) s = nullptr);
	public:
		ListNodePosi(T) insertAsPred(T const& e);
		ListNodePosi(T) insertAsSucc(T const& e);
	};

	template<class T>
	inline ListNode<T>::ListNode(T e, ListNodePosi(T) p, ListNodePosi(T) s) {
		this->data = e;
		this->pred = p;
		this->succ = s;
	}

	template<class T>
	inline ListNodePosi(T) ListNode<T>::insertAsPred(T const& e) {
		ListNodePosi(T)x = new ListNode(e, pred, this);
		pred->succ = x;
		pred = x;
		return x;
	}

	template<class T>
	inline ListNodePosi(T) ListNode<T>::insertAsSucc(T const& e) {
		ListNodePosi(T)x = new ListNode(e, this, succ);
		succ->pred = x;
		succ = x;
		return x;
	}
#pragma once
#include"ListNode.hpp"
#include<iostream>
namespace myList {
	template<class T>
	class List
	{
	private:
		int _size;//链表规模
		ListNodePosi(T) header;//头哨兵
		ListNodePosi(T) trailer;//尾哨兵

	protected:
		void init();//初始化函数
		void copyNodes(ListNodePosi(T) p, int n);//拷贝函数
		int clear();//列表清空方法
		bool isMax(ListNodePosi(T)p1, ListNodePosi(T)p2);//判断节点数据大小
		ListNodePosi(T) selectMax(ListNodePosi(T)p, int n);//选取数据最大的节点
		ListNodePosi(T) selectMin(ListNodePosi(T)p, int n);//选取数据最小的节点
		void selectSort(ListNodePosi(T)p, int n,bool choice=true);//选择排序
		void _merge(ListNodePosi(T)& p, int n, List<T>& L, ListNodePosi(T)q, int m);//归并
		void mergeSort(ListNodePosi(T)& p, int n);//归并排序
		
	public:
		List() { init(); }//默认构造器,即只创建一个含有头哨兵和尾哨兵的空链表
		List(List<T>const& L) { copyNodes(L.first(), L._size); }//复制传入构造器的整个链表
		List(List<T>const& L, int r, int n);//复制L的某一区间
		~List();//列表析构器
	public:
		Rank size() { return this->_size; }
		ListNodePosi(T) insertAsFirst(T const& e);//头节点
		ListNodePosi(T) insertAsLast(T const& e);//尾节点
		ListNodePosi(T) insertAfter(ListNodePosi(T) p, T const& e);//e作为p的后继插入
		ListNodePosi(T) insertBefore(ListNodePosi(T)p, T const& e);//e作为p的前驱插入
		ListNodePosi(T) first()const { return this->header->succ; }//返回首节点物理位置
		ListNodePosi(T) last()const { return this->trailer->pred; }//返回尾节点物理位置
		T remove(ListNodePosi(T)p);//删除节点
		ListNodePosi(T) find(T& const e, int n, ListNodePosi(T)p)const;//查找数据所在的节点
		ListNodePosi(T) find(T const& e)const;//查找数据所在的节点
		int dedeuplicate();//提出列表中的重复元素;
		ListNodePosi(T) search(T const& e, int n,ListNodePosi(T) p)const;//排序完成后的查找
		void traverse(void(*visit)(T&));//遍历元素
		void Sort(ListNodePosi(T)p, int n);//随机选取排序方法:归并、选择
		int dedeuplicateProMax();
	private:
		T& operator[](Rank r)const;//重载下标运算符,通过秩直接访问列表节点(虽然方便,但是效率低)
	};

	template<class T>
	inline void List<T>::init() {
		header = new ListNode<T>;//new出来一个头哨兵节点
		trailer = new ListNode<T>;//new出来一个尾哨兵节点
		header->succ = trailer;//头哨兵的后继指向尾哨兵
		header->pred = nullptr;//头哨兵的前驱为nullptr
		trailer->pred = header;//尾哨兵的前驱为头哨兵
		trailer->succ = nullptr;//尾哨兵的后继为nullptr
		_size = 0;//记录链表规模为0
	}
	
	template<class T>//复制传入构造器的整个链表
	inline void List<T>::copyNodes(ListNodePosi(T) p, int n) {
		init();//创建头尾哨兵节点并作初始化
		while (n--) {
			insertAsLast(p->date);//将从p起的n项以此做为尾节点插入
			p = p->succ;//每次插入完成后p都指向下一个节点
		}
	}
	
	template<class T>//清空链表
	inline int List<T>::clear() {
		int oldSize = _size;//备份原来列表的数据量
		while (0 < _size) {
			remove(this->header->succ);//不断删除首节点
		}
		return oldSize;//返回删除列表节点的大小
	}

	//列表清空方法
	template<class T>//比较两个节点所存数据的大小
	inline bool List<T>::isMax(ListNodePosi(T) p1, ListNodePosi(T) p2) {
		if (p1->data >= p2->data)return true;
		else return false;

	}

	template<class T>//查询列表中最大数据
	inline ListNodePosi(T) List<T>::selectMax(ListNodePosi(T) p, int n) {
		ListNodePosi(T)max = p;//将其实最大值设为p
		for (ListNodePosi(T) cur = p; 1 < n; n--) {
			//从节点p开始往后查询n个节点
			if (isMax(cur = cur->succ, max)) {
				//isMax这个函数,第一个参数表比第二个参数所存储的数据大返回true
				max = cur;//将cur节点给max;
			}
		}
		return max;
	}

	template<class T>//查询列表中最小数据
	inline ListNodePosi(T) List<T>::selectMin(ListNodePosi(T) p, int n) {
		ListNodePosi(T)min = p;
		for (ListNodePosi(T) cur = p; 1 < n; n--) {
			if (!isMax(cur = cur->succ, min)) {
				min = cur;
			}
		}
		return min;
	}

	template<class T>
	inline void List<T>::selectSort(ListNodePosi(T) p, int n,bool choice) {
		ListNodePosi(T)head = p->pred;
		ListNodePosi(T)tail = p;
		for (int i = 0; i < n; i++) {
			tail = tail->succ;
		}
		while (1 < n) {
			if (choice) {
				ListNodePosi(T) max = selectMax(head->succ, n);
				insertBefore(tail, remove(max));
			}
			else {
				ListNodePosi(T) min = selectMin(head->succ, n);
				insertBefore(tail, remove(min));
			}
		
			tail = tail->pred;
			n--;
		}
	}

	template<class T>
	inline void List<T>::_merge(ListNodePosi(T)& p, int n, List<T>& L, ListNodePosi(T) q, int m) {
		ListNodePosi(T)pp = p->pred;
		while ((0 <m) && (q != p)) //q尚未出界(或在mergeSort()中,p亦尚未出界)之前
		      if ((0 < n) && (p->data <= q->data)) //若p尚未出界且v(p) <= v(q),则
			         { p = p->succ; n--; } //p直接后移,即完成归入
		       else //否则,将q转移至p之前,以完成归入
			          { insertBefore(p,L.remove((q = q->succ)->pred)); m--; }
		    p= pp->succ; //更新的首节点
	}

	template<class T>
	inline void List<T>::mergeSort(ListNodePosi(T)& p, int n) {
		if (n < 2)return;
		int m = n >> 1;
		ListNodePosi(T)q = p;
		for (int i = 0; i < m; i++)q = q->succ;
		mergeSort(p, m); mergeSort(q, n - m);
		_merge(p, m, *this, q, n - m);
	}
	
	template<class T>//复制L中自第r项起的n项
	inline List<T>::List(List<T> const& L, int r, int n) {
		copyNodes(L[r], n);
	}
	
	template<class T>
	inline List<T>::~List() {
		clear();//清空列表
		delete header;//释放头哨兵
		delete trailer;//释放尾哨兵
	}
	template<class T>//e作为首节点插入
	inline ListNodePosi(T) List<T>::insertAsFirst(T const& e) {
		_size++;//规模++
		return header->insertAsSucc(e);//返回前插节点的物理位置
	}
	
	template<class T>//e作为尾节点插入
	inline ListNodePosi(T) List<T>::insertAsLast(T const& e) {
		_size++;
		return this->trailer->insertAsPred(e);
	}
	
	template<class T>//e作为p的后继插入
	inline ListNodePosi(T) List<T>::insertAfter(ListNodePosi(T) p, T const& e) {
		this->_size++;
		return p->insertAsSucc(e);
	}
	
	template<class T>//e作为p的前驱插入
	inline ListNodePosi(T) List<T>::insertBefore(ListNodePosi(T) p, T const& e) {
		this->_size++;
		return p->insertAsPred(e);
	}
	
	template<class T>//删除节点p
	inline T List<T>::remove(ListNodePosi(T) p) {
		T e = p->data;//备份p所指向的数据
		p->pred->succ = p->succ;//p的前驱的后继为p的后继
		p->succ->pred = p->pred;//p后继的前驱为p的前驱
		delete p;//释放p内存
		this->_size--;//链表规模--
		return e;//返回p节点的数据
	}
	template<class T>
	inline ListNodePosi(T) List<T>::find(T& const e, int n, ListNodePosi(T) p) const {
		while (0 < n--) {
			if (e == (p=p->pred)->data)return p;
		}
		return nullptr;
	}
	template<class T>
	inline ListNodePosi(T) List<T>::find(T const& e) const {
		return find(e, _size, trailer);
	}
	template<class T>
	inline int List<T>::dedeuplicate() {
		if (this->_size < 2)return 0;//平凡列表自然无重复元素,不需要剔除
		int oldSize = this->_size;//记录原始规模大小
		ListNodePosi(T) p = this->header; Rank r = 0;
		while (trailer!=(p = p->succ)) {
			ListNodePosi(T) q = find(p->data, r, p);
			q ? remove(q) : r++;
	
		}
		return oldSize - _size; 
	}
	template<class T>
	inline ListNodePosi(T) List<T>::search(T const& e,int n, ListNodePosi(T) p) const {
		while (0 <= n--) {
			if (p->pred->data == e)break;
			p = p->pred;
		}
		return p;
	}
	template<class T>
	inline void List<T>::traverse(void(*visit)(T&)) {
		for (ListNodePosi(T)p = first(); p != this->trailer; p = p->succ) {
			visit(p->data);
		}
	}

	//遍历元素
	template<class T>
	inline void List<T>::Sort(ListNodePosi(T) p, int n) {
		switch (rand() % 2)
		{
		case 0:selectSort(p, n); break;
		case 1:mergeSort(p, n); break;
		}
	}

	//随机选取排序方法:归并、选择
	template<class T>
	inline int List<T>::dedeuplicateProMax()
	{
		if (this->_size < 2)return 0;//平凡列表自然无重复元素,不需要剔除
		int oldSize = this->_size;//记录原始规模大小
		ListNodePosi(T) p = first();
		ListNodePosi(T) q;
		while (trailer != (q = p->succ))
			if (p->data != q->data)p = q;
			else remove(q);
		return oldSize - _size;
	}
	template<class T>
	void show(T e) {
		std::cout << e << ' '; 
	}
	template<class T>
	T& List<T>::operator[](Rank r)const {
		ListNodePosi(T) p = this->first();//从首节点开始
		while (0 < r--) {
			p = p->succ;//找到第r个元素
		}
		return p->data;//返回第r个元素所存储的数据
	}
}

总结

      链表实质上就是链接在堆区开辟的一片数据结构。 只要明确的知道每一个节点的前驱的后继,那么链表的功能实现起来就十分容易了。

C++中的模板(template)是一种可以用来创建通用代码的机制,可以让我们编写一些通用的代码,使其可以适用于多种不同的数据类型。模板可以用于函数、类、成员函数、类模板等。 函数模板的定义形式如下: ```cpp template<typename T> T add(T a, T b) { return a + b; } ``` 在这个示例中,我们定义了一个add函数模板,它接受两个参数,类型为T,然后返回它们的和。在函数定义前面加上`template<typename T>`表示这是一个函数模板,并且T是一个类型参数。我们可以在调用函数时指定T的类型,例如: ```cpp int main() { std::cout << add<int>(1, 2) << std::endl; // 输出3 std::cout << add<double>(1.0, 2.0) << std::endl; // 输出3.0 return 0; } ``` 在这个示例中,我们分别使用int和double类型来调用add函数模板,并将结果输出到屏幕上。 类模板的定义形式如下: ```cpp template<typename T> class Stack { public: void push(T value); T pop(); bool empty() const; private: std::vector<T> data; }; template<typename T> void Stack<T>::push(T value) { data.push_back(value); } template<typename T> T Stack<T>::pop() { T value = data.back(); data.pop_back(); return value; } template<typename T> bool Stack<T>::empty() const { return data.empty(); } ``` 在这个示例中,我们定义了一个Stack类模板,它包含三个成员函数:push、pop和empty。在类定义前面加上`template<typename T>`表示这是一个类模板,并且T是一个类型参数。在类定义后面的成员函数的定义中,需要在类名和函数名之间加上`<T>`来指明这是一个模板函数。我们可以在创建类实例时指定T的类型,例如: ```cpp int main() { Stack<int> my_stack; my_stack.push(10); my_stack.push(20); std::cout << my_stack.pop() << std::endl; // 输出20 std::cout << my_stack.pop() << std::endl; // 输出10 std::cout << my_stack.empty() << std::endl; // 输出1(true) return 0; } ``` 在这个示例中,我们创建了一个Stack<int>实例,并向其中添加了两个元素,然后依次将它们弹出并输出到屏幕上。最后,我们检查了栈是否为空。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值