C++泛型双向链表(简单的学生老师管理系统)

链表是线性表的一种链式表示,在物理存储中是随机的,在逻辑结构中程线性,单向链表在遍历时,如果需要访问之前的节点则需要从新遍历,而双向链表则可以在任意位置利用前驱指针访问数据。

定义数据结构

#include"iostream"
#include"fstream"
#include"Student.h"
using namespace std;
template<typename T>
class template_class
{
private:
	T data;
public:
	template_class<T>* head;//头节点
	template_class<T>* next;//后继指针
	template_class<T>* pior;//前驱指针
	template_class();//构造函数
	~template_class();//析构函数
public:
	void init_list(T data);//添加节点
	void delet_list(T data);//删除节点
	void Change_list(T data);//改变节点数据
	void search_list(T data);//搜索节点
	void print_list();//打印整条链表
	int Check_list(T data);//检查新元素是否已经存在
	void Sort_list();//排序

	//文件操作
	void save();
	void save1();
	void save2();
	void read2();
	void read1();
	void read();
	//运算符重载
public:
	friend istream& operator>>(istream& in, template_class<T>& obj);
	friend ostream& operator<<(ostream& out, template_class<T>& obj);
};

这段代码中的文件操作,是为了存储项目中的学生,老师,校长三个类的数据,暂时我没有好的方法对这段文件操作有所优化,希望大家有什么建议欢迎指出

构造与析构函数

template<typename T>
inline template_class<T>::template_class()
{
	//指针全部赋空
	this->head = NULL;
	this->next = NULL;
	this->pior = NULL;
}

template<typename T>
inline template_class<T>::~template_class()
{
	//如果头节点为空
	if (head == NULL)
	{
		return;
	}

	template_class<T>* pcurrent = head;
	template_class<T>* curr = NULL;
	
	while (pcurrent->next != NULL)
	{
		curr = pcurrent;
		pcurrent = pcurrent->next;
		delete curr;
	}
	//销毁最后的节点
	delete pcurrent;
	pcurrent = NULL;
	curr = NULL;
		
}

 析构函数一定要把空间释放干净,这里while语句没有释放最后一个节点,可以用do while语句进行优化。

双向链表的四种基本操作

创建链表增加节点

template<typename T>
inline void template_class<T>::init_list(T data)
{
	template_class<T>* pcurrent = new template_class<T>;
	pcurrent->data = data;
	if (head == NULL)//如果头指针为空
	{
		head = pcurrent;//将输入的值赋给头指针
	}
	else
	{
		template_class<T>* curr = head;//curr从头节点出发,遍历到最后一个节点
		while (curr->next != NULL)
		{
			curr = curr->next;
		}
		//将最后一个节点的后继指针指向pcurrent
		curr->next = pcurrent;
		//pcurrent的前驱指针指向curr
		pcurrent->pior = curr;
		//pcurent的后继指针赋值为空
		pcurrent->next = NULL;
	}
}

在实现数据结构时我们往往会指定插入的位置,而在项目中为了使用者方便,一般使用头插或尾插法(这里眼熟的时尾插法)

删除节点

template<typename T>
inline void template_class<T>::delet_list(T data)
{
	if (head == NULL)
	{
		cout << "链表为空,无法删除!" << endl;
		return;
	}

	//	分别定义ptr,ptr1指针指向head,nupptr(空指针)
	template_class<T>* pcurrent = head;
	template_class<T>* curr = NULL;

	do
	{
		
		if (pcurrent->data == data)
		{
			//	如果删除头节点
			if (pcurrent == head)
			{
				//	如果只有一个节点
				if (head->next == NULL)
				{
					// 链表只有一个节点
					delete head;
					head = NULL;
				}
				else
				{
					// 链表有多个节点
					template_class<T>* last = head;
					while (last->next != NULL)
					{
						last = last->next;	//	更新尾部指针
					}
					head = head->next; // 更新头部指针
					last->next = NULL; // 更新最后一个节点的 next 指针
					delete pcurrent;
				}
				cout << "删除成功!" << endl;
				system("pause");
				return;
			}
			else
			{
				pcurrent->next->pior = curr;
				curr->next = pcurrent->next;

				delete pcurrent;
				cout << "删除成功!" << endl;
				system("pause");
				return;
			}
		}

		curr = pcurrent;			//	更新其前驱指针
		pcurrent = pcurrent->next;	//	ptr指针后移

	} while (pcurrent->next!=NULL );

	cout << "未找到要删除的节点!" << endl;
	this->print_list();
	
}

这里的删除如此麻烦是因为头节点中有值,可以通过在创建链表时不给头节点赋值来优化,但是也会存在浪费空间的问题,如果数据量庞大可以忽略不计

更改数据(从头遍历找到数据)

template<typename T>
inline void template_class<T>::Change_list(T data)
{
	if (head == NULL)
		return;
	else
	{
		template_class<T>* pcurrent = head;
		do
		{
			if (pcurrent->data == data)
				break;
			else
				pcurrent = pcurrent->next;
		} while (pcurrent->next != NULL);
		T data1;
	
		cout << "请输入你修改后的数据" << endl;
		cin >> data1;
		if (!this->Check_list(data1))//使用check函数查重
		{
			pcurrent->data = data1;
			cout << "修改完成" << endl;
		}
		else
		{ 
			cout << "请勿重复" << endl;
		return;
		}
	}
}

查找数据

template<typename T>
inline void template_class<T>::search_list(T data)
{
	if (head == NULL)
	{
		cout << "没有数据可以查询" << endl;
		return;
	}
	else
	{
		template_class<T>* pcurrent = head;
		while (pcurrent->next != NULL)
		{
			if (pcurrent->data == data)
			{
				break;
			}
			pcurrent = pcurrent->next;
			if (pcurrent->next == NULL && pcurrent->data != data)
			{
				cout << "没有你要找的数据" << endl;
				return;
			}
		}
		cout << pcurrent->data << endl;
	}
}

针对项目进行的优化 

 查重

template<typename T>
inline int template_class<T>::Check_list(T data)
{
	template_class<T>* pcurrent = head;
	do
	{
		if (pcurrent->data == data)//如果找到相应的数据
		{
			cout << "不得重复" << endl;
			return 1;
		}
		pcurrent = pcurrent->next;
	} while(pcurrent->next!=NULL);
	return 0;
}

在添加数据的过程中可以针对数据的某个属性进行查重。

自定义类中的运算符重载(学生类为例)

using namespace std;
class Student
{
private:
	int id;
	string name;
	int score;
public:
	friend istream& operator>>(istream& in, Student &obj);
	friend ostream& operator<<(ostream& out, Student& obj);

	bool operator==(Student& obj2);
	bool operator!=(Student& obj2);
	bool operator<(Student& obj2);
};


istream& operator>>(istream& in, Student& obj)
{
    in >> obj.id;
    in >> obj.name;
    in >> obj.score;
    // TODO: 在此处插入 return 语句
    return in;
}

ostream& operator<<(ostream& out, Student& obj)
{
    out << obj.id << "\t" << obj.name << "\t" << obj.score << endl;
    // TODO: 在此处插入 return 语句
    return out;
}

bool Student::operator==(Student& obj2)
{
    if (this->id == obj2.id)
        return true;
    else
        return false;
}

bool Student::operator!=(Student& obj2)
{
    if (this->id != obj2.id)
        return true;
    else
        return false;
}

bool Student::operator<(Student& obj2)
{
    if (this->score < obj2.score)
        return true;
    else
        return false;
}

 在模板类中用于判断和输入数据时,>>,<<,==,!=,<=,>=运算符仅用于已经定义的简单数据类型,可以通过运算符重载定制操作对象。

文件操作

template<typename T>
inline void template_class<T>::save2()
{
	template_class<T>* pcurrent = head;
	ofstream save_file("Headteacher.txt");
	do
	{
		save_file << pcurrent->data << endl;
		pcurrent = pcurrent->next;
	} while (pcurrent != NULL);

	save_file.close();
}

template<typename T>
inline void template_class<T>::read2()
{
	ifstream read_file("Headteacher.txt");
	if (!read_file.is_open())
	{
		return;
	}

	T tempData; // 创建临时变量存储数据
	while (read_file >> tempData) // 只要能读到数据就会返回bool类型的true
	{
		this->init_list(tempData); // 调用函数将数据添加到链表中
	}
	read_file.close();
}

 主函数

#define _CRT_SECURE_NO_WARNINGS
#include"Student.h"
#include"teacher.h"
#include"Head_teacher.h"
#include"template_class.h"
#include"stdlib.h"
#include<stdlib.h>
#include<stdio.h>
#include"windows.h"

template_class<Student> L;
template_class<teacher>Tea_L;
template_class<Head_teacher>Head_L;


void menu1()
{
	cout << "\t\t\t\t\t\t" << "******************************" << endl;
	cout << "\t\t\t\t\t\t" << "**********  1.查询学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  2.打印整条链表****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  3.按照成绩排序学生" << endl;
	cout << "\t\t\t\t\t\t" << "******************************" << endl;
}

void menu2()
{
	cout << "\t\t\t\t\t\t" << "******************************" << endl;
	cout << "\t\t\t\t\t\t" << "**********  1.增加学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  2.删除学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  3.改变学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  4.查询学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  5.打印整条链表****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  6.按照成绩排序学生" << endl;
	cout << "\t\t\t\t\t\t" << "******************************" << endl;
}

void menu3()
{
	cout << "\t\t\t\t\t\t" << "******************************" << endl;
	cout << "\t\t\t\t\t\t" << "**********  1.增加学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  2.删除学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  3.改变学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  4.查询学生信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  5.打印整条链表****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  6.按照成绩排序学生" << endl;
	cout << "\t\t\t\t\t\t" << "**********  7.增加教师信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  8.删除教师信息****" << endl;
	cout << "\t\t\t\t\t\t" << "**********  9.改变教师信息****" << endl;
	cout << "\t\t\t\t\t\t" << "********** 10.查询教师信息****" << endl;
	cout << "\t\t\t\t\t\t" << "********** 11.打印所有老师****" << endl;
	cout << "\t\t\t\t\t\t" << "******************************" << endl;
}


void main1()
{
	cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
	int i;
	char m[300];
	memset(m, 0, 300);
	for (i = 0; i < 101; i++) {
		sprintf(&m[0], "%3d", i);
		m[3] = '%';
		m[4 + i] = '=';
		printf("\r%s>", m);
		fflush(stdout);
		Sleep(10);
	}
	printf("\n");
	cout << "\t\t\t\t\t" << "欢迎同学进入" << endl;
	system("pause");
	system("cls");
	L.read();
	while (true)
	{
		menu1();
		int y;
		cout << "请输入你的选择" << endl;
		cin >> y;
		switch (y)
		{
		case 1:
		{
			Student a;
			cout << "请输入你要查询的学生的信息" << endl;
			cin >> a;
			L.search_list(a);
		}break;
		case 2:
		{
			L.print_list();
		}break;
		case 3:
		{
			L.Sort_list();
		}break;
		}

		system("pause");
		system("cls");
	}
}

void main2()
{

	cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
	int i;
	char m[300];
	memset(m, 0, 300);
	for (i = 0; i < 101; i++) {
		sprintf(&m[0], "%3d", i);
		m[3] = '%';
		m[4 + i] = '=';
		printf("\r%s>", m);
		fflush(stdout);
		Sleep(10);
	}
	printf("\n");
	cout << "\t\t\t\t\t" << "欢迎老师进入" << endl;
	system("pause");
	system("cls");
	L.read();

	while (true)
	{
		menu2();
		int x;
		cout << "请输入你的选择" << endl;
		cin >> x;
		switch (x)
		{
		case 1:
		{
			Student data;
			cout << "请输入你要添加的学生信息" << endl;
			cin >> data;
			if (!L.Check_list(data))
			{
				L.init_list(data);
				L.save();
			}
			else
				continue;
		}break;

		case 2:
		{
			Student dat;
			cout << "请输入你要删除的学生信息" << endl;
			cin >> dat;
			L.delet_list(dat);
			L.save();
		}break;
		case 3:
		{
			Student da;
			cout << "请输入你要更改的学生的信息" << endl;
			cin >> da;
			L.Change_list(da);
			L.save();
		}break;
		case 4:
		{
			Student d;
			cout << "请输入你要查询的学生的信息" << endl;
			cin >> d;
			L.search_list(d);
		}break;
		case 5:
		{
			L.print_list();
		}break;
		case 6:
		{
			L.Sort_list();
		}break;
		}

		
		system("pause");
		system("cls");
	}
	
}

void main3()
{
	cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
	int i;
	char m[300];
	memset(m, 0, 300);
	for (i = 0; i < 101; i++) {
		sprintf(&m[0], "%3d", i);
		m[3] = '%';
		m[4 + i] = '=';
		printf("\r%s>", m);
		fflush(stdout);
		Sleep(10);
	}
	printf("\n");
	cout << "\t\t\t\t\t" << "欢迎校长进入" << endl;
	system("pause");
	system("cls");
	L.read();
	Tea_L.read1();

	while (true)
	{
		menu3();
		int x;
		cout << "请输入你的选择" << endl;
		cin >> x;
		switch (x)
		{
		case 1:
		{
			Student data;
			cout << "请输入你要添加的学生信息" << endl;
			cin >> data;
			if (!L.Check_list(data))
			{
				L.init_list(data);
				L.save();
			}
			else
				continue;
		}break;

		case 2:
		{
			Student dat;
			cout << "请输入你要删除的学生信息" << endl;
			cin >> dat;
			L.delet_list(dat);
			L.save();
		}break;
		case 3:
		{
			Student da;
			cout << "请输入你要更改的学生的信息" << endl;
			cin >> da;
			L.Change_list(da);
			L.save();
		}break;
		case 4:
		{
			Student d;
			cout << "请输入你要查询的学生的信息" << endl;
			cin >> d;
			L.search_list(d);
		}break;
		case 5:
		{
			L.print_list();
		}break;
		case 6:
		{
			L.Sort_list();
		}break;
		case 7:
		{
			teacher b;
			cout << "请输入要添加的老师的信息" << endl;
			cin >> b;
			Tea_L.init_list(b);
			Tea_L.save1();
		}break;
		case 8:
		{
			teacher c;
			cout << "请输入你要删除的老师的信息" << endl;
			cin >> c;
			Tea_L.delet_list(c);
			Tea_L.save1();
		}
		case 9:
		{
			teacher d;
			cout << "请输入你要修改的教师的信息" << endl;
			cin >> d;
			Tea_L.Change_list(d);
			Tea_L.save1();
		}break;
		case 10:
		{
			teacher f;
			cout << "请输入你要查询的教师的信息" << endl;
			cin >> f;
			Tea_L.search_list(f);
		}
		case 11:
		{
			Tea_L.print_list();
		}break;
		}


		system("pause");
		system("cls");
	}

}





int main()
{
	while(true)
	{
	cout << "\t\t\t\t\t\t" << "1学生  2老师  3校长" << endl;
	int h;
	cout << "请输入你的选择" << endl;
	cin >> h;

		switch (h)
		{
		case 1:main1(); break;
		case 2:main2(); break;
		case 3:main3(); break;

		}
	}
}

程序运行进入过程中有 进度条 时别的大神做的直接搬运过来。

运行展示

 

这里的查找因为运算符重载只要求学号所以只吧学号输对就行

 文件中的数据

总结:这个项目还存在很多不足

1.在查询数据时还需要将数据全部输入

2.没有登陆系统,Headteacher类的属性没有利用上

3.没有使用双向链表的前驱指针进行反向遍历

特别感谢:闫同学

在文件操作,删除节点的算法方面的帮助

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值