数据结构之单链表(SglLinkedList)源码

本文详细介绍了单链表数据结构SglLinkedList的实现,包括头文件SglLinkedList.h的定义以及在main.cpp测试文件中的应用,通过实例展示了如何操作和使用该链表。测试结果显示代码运行正确,能够完成预期功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SglLinkedList.h文件

#pragma once
/*
*Copyright© 中国地质大学(武汉) 信息工程学院
*All right reserved.
*
*文件名称:SysLog.h
*摘       要:实现系统登陆功能
*
*当前版本:1.0
*作       者:邵玉胜
*完成日期:2018-03-09
*/

#include<iostream>
using namespace std;

//结点模板类定义
template<class T>
struct LinkedNode
{
	T _data;                                      //存放数据域
	LinkedNode* _next;                            //存放指针域,指向下一结点
	LinkedNode(LinkedNode<T>* ptr = nullptr) {    //仅初始化指针成员的构造函数
		_next = ptr;
	}
	//初始化指针域与数据域的构造函数
	LinkedNode(const T& data, LinkedNode<T>* ptr = nullptr) {
		_data = data;
		_next = ptr;
	}
};



//单链表模板类定义
template<class T>
class SglLinkedList
{
private:
	LinkedNode<T>* _pFirst;                             //指向头结点的指针
	int _iCurrent;                                     //保存结点数
	LinkedNode<T>* _pRear;                              //指向尾结点的指针
public:
	SglLinkedList();                             //构造函数
	void init(SglLinkedList<T>& listPar);  //初始化函数
	SglLinkedList(SglLinkedList<T>& listPar);    //复制构造函数
	//赋值运算符重载
	SglLinkedList<T>& operator = (SglLinkedList<T> listPar);
	~SglLinkedList();                            //析构函数
	//输入输出函数
	void input(T endTag);                                //输入函数
	void output();                               //输出函数
	//追加与插入函数
	void append(const T data);                   //追加函数
	bool insert(int pos, const T data);         //插入函数,pos从1开始
	//删除与置空函数
	bool remove(int pos, T& data);               //删除第pos个位置的数据并用data保存起来
	void makeEmpty();                            //将整个链表置空
	//判断链表是否为空的函数  
	bool isEmpty()const { return (_iCurrent == 0) ? true:false; }                             //判断链表空否
	////取特定位置数据的函数
	bool getItem(const int pos, T& data)const;              //取出pos个位置的数据并用data保存起来
	//取头结点
	LinkedNode<T>* getHead();                          //获取头结点地址
	LinkedNode<T>* getRear();                          //获取尾结点地址
	LinkedNode<T>* search(const T data)const;                   //搜索含数据x的元素
	LinkedNode<T>* locate(const int pos)const;                  //取出第pos个元素的位置,pos从1开始
	void sort(bool asc = true);                      //排序函数
	int getLengh()const { return _iCurrent; };       //获取表的长度
};


template < class T >
SglLinkedList<T>::SglLinkedList()
{
	_pRear = _pFirst = new LinkedNode<T>();                 //创建头结点
	if (_pFirst == nullptr) {                      //如果内存分配失败
		cerr << "初始化时内存分配错误!" << endl;
		exit(-1);
	}
	_iCurrent = 0;                                //还未包含结点
}

//获取头指针的函数
template<class T>
LinkedNode<T>* SglLinkedList<T>::getHead() {
	return _pFirst;
}

//获取尾结点的地址
template<class T>
LinkedNode<T>* SglLinkedList<T>::getRear() {
	return _pRear;
}

//初始化函数
template<class T>
void SglLinkedList<T>::init(SglLinkedList<T>& listPar) {
	this->_iCurrent = listPar._iCurrent;
	//临时变量,用于暂存结点值
	T tempValue;
	LinkedNode<T>* srcPtr = listPar.getHead();            //原参数变量中的头指针
	LinkedNode<T>* destPtr = _pFirst = new LinkedNode<T>(); //本对象的头指针
	while (srcPtr->_next != nullptr) {                    //当原参数的遍历还未达到末尾时
		tempValue = srcPtr->_next->_data;                 //暂存从参数链表中的结点值
		destPtr->_next = new LinkedNode<T>(tempValue);             //开辟内存分配给结点
		if (destPtr->_next == nullptr) {                  //内存分配错误
			cerr << "内存分配错误!" << endl;
			exit(-1);
		}
		destPtr = destPtr->_next;                        //循环:将destPtr的下一结点赋值给destPtr
		srcPtr = srcPtr->_next;                          //循环:将destPtr的下一结点赋值给destPtr
	}

	//这一个应该可以省略
	//destPtr = nullptr;                               //尾结点的指针指向空 
}

//复制构造函数
template<class T>
SglLinkedList<T>::SglLinkedList(SglLinkedList<T>& listPar) {
	init(listPar);
}

//赋值运算符重载
template<class T>
SglLinkedList<T>& SglLinkedList<T>::operator = (SglLinkedList<T> listPar) {
	init(listPar);
}


//清空链表的函数
template<class T>
void SglLinkedList<T>::makeEmpty() {
	LinkedNode<T>* destPtr = _pFirst->_next;          //不能是LinkedNode<T>* destPtr = _pFirst,
	                                                   //因为如果这样的话,循环之后之后一个元素的_next就是指向空的
	                                                   //会导致这个元素删除不了
	while (destPtr != nullptr) {
		/*delete _pFirst->_next;                       //这种写法是错误的,不能够先删除
		_pFirst = _pFirst->_next;*/
		LinkedNode<T>* tempDel = destPtr;              //临时指向
		destPtr = destPtr->_next;                      //将_pFirst指向_pFirst->_next,用于循环
		delete tempDel;                                //删除tempDel所指向的内存
	}
	
	_pFirst->_next = nullptr;                          //首结点地址为空
	_pRear = _pFirst;                                  //尾结点地址设为空

	//下面这样写是不行的,因为delete之后,该结点中的_next成员已经不存在,就没有办法
	//在下一个循环继续指向
	/*LinkedNode<T>* destPtr = _pFirst;              
											       
	while (destPtr->_next != nullptr) {	 
		LinkedNode<T>* tempDel = destPtr->_next;       
		destPtr = destPtr->_next;                      
		delete tempDel;                                     
	}
	_pFirst->_next = nullptr;*/

	_iCurrent = 0;
}

template<class T>
SglLinkedList<T>::~SglLinkedList()
{
	//直接调用makeEmpty,详情查看makeEmpty()函数
	makeEmpty();
}


//输入函数
//此处的输入不设定输入个数,以endTag作为结束标志
//后插法
template<class T>
void SglLinkedList<T>::input(T endTag) {                
	T tempValue;
	LinkedNode<T>* destPtr = _pFirst;                     //不可写为LinkedNode<T>* destPtr = _pFirst->_next;
	                                                    //因为_pFirst->next为null,这样赋值,
	                                                    //即使destPtr开辟了空间,_pFirst->next仍为null
	cout << "请开始输入结点元素,并以参数作为结点标志:" << endl;
	while (cin >> tempValue) {                          //循环输入
		if (tempValue == endTag)                        //如果遇到结束符,结束输入
			break;
		destPtr->_next = new LinkedNode<T>(tempValue);  //将输入的值建立结点
		if (destPtr == nullptr) {                       //分配内存失败
			cerr << "内存分配错误!" << endl;
			exit(-1);
		}
		_iCurrent++;                                    //建立一个结点,结点数加一
		_pRear = destPtr->_next;
		destPtr = destPtr->_next;                      //开始指向下一结点,循环
	}

	//这个应该可以省略的
	//destPtr = nullptr;                               //将最后一个destPtr指向空,结束输入
}



//输出函数
template<class T>
void SglLinkedList<T>::output() {
	LinkedNode<T>* destPtr = _pFirst->_next;                   //建立一个指针指向头结点
	if (isEmpty()) {
		cout << "链表为空,不能输出!" << endl;
		return;
	}
	while (destPtr != nullptr) {               //当未达到尾结点
		cout << destPtr->_data << " ";         //输出结点数据
		destPtr = destPtr->_next;
	}

	cout << endl;
	return;
}

//追加函数
template<class T>
void SglLinkedList<T>::append(const T data) {        //在末尾插入一个结点
	_pRear->_next = new LinkedNode<T>(data);         //为心结点申请内存
	if (_pRear->_next == nullptr) {                  //如果内存分配失败
		cerr << "内存分配错误!" << endl;
		exit(-1);
	}
	_iCurrent++;                                     //结点数加一
	_pRear = _pRear->_next;                          //尾巴结点指针指向新结点
}                   

//插入函数,pos从1开始
template<class T>
bool SglLinkedList<T>::insert(int pos, const T data) {
	if (pos < 1 || pos > _iCurrent) {                //插入位置错误
		cout << "插入的位置有误,只能插入在1~"<<_iCurrent<<"位置!" << endl;
		return false;
	}
	//遍历到插入的位置
	LinkedNode<T>* destPtr = _pFirst;
	for (int i = 0; i < pos - 1; i++) {
		destPtr = destPtr->_next;
	}
	//为插入位置的结点分配内存
	LinkedNode<T>* insPtr = new LinkedNode<T>(data);
	if (insPtr == nullptr) {
		cerr << "内存分配错误!" << endl;
		exit(-1);
	}
	insPtr->_next = destPtr->_next;               //将待插入的结点的指针指向遍历最后的下一结点
	destPtr->_next = insPtr;                      //遍历最后的下一结点的指针让将待插入结点的位置

	_iCurrent++;                                  //结点数加一,尾指针不变
	return true;
}      

//删除第pos个位置的数据并用data保存起来
//pos从1开始
template<class T>
bool SglLinkedList<T>::remove(int pos, T& data){
	if (pos < 1 || pos > _iCurrent) {                //插入位置错误
		cout << "删除的位置有误,只能在1~" << _iCurrent << "位置删除!" << endl;
		return false;
	}
	//遍历到删除的位置
	LinkedNode<T>* destPtr = _pFirst;
	for (int i = 0; i < pos - 1; i++) {
		destPtr = destPtr->_next;
	}
	//如果删除的是最后一个元素,那么尾指针要变动
	if (pos == _iCurrent) {
		_pRear = destPtr;
	}
	LinkedNode<T>* delPtr = destPtr->_next;
	data = delPtr->_data;                         //将删除的结点值保存起来
	destPtr->_next = delPtr->_next;               //将遍历到最后结点的指针指向被删除元素指向的结点
	delete delPtr;                                //释放被删除的内存

	_iCurrent--;                                  //删除成功后,节点数减1
	return true;
}

//取出pos个位置的数据并用data保存起来
template<class T>
bool SglLinkedList<T>::getItem(const int pos, T& data) const{
	if (pos < 1 || pos > _iCurrent) {                //插入位置错误
		cout << "获取的位置有误,只能获取在1~" << _iCurrent << "位置的数据!" << endl;
		return false;
	}
	//遍历到提取的位置
	LinkedNode<T>* destPtr = _pFirst;
	for (int i = 0; i < pos; i++) {
		destPtr = destPtr->_next;
	}
	data = destPtr->_data;                         //将提取的结点值保存起来
	return true;
}

//搜索含数据x的元素
//返回第一个含数据x的结点的地址
template<class T>
LinkedNode<T>* SglLinkedList<T>::search(const T data) const{
	LinkedNode<T>* destPtr = _pFirst->_next;                   //建立一个新的指针指向首结点
	while (destPtr != nullptr && destPtr->_data != data) {     //当没有循环到末尾并且未找到数据之前一直循环
		destPtr = destPtr->_next;                              //指向下一结点
	}
	//当达到末尾是指向的是空指针,否则指向的是含数据x的结点的地址
	return destPtr;                                            
}



//取出第pos个元素的位置,pos从1开始
template<class T>
LinkedNode<T>* SglLinkedList<T>::locate(const int pos) const{
	if (pos < 1 || pos > _iCurrent) {                //插入位置错误
		cout << "获取的位置有误,只能获取在1~" << _iCurrent << "的位置的地址!" << endl;
		return nullptr;
	}
	//遍历到删除的位置
	LinkedNode<T>* destPtr = _pFirst;
	for (int i = 0; i < pos; i++) {
		destPtr = destPtr->_next;
	}

	return destPtr;
}

//排序函数
//默认为升序
template<class T>
void SglLinkedList<T>::sort(bool asc /*= true*/) {
	if (isEmpty()) {                                //首先判断时候为空,为空不能排序
		cout << "空链表,不能进行排序" << endl;
		return;
	}
	 
	for (LinkedNode<T>* idestPtr = _pFirst->_next;   //第一层循环的初始条件是令一个指针指向首结点
	idestPtr != _pRear;                              //判断条件是达到倒数第二个结点结束
		idestPtr = idestPtr->_next) {                //使当前指针指向下一结点
		for (LinkedNode<T>* jdestPtr = idestPtr->_next; //第二层循环的初始条件使指向第一层循环初始条件的下一结点
		jdestPtr != nullptr;                            //判断条件使是否达到尾指针                      
			jdestPtr = jdestPtr->_next) {              //使第二层循环的当前结点指向下一结点
			if (asc == true) {
				if (idestPtr->_data > jdestPtr->_data) {//如果使升序排序
					T tempValue;
					tempValue = idestPtr->_data;
					idestPtr->_data = jdestPtr->_data;
					jdestPtr->_data = tempValue;
				}
			}
			else {
				if (idestPtr->_data < jdestPtr->_data) {
					T tempValue;
					tempValue = idestPtr->_data;
					idestPtr->_data = jdestPtr->_data;
					jdestPtr->_data = tempValue;
				}
			}
		}
	}
	return;
}

main.cpp文件(测试文件)

#include"SglLinkedList.h"

int main(){
	SglLinkedList<int> test;
	//测试输入函数
	test.input(0);       
	//测试输出函数
	test.output();
	//测试输出长度函数
	cout << test.getLengh() << endl;
	//测试置空函数
	/*test.makeEmpty();
	test.output();
	cout << test.getLengh() << endl;*/
	//测试追加函数
	test.append(99);
	test.output();
	cout << test.getLengh() << endl;
	//测试插入函数
	test.insert(3, 88);
	test.output();
	cout << test.getLengh() << endl;
	//测试删除函数
	int temp;
	if (test.remove(3, temp)) {
		cout << "删除成功,您删除的是:" << temp << endl;
		cout << "删除之后的元素为:" << endl;
		test.output();
	}
	else {
		cout << "删除失败!" << endl;
	}
	//测试获取结点值的函数
	if (test.getItem(3, temp)) {
		cout << "获取成功,您获取的是:" << temp << endl;
	}
	else {
		cout << "获取失败!" << endl;
	}
	//测试寻找函数
	if (test.search(5) != nullptr) {
		cout << test.search(5)->_data << endl;;
	}
	else {
		cout << "未找到包含该数据的结点!" << endl;
	}
	
	//测试获取特定位置结点的位置的函数
	if (test.locate(3) != nullptr) {
		cout << "3的下一个结点值为:" << test.locate(3)->_next->_data << endl;
	}
	 
	//测试排序函数
	test.sort(false);
	cout << "降序排序之后的链表为:" << endl;
	test.output();
	test.sort();
	cout << "升序排序之后的链表为:" << endl;
	test.output();

	//测试赋值构造函数
	cout << "赋值构造函数的测试:" << endl;
	SglLinkedList<int>test2 = test;
	test2.output();
	if (test2.locate(3) != nullptr) {
		cout << "3的下一个结点值为:" << test2.locate(3)->_next->_data << endl;
	}
	
	//测试赋值运算符的重载函数
	SglLinkedList<int>test3 = test2;
	cout << "赋值构造函数的测试:" << endl;
	test3.output();
	if (test3.locate(3) != nullptr) {
		cout << "3的下一个结点值为:" << test3.locate(3)->_next->_data << endl;
	}
	return 0;
}

测试结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值