本例子是《数据结构与算法c++描述》中的线性表中单向链表(singly linked list)
单链表原理图:
first 为头节点 ,由于第一个节点 e1 的指针指向第二个节点 e2, e2 的指针指向e3 ,. . .,最后一个节点链接域为 N U L L (或0 ),故这种结构也被称作链(chain)。
单链表定义:
/*
数组结构中的线性表链表样式
对应书中代码:数据结构算法与应用c++描述
程序编写:比卡丘不皮
编写时间:2020年6月30日 17:55:29
*/
#pragma once
#include "all_error.h"
#include <iostream>
using namespace std;
template<class T>
class Chain; //声明
template<class T>
class ChainIterator; //迭代器
template<class T>
class ChainNode
{
friend Chain<T>;
friend ChainIterator<T>; //因为要访问private
private:
T data;
ChainNode<T> * link;
};
template<class T>
class Chain
{
friend ChainIterator<T>; //应为要访问private
public:
Chain() { first = 0; last = first; };//初始化函数
~Chain(); //析构函数
Chain(const Chain<T> & Val); //复制构造函数
bool IsEmpty() const { return first == 0; } //判断是不是空
int Length()const; //链表的长度
bool Find(int k, T &x) const; //查找第k可的数据,没有为false
int Search(const T & x) const; //查找数据函数
Chain<T>& Delete(int k, T & x); //删除某个数据
Chain<T>& Insert(int k, const T & x); //插入数据
void Output(ostream & out)const; // 输出接点数据
//扩充链表
void Erase(); //删除链表
void Zero() { first = 0; } //数据first 为0
Chain<T>& Append(const T & x); //在最后添加
Chain<T>& Reverse(); //元素次序的反转 原地的反转
//线性表交叉组合 参数一定不是自己的变量
Chain<T>& Alternate(const Chain<T>& list1, const Chain<T>& list2);
//有规律(元素从小到大排列)线性表的组合
Chain<T>& Merge(const Chain<T>& list1, const Chain<T>& list2);
//链表排序 排序法
void sort();
//线性表分割函数 交叉分割
void Split(Chain<T>& list1, Chain<T>& list2);
Chain<T> & operator =(const Chain<T>& x); //复制构造函数一定要同时写这个
private:
ChainNode<T> * first; //记录头指针
ChainNode<T> * last; //记录尾指针
};
//链表遍历类
template<class T>
class ChainIterator
{
public:
T * Initialize(const Chain<T> & c)
{
location = c.first;
if (location)
{
return &location->data;
}
return 0;
}
T * Next()
{
if (!location)
{
return 0;
}
location = location->link;
if (location)
{
return &location->data;
}
return 0;
}
private:
ChainNode<T> *location;
};
有关 #include "all_error.h" 请看我博客中(1)的写法:
传送门:https://blog.csdn.net/weixin_42126427/article/details/107013624
首先
template<class T>
class Chain; //声明
template<class T>
class ChainIterator; //迭代器
声明,在对应类中friend class 是可以在对应链表中访问private变量,所以要声明。
析构函数:
template<class T>
Chain<T>::~Chain()
{
ChainNode<T> * next;
while (first)
{
next = first->link;
delete first;
first = next;
}
}
循环对当前节点删除,而不是只是把first 给delete ,这里一定要这样删除,不然类存泄漏.
Length()函数:
template<class T>
int Chain<T>::Length() const
{
ChainNode<T> * current = first;
int len = 0;
while (current)
{
len++;
current = current->link;
}
return len;
}
目的:计算链表的长度,返回长度。
Find(int k, T &x)函数:
template<class T>
bool Chain<T>::Find(int k, T & x) const
{
if (k < 1)
{
return false;
}
ChainNode<T> * current = first;
int index = 1; //current的索引
while (index < k && current)
{
current = current->link;
index++;
}
if (current)
{
x = current->data;
return true;
}
return false;
}
查找第k可的数据,没有为false,有数据,把值赋值给X;
搜索Search(const T & x):
//寻找x, 如果发现x 返回x的地址
//如果不在列表中,返回0
template<class T>
int Chain<T>::Search(const T & x) const
{
ChainNode<T> *current = first;
int index = 1;
while (current && current->data != x)
{
current = current->link;
index++;
}
if (current)
{
return index;
}
return 0;
}
删除 Delete(int k, T & x)
删除原理图:
比如要删除第4个,要找到第4个,然后让3连接到5上,然后释放4的节点;
//把第k个元素取至x,然后从链表中删除第 k个元素
//如果不存在第 k个元素,则引发异常 OutOfBounds
template<class T>
Chain<T>& Chain<T>::Delete(int k, T & x)
{
if (k < 1 || !first)
{
throw OutOfBounds();
}
// p最终将指向第 k个节点
ChainNode<T> * p = first;
// 将p移动至第k个元素,并从链表中删除该元素
if (k == 1) //p已经指向第k个元素
{
first = first->link; //这样就删除了
}
else
{
ChainNode<T> * q = first;
for (int index = 1; index < k-1 && q; index++)
{
q = q->link;
}
if (!q || !q->link)
{
throw OutOfBounds();
}
//存在第k个元素 删除对应元素
p = q->link;
if (p == last)
{
last = q;
}
q->link = p->link; //两种可能性,p->link为0, 那么 q->link 就是0, 若有下个数据,就连接下个数据
}
x = p->data;
delete p;
return *this;
}
输出Output(ostream & out)
template<class T>
void Chain<T>::Output(ostream & out) const
{
ChainNode<T> *current;
for (current = first; current; current = current->link)
{
out << current->data << " ";
}
}
//重载<<
template<class T>
ostream & operator <<(ostream & out, const Chain<T> & x)
{
x.Output(out);
return out;
}
插入Insert(int k, const T & x):
插入原理图:
当插入为0为在first插入最前,当为k不是0在中间插入 ,OutOfBounds 这些错误在上面的传送门中的头文件中
//在第k个元素之后插入x
//如果不存在第 k个元素,则引发异常OutOfBounds
//如果没有足够的空间,则传递 Nomem异常
template<class T>
Chain<T>& Chain<T>::Insert(int k, const T & x)
{
if (k < 0)
{
throw OutOfBounds();
}
//p最终将指向第 k个节点
ChainNode<T> * p = first;
//将p移动至第k个元素
for (int index = 1; index < k && p; index++ )
{
p = p->link;
}
//不存在第k个元素
if (k > 0 && !p)
{
throw OutOfBounds();
}
//插入
ChainNode<T> * y = new ChainNode<T>;
y->data = x;
if (k) //在p之后插入
{
y->link = p->link;
p->link = y;
}
else
{
y->link = first;
first = y;
}
if (!y->link)
{
last = y; //记录最后一个节点
}
return *this;
}
删除Erase():
//删除说有节点
template<class T>
void Chain<T>::Erase()
{
ChainNode<T> *next;
while (first)
{
next = first->link;
delete first;
first = next;
}
}
添加Append(const T & x):
//在链表尾部添加一个节点
template<class T>
Chain<T>& Chain<T>::Append(const T & x)
{
ChainNode<T> *y;
y = new ChainNode<T>;
y->data = x; y->link = 0;
if (first) //链表非空
{
last->link = y;
last = y;
}
else
{
first = last = y;
}
return *this;
}
已上函数都是书上的源码部分,对应的函数都已经实现,下面部分为后面习题部分自己实现的函数功能,自己写的测试可以通过,若你发现有些有问题欢迎在下面评论。
复制构造函数:
//复制构造函数
template<class T>
Chain<T>::Chain(const Chain<T>& Val)
{
this->Erase(); //先清除所有数据
ChainNode<T> * next = Val.first;
while (next)
{
this->Append(next->data);
next = next->link;
}
}
//重载 =
template<class T>
Chain<T>& Chain<T>::operator=(const Chain<T>& x)
{
this->Erase(); //先清除所有数据
ChainNode<T> * next = x.first;
while (next)
{
this->Append(next->data);
next = next->link;
}
return *this;
}
反转 Reverse():
//次序反转
template<class T>
Chain<T>& Chain<T>::Reverse()
{
if (first ==0 && first == last && first->link == 0)
{
return *this;
}
ChainNode<T> * current = first; //记录位置
ChainNode<T> * pre = NULL;
last = first; //头变尾
//好好体会一下这个
while (first)
{
current = first;
first = first->link;
current->link = pre;
pre = current;
}
first = current; //current 为头指针
return *this;
}
交叉线性Alternate(const Chain<T>& list1, const Chain<T>& list2)
//交叉线性
template<class T>
Chain<T>& Chain<T>::Alternate(const Chain<T>& list1, const Chain<T>& list2)
{
if (this == &list1 && this == &list2)
{
cout << "参数不能是本身变量" << endl;
return *this;
}
//清除前链表的数据
this->Erase();
// int length = list1.Length() + list2.Length();
ChainNode<T> * pList1 = list1.first;
ChainNode<T> * pList2 = list2.first;
while (pList1 && pList2)
{
this->Append(pList1->data);
this->Append(pList2->data);
//指向下个
pList1 = pList1->link;
pList2 = pList2->link;
}
if (!pList1) //pList1为空的话
{
while (pList2)
{
this->Append(pList2->data);
pList2 = pList2->link;
}
}
else
{
while (pList1)
{
this->Append(pList1->data);
pList1 = pList1->link;
}
}
return *this;
}
合并有序数据Merge(const Chain<T>& list1, const Chain<T>& list2)
//合并数据 前提数据必须是有序的。
template<class T>
Chain<T>& Chain<T>::Merge(const Chain<T>& list1, const Chain<T>& list2)
{
//清除前链表的数据
if (this == &list1 && this == &list2)
{
cout << "参数不能是本身变量" << endl;
return *this;
}
//清除前链表的数据
this->Erase();
//获取两个链表的头节点
ChainNode<T> * pList1 = list1.first;
ChainNode<T> * pList2 = list2.first;
while (pList1 && pList2)
{
if (pList1->data <= pList2->data)
{
this->Append(pList1->data);
pList1 = pList1->link;
}
else
{
this->Append(pList2->data);
pList2 = pList2->link;
}
}
//当其中一个链表为空或者都为空,开始下面处理
if (!pList1)
{
while (pList2)
{
this->Append(pList2->data);
pList2 = pList2->link;
}
}
else
{
while (pList1)
{
this->Append(pList1->data);
pList1 = pList1->link;
}
}
return *this;
}
排序sort()
//选择排序
template<class T>
void Chain<T>::sort()
{
ChainNode<T> * p = first->link; //获取
ChainNode<T> * min = first; //先获取头节点数据为最小值
while(min)
{
p = min->link;
while (p)
{
if (min->data > p->data)
{
T temp = min->data;
min->data = p->data;
p->data = temp;
}
p = p->link;
}
min = min->link; //找到下一位
}
}
这里是书上没有要求的,为了后面测试方便。
拆分Split(Chain<T>& list1, Chain<T>& list2):
template<class T>
void Chain<T>::Split(Chain<T>& list1, Chain<T>& list2)
{
//拆分的两个变量不能为当前拆分的值
if (this == &list1 && this == &list2)
{
cout << "参数不能是本身变量" << endl;
return ;
}
//若要拆分 list1 与 list2 首先要滞空
list1.Erase();
list2.Erase();
ChainNode<T> * current = first;
int index = 1;
while (current)
{
if ((index & 1) == 1) //当为奇数的时候
{
list1.Append(current->data);
}
else //当为偶数的时候
{
list2.Append(current->data);
}
//到达下个数据
current = current->link;
index++;
}
}
测试函数:
void testChain()
{
Chain<int> c;
cout << "新建链表: " << endl;
cout << "链表是否为空: " << c.IsEmpty() << endl;
cout << "链表的长度: " << c.Length() << endl;
cout << "链表为: " << c << endl << endl;
//测试插入函数
c.Insert(0, 1).Insert(1, 2).Insert(2, 3).Insert(3,4);
cout << "链表是否为空: " << c.IsEmpty() << endl;
cout << "链表的长度: " << c.Length() << endl;
cout << "链表为: " << c << endl << endl;
int value = 0;
//find函数查找
c.Find(2, value);
cout << "测试Find()函数---c.Find(2,x):" << endl;
cout << "x为: " << value << endl << endl;
//查找函数
cout << "测试Search():" << endl;
cout << "元素0的位置为: " << c.Search(0) << endl;
cout << "元素2的位置为: " << c.Search(2) << endl << endl;
//测试删除
cout << "开始删除链表" << endl;
cout << "源链表: " << c << endl;
c.Delete(2, value);
cout << "删除的数据是: " << value <<endl;
cout << "删除后的链表: " << c << endl;
//append 函数测试
c.Append(10);
cout << "插入后链表为: " << c << endl << endl;
//测试新遍历器
int * va;
ChainIterator<int> d;
va = d.Initialize(c);
while (va)
{
cout << *va << " ";
va = d.Next();
}
cout << endl;
//测试构建复制函数
cout << "构建复制函数" << endl;
Chain<int> NewNum(c);
cout << "源链表: " << c << endl;
cout << "新链表: " << NewNum << endl;
//测试 =
cout << "=号赋值" << endl;
NewNum = c;
cout << "源链表: " << c << endl;
cout << "新链表: " << NewNum << endl;
cout << endl;
//测试反转函数
cout << "源链表: " << c << endl;
c.Reverse();
cout << "反转后的链表: " << c << endl;
cout << endl;
cout << " 测试Alternate 函数 " << endl;
Chain<int> testAl;
testAl.Alternate(NewNum,c);
cout << "NewNum 列表是: " << NewNum << endl;
cout << "c 列表是: " << c << endl;
cout << "testAl 列表是: " << testAl << endl;
cout << endl;
//排序测试 选择排序
cout << "排序测试" << endl;
cout << "c 列表是: " << c << endl;
cout << "testAl 列表是: " << testAl << endl;
c.sort();
testAl.sort();
cout << "排序后 c 列表是: " << c << endl;
cout << "排序后 testAl 列表是: " << testAl << endl;
//排序后测试Merge 函数
cout << "排序后测试Merge 函数" << endl;
Chain<int> testMerge;
testMerge.Merge(c, testAl);
cout << "Merge(c, testAl) 函数: " << testMerge << endl;
cout << endl;
//测试Split
cout << "测试Split 函数" << endl;
Chain<int> one, two;
cout << "拆分前testMerge数据:" << testMerge << endl;
cout << "拆分前one数据: " << one << endl;
cout << "拆分前two数据: " << two << endl;
testMerge.Split(one, two);
cout << "拆分后testMerge数据:" << testMerge << endl;
cout << "拆分后one数据: " << one << endl;
cout << "拆分后two数据: " << two << endl;
}
主函数:
#include <iostream>
#include "listearLine.h"
#include "Chain.h"
using namespace std;
int main()
{
//测试线性表数组
//testLinearList();
//测试线性表链表
testChain();
return 0;
}
程序运行结果:
新建链表:
链表是否为空: 1
链表的长度: 0
链表为:
链表是否为空: 0
链表的长度: 4
链表为: 1 2 3 4
测试Find()函数---c.Find(2,x):
x为: 2
测试Search():
元素0的位置为: 0
元素2的位置为: 2
开始删除链表
源链表: 1 2 3 4
删除的数据是: 2
删除后的链表: 1 3 4
插入后链表为: 1 3 4 10
1 3 4 10
构建复制函数
源链表: 1 3 4 10
新链表: 1 3 4 10
=号赋值
源链表: 1 3 4 10
新链表: 1 3 4 10
源链表: 1 3 4 10
反转后的链表: 10 4 3 1
测试Alternate 函数
NewNum 列表是: 1 3 4 10
c 列表是: 10 4 3 1
testAl 列表是: 1 10 3 4 4 3 10 1
排序测试
c 列表是: 10 4 3 1
testAl 列表是: 1 10 3 4 4 3 10 1
排序后 c 列表是: 1 3 4 10
排序后 testAl 列表是: 1 1 3 3 4 4 10 10
排序后测试Merge 函数
Merge(c, testAl) 函数: 1 1 1 3 3 3 4 4 4 10 10 10
测试Split 函数
拆分前testMerge数据:1 1 1 3 3 3 4 4 4 10 10 10
拆分前one数据:
拆分前two数据:
拆分后testMerge数据:1 1 1 3 3 3 4 4 4 10 10 10
拆分后one数据: 1 1 3 4 4 10
拆分后two数据: 1 3 3 4 10 10
请按任意键继续. . .
本文例子程序:
链接:https://pan.baidu.com/s/1m8KlkXVzzNGKmKE9I9lwCA 提取码:c2te
如果遇到什么问题可以联系我,关注我的博客,一起加油学习吧。