目录
目录
列表
节点(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都指向下一个节点
}
}
算法:
- 创建一条链表,即含有header 和trailer的哨兵节点 调用 init()函数;
- 当需要复制的节点个数大于零时,我们不断重复插入操作?
insertAsLast(p->date);//将从p起的n项以此做为尾节点插入 template<class T>//e作为尾节点插入
inline ListNodePosi(T) List<T>::insertAsLast(T const& e) {
_size++;
return this->trailer->insertAsPred(e);
} -
让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节点的数据
}
算法实现:
- 备份该节点所储存的信息;
- 更新该节点的前驱的后继为该节点的后继;
- 更新该节点后继的前驱为该节点的前驱;
- 释放该节点;
- 存储规模减一
- 返回该节点的数据
删除前?
删除后?
?对外接口
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节点的数据
}
算法实现:
- 备份该节点所储存的信息;
- 更新该节点的前驱的后继为该节点的后继;
- 更新该节点后继的前驱为该节点的前驱;
- 释放该节点;
- 存储规模减一
- 返回该节点的数据
?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;
}
? ? ? ? 算法:查找最大元素
- ?从首节点开始,先将最大值设为p
- 从该节点开始往后遍历n次,调用比较函数inline bool List<T>::isMax(ListNodePosi(T) p1, ListNodePosi(T) p2)
- 如果后面的数据比前面的大,将该值赋予max
- 返回max
? 算法:查找最小元素
- ?从首节点开始,先将最小值设为p
- 从该节点开始往后遍历n次,调用比较函数inline bool List<T>::isMax(ListNodePosi(T) p1, ListNodePosi(T) p2)
- 如果后面的数据比前面的小,将该值赋予min
- 返回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即可由大到小;
传入参数(从某个节点开始排列,具体排多少个节点)
算法实现:
- 创建一个head指针指向p的后继,tail指针指向p
- 让tail指向tail的后继重复n次来到末端
- 选取最大节点,将最大节点插入在tail的前驱
- n--;tail移动到自己的前驱
- 重复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; //更新的首节点
}
算法:
- 将一条链表按照中间拆成两部分
- 对左边的链表在进行拆解,对右边的链表在进行拆解,知道链表长度小于2
- 调用归并函数,对左右两链表间进行排列,整合成一个大链表
- 重复操作,直到排好序。?
?归并算法:
- 创建一个新指针指向左链表的前驱;
- 当左右链表均为出界时
- p未出界同时p节点存储的数据小于或者等于q节点存储的数据时p节点后移,n--
- 其他情况即p出界或者p数据大于q数据则将q数据作为p的前驱插入,q右移,并且删除q节点,右链表长度--
- 更新首节点,排好序的链表就完成了;
?
查找某一元素
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;
}
剔除
算法实现:
- 记录原始链表规模
- 将头节点的物理地址赋给一个新节点
- 当新节点的物理位置不等于尾哨兵时,不断执行:在r p的范围查找p指针所指的数据,如果有相同的,那么删除这个元素 r++
- 返回现有规模;
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;
}
?有序列表剔除
算法图解:
算法:
- 创建两个节点 p=first(),q
- q=p->succ;当q补位尾哨兵时不断执行 如果p q数据不相等 p=q;相等剔除q
- p q 后移动
- 返回当前规模,?
完整代码如下
?
#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个元素所存储的数据
}
}
总结
? ? ? 链表实质上就是链接在堆区开辟的一片数据结构。 只要明确的知道每一个节点的前驱的后继,那么链表的功能实现起来就十分容易了。
|