链表是线性表的一种链式表示,在物理存储中是随机的,在逻辑结构中程线性,单向链表在遍历时,如果需要访问之前的节点则需要从新遍历,而双向链表则可以在任意位置利用前驱指针访问数据。
定义数据结构
#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.没有使用双向链表的前驱指针进行反向遍历
特别感谢:闫同学
在文件操作,删除节点的算法方面的帮助