数据结构课程设计题目——链表综合算法设计、带头双向循环链表、插入、显示、删除、修改、排序

news2024/11/19 19:39:06

文章目录

  • 链表综合算法设计——校园人事信息系统
    • 1.要求
    • 2.代码实现(以带头双向循环链表为例)
      • 2.1基本程序结构
      • 2.2节点和链表的初始化
      • 2.3链表的插入
      • 2.4链表的显示
      • 2.5链表的删除
      • 2.6链表的修改
      • 2.7链表的排序(仿函数实现)
    • 3.全部源码

链表综合算法设计——校园人事信息系统

1.要求

  课程设计题目1–链表综合算法设计

  一、设计内容

  已知简单的人事信息系统中职工记录包含职工编号(no)、职工姓名(name)、部门名称(depname)、职称(title)和工资数(salary)等信息(可以增加其他信息),设计并完成一个简单的人事信息管理系统,要求完成但不限于以下功能:
  (1) 增加一个职工信息;
  (2) 显示所有职工信息;
  (3) 按部门名称分类显示该部门所有职工信息;
  (4) 按部门显示各部门职工工资总额;
  (5) 删除职工信息(可以删除符合条件的一批记录)
  (6) 按职称调整工资;
  (7) 可自行增加功能(例如按职工编号排序等其他功能)

  二、设计要求

  以菜单形式显示,主菜单包括(可以扩展)(1:增加职工信息 2:删除员工信息 3:查找职工信息 4:工资调整 5:统计分析 0:退出)相应的主菜单可以设计子菜单。

            

2.代码实现(以带头双向循环链表为例)

  除了mian函数和system_start函数外,其他的函数都默认在类中实现

2.1基本程序结构

  这段代码是一个简单的C++程序,主要包含一个菜单的系统,用于处理输入信息。

  下面是代码的详细解释:

  void system_start(): 这是一个函数,用于启动菜单的程序。

  int main(): 这是程序的主函数。

  system_start 程序开始的函数:

  定义了两个整数变量 option 和 flag。option 用于存储用户的选择,而 flag 用于标记是否退出系统。在创建了链表之后,我们就进入一个无限循环,直到用户选择退出。调用 Init_menu() 函数来初始化菜单。

  接着提示用户输入选项。使用 switch 语句根据用户的选择执行相应的操作。每个 case 对应于一个特定的操作。如果用户选择0,flag 被设置为1,表示用户希望退出系统。检查 flag 是否为1。如果是,打印退出成功的消息并跳出循环。

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

#include"LinkList.h"

void system_start()
{
	int option = 0, flag = 0;
	staff_linklist::LinkList_Information lt;
	while(1)
	{
		//system("cls");
		Init_menu();
		cout << "请输入:";
		if (cin >> option);
		switch (option)
		{
		case(1):
			//lt.push_back();
			break;
		case(2):
			//lt.Print_Information();
			break;
		case(3):
			//lt.Print_Depname_Information();
			break;
		case(4):
			//lt.Print_Depname_Salary();
			break;
		case(5):
			//lt.Erase_Person_Information();
			break;
		case(6):
			//lt.Erase_All_Informations();
			break;
		case(7):
			//lt.Adjust_Person_Salary();
			break;
		case(8):
			//lt.Print_Person_No_Information();
			break;
		case(9):
			//lt.Print_Person_Salary_Information();
			break;
		case(0):
			flag = 1;
			break;
		default:
			cout << "输入错误,请重新输入\n";
			break;
		}
		if (flag == 1)
		{
			cout << "退出成功\n";
			break;
		}
	} 
}

int main()
{
	system_start();
	return 0;
}

  根据自己的喜好设计打印菜单即可:

//打印菜单
void Init_menu()
{
	printf("————————————————————————————————————————\n");
	printf("|         校园人事信息系统菜单         |\n");
	printf("|   ————————————————————————————————   |\n");
	printf("|       输入1:增加一个职工信息        |\n");
	printf("|       输入2:显示所有职工信息        |\n");
	printf("|       输入3:按部门分类显示信息      |\n");
	printf("|       输入4:按部门工资显示信息      |\n");
	printf("|       输入5:删除职工信息            |\n");
	printf("|       输入6:删除所有职工信息        |\n");
	printf("|       输入7:按职称调整工资          |\n");
	printf("|       输入8:按职工编号排序          |\n");
	printf("|       输入9:按职工工资排序          |\n");
	printf("|       输入0:退出系统                |\n");
	printf("————————————————————————————————————————\n");
}

            

2.2节点和链表的初始化

  创建链表节点并且实现节点的构造函数:

  根据上面提供的题目,我们创建一个结构体用于表示一个职工的信息。这个结构体包含了一些数据成员,职工编号、姓名、性别、部门、职称、工资,以及两个指针,指向职工信息链表中的前一个和后一个节点,用于双向链表的连接。

  注意:构造函数中使用了memcpy函数来初始化_name和_sex成员。memcpy函数用于从源地址复制指定数量的字节到目标地址。当使用memcpy来复制字符串时,且必须确保目标地址有足够的空间来存放源地址的字符串,并且源地址的字符串是以null字符(\0)结尾的。

  这里为Staff_Information_Node结构体定义一个构造函数有很多好处:

  (1)初始化成员变量:构造函数允许你在创建结构体对象时,直接为成员变量赋予初始值。这样,你就不需要在创建对象后再逐一设置每个成员变量的值,使代码更简洁。

  (2)提高代码的可读性和可维护性,保证成员变量的正确初始化:通过构造函数,可以清晰地看到在创建对象时需要哪些信息,以及这些信息是如何被用来初始化成员变量的。如果没有构造函数,那么成员变量的初始值将是未定义的,这可能会导致程序行为不确定

  (3)提供类型检查和安全性,也便于扩展和修改:在构造函数中,你可以对传入的参数进行类型检查,以确保它们是正确的类型。如果需要在将来对结构体进行扩展或修改,那么构造函数可以更容易地进行相应的调整。

//职工信息节点
typedef struct Staff_Information_Node
{
	size_t _no;       //编号
	char _name[100];  //姓名
	char _sex[10];    //性别
	string _depname;  //部门
	string _title;    //职称
	size_t _salary;   //工资
	struct Staff_Information_Node* _prev;//前驱节点
	struct Staff_Information_Node* _next;//后继节点

	//职工信息节点的构造函数    初始化列表
	Staff_Information_Node(size_t no, const char* name, const char* sex,
		const string& depname, const string& title, size_t salary,
		Staff_Information_Node* prev = nullptr, Staff_Information_Node* next = nullptr)
		:_no(no)
		, _depname(depname)
		, _title(title)
		, _salary(salary)
		, _next(next)
	{
		memcpy(_name, name, 20);//用内存函数初始化数组,实现深拷贝
		memcpy(_sex, sex, 10);
	}
}SNode;

  
创建链表并且实现链表的构造函数和析构函数:

  这里是一个C++的链表类定义,其中使用了之前定义的Staff_Information_Node结构体。这个链表类包含了一个私有成员变量_head,表示链表的头结点,以及一个_size成员变量,表示链表的大小。

  在这个构造函数中,你创建了一个新的SNode对象作为头结点,上面写的SNode的构造函数就可以直接对头结点进行初始化了,同时初始化了成员变量size。 然后,你将头结点的_prev和_next指针都指向了自身,形成一个循环链表。

  在析构函数中,首先将_size成员变量设置为0,然后遍历链表,逐个删除链表中的元素,并释放它们所占用的内存空间。 最后删除了头结点,释放了它所占用的内存空间,并将_head指针设置为nullptr,以确保头结点不再占用任何内存空间。

  这样子析构函数就可以能够正确地释放链表中的所有节点和头结点的内存空间,避免内存泄漏的问题。

namespace staff_linklist
{
	//链表类
	class LinkList_Information
	{
	public:
		//构造函数
		LinkList_Information()
		{
			_head = new SNode(0, "头结点", "000", "000", "000", 000);
			//创建初始化头结点
			_head->_prev = _head;
			_head->_next = _head;

			_size=0;
		}	

		//析构函数  
		~LinkList_Information()
		{
			SNode* cur = _head->_next;
			_size = 0;

			//挨个删除链表中的元素,释放空间,避免内存泄漏
			while (cur != _head)
			{
				SNode* tmp = cur;
				cur = cur->_next;

				delete tmp;
				tmp = nullptr;
			}

			delete _head; // 释放头结点的内存  
			_head = nullptr;
		}
		
private:
		SNode* _head;//头结点、哨兵位
		size_t _size;
	};
}

            

2.3链表的插入

实现链表节点的创建和插入:

  Create_SNode函数用于创建一个新的SNode对象。 首先,它提示用户输入一些信息,并使用cin从标准输入流( while (!(cin >> no >> name >> sex >> depname >> title >> salary)))中读取这些信息。如果输入失败,它会清除cin的错误状态,并忽略输入流中的一行,然后输出一个错误消息,并递归调用自身以重新输入。使用输入的信息创建一个新的SNode对象,并返回该对象的指针。

  push_back函数用于在链表的尾部插入一个新的节点。 首先,它调用Create_SNode函数创建一个新的节点。如果创建节点失败,它会直接返回。接着,它找到链表的尾节点,并将新节点连接到尾节点和头结点之间。最后,它增加链表的大小,并输出一个成功消息。

//创建一个新的节点
SNode* Create_SNode()
{
	size_t no, salary;
	char name[100], sex[10];
	string depname, title;
	cout << "请输入编号、姓名、性别、部门、职称、工资" << endl;

	//如果使用cin想要接收char类型的变量
	//但是却使用了int类型的变量来接收,这可能会导致死循环
	while (!(cin >> no >> name >> sex >> depname >> title >> salary))
	{
		cin.clear();
		cin.ignore(256, '\n'); // ignore to next line  
		cerr << "输入失败,请重新输入:" << endl;
	}

	//创建新节点
	SNode* newnode = new SNode(no, name, sex, depname, title, salary);
	if (newnode == nullptr)
	{
		perror("创建节点失败\n");
		return nullptr;
	}

	return newnode;
}

//尾插
void push_back()
{
	SNode* newnode = Create_SNode();//创建节点
	if (newnode == nullptr)
	{
		return;
	}

	//找到尾结点
	SNode* tail = _head->_prev;

	//连接尾结点
	tail->_next = newnode;
	newnode->_prev = tail;

	//链接头结点
	_head->_prev = newnode;
	newnode->_next = _head;

	_size++;

	cout << "添加成功\n";
}

一个小优化:

  为了防止之后的按部门打印中,因为没有对应的部门而导致打印的信息变少。我们可以强制检查,输入的信息的部门是否存在且合理。

  使用vector存储所有的部门,这里可以使用static修饰,将数据存放在静态区中,减少程序运行消耗。

static vector<string> Depname={ "教务处","学生处","人事处","总务处","纪检部"};
static int Depname_nember = Depname.size();

  我们使用了一个名为Depname的数组来存储有效的部门名称,并使用一个名为Depname_nember的变量来存储数组的大小。然后,使用c_str()函数将输入的部门名称字符串depname转换为C风格字符串,并与Depname数组中的每个元素进行比较。 这样就可以检查是否符合输入的部门信息。

  注意:使用了(depname).c_str()来将depname字符串转换为C风格字符串,然后与Depname[i]进行比较。可能是不安全的,因为c_str()函数返回一个指向内部缓冲区的指针,而这个缓冲区可能在后续操作中被修改或销毁。 直接比较即可 if (Depname[i] == depname)

//检查,如果输入没有该部门,就重新输入
for (int i = 0; i < Depname_nember; i++)
{
	if (Depname[i] == (depname).c_str())
	{
		break;
	}
	
	//if (i == Depname_nember - 1)
	if (Depname[i] == depname)
	{
		cout << "没有该部门,输入失败,请重新输入:\n";
		return Create_SNode();
	}
}

            

2.4链表的显示

直接打印链表中的成员:

  Print_Person_Information函数用于打印单个节点的个人信息。 为了排列整齐,我们使用了printf函数来格式化输出,其中%-6d、%-6s、%-6s、%-8s、%-10s和%-5d分别表示输出的字段宽度和对齐方式。它使用了(cur->_depname).c_str()和(cur->_title).c_str()来将std::string类型的部门名称和职称转换为C风格字符串,以便与printf函数一起使用。

  Print_Information函数用于打印整个链表中的个人信息。 首先检查链表是否为空,如果为空,则输出一条消息并直接返回。否则,它输出表头和分隔线,然后遍历链表中的每个节点,并调用Print_Person_Information函数来打印每个节点的个人信息。最后,它输出分隔线。

  这里我们将Print_Person_Information和Print_Information函数分离,通过将打印个人信息的功能封装在Print_Person_Information函数中,我们就可以在其他地方重用它,而无需重复编写相同的代码。这有助于减少重复并降低出错的可能性。而且通过将打印功能分离为两个函数,我们可以更灵活地处理不同的打印需求。

//打印个人信息
void Print_Person_Information(SNode* cur)
{
	printf("%-6d %-6s %-6s %-8s %-10s %-5d\n", cur->_no, cur->_name,
		cur->_sex, (cur->_depname).c_str(), (cur->_title).c_str(), cur->_salary);
}

//打印函数
void Print_Information()
{
	SNode* cur = _head->_next;

	if (cur == _head)
	{
		cout << "该系统中信息为空\n";
		return;
	}

	cout << "人事信息系统人员名单\n";
	printf("———————————————————————————————————-----——-———\n");
	printf("编号   姓名   性别   部门     职称       工资\n");
	while (cur != _head)
	{
		Print_Person_Information(cur);
		cur = cur->_next;
	}
	printf("——————————————————————————————————————------——\n");

}

  
按部门打印链表中的成员:

  循环逻辑和上面的代码一样,只需要再添加一层循环,分别打印各自的部门即可。

  外层循环遍历Depname向量中的每个部门名称。在每次外层循环迭代中,内层循环遍历链表中的每个节点。

  对于链表中的每个节点,通过比较节点的部门名称与当前外层循环中的部门名称,判断节点是否属于当前部门。如果节点属于当前部门,则调用Print_Person_Information函数打印该节点的个人信息。打印完一个部门的信息后,输出两个空行以分隔不同部门的信息。

static vector<string> Depname={ "教务处","学生处","人事处","总务处","纪检部"};
static int Depname_nember = Depname.size();
for (int i = 0; i < Depname_nember; i++)
{
	//双重循环,依次打印各部门的信息
	SNode* _cur = _head->_next;
	printf("\t\t%s:\n", (Depname[i]).c_str());
	while (_cur != _head)
	{
		//判断部门
		if (_cur->_depname == Depname[i])
		{
			Print_Person_Information(_cur);
		}

		_cur = _cur->_next;
	}
	cout << endl << endl;
}

  
按部门打印链表中的成员和工资总和:

  这段代码和前面的代码逻辑也是一样的,在前面的基础上增加了统计每个部门的工资总额和所有部门的工资总额的功能。

  我们增加了计数器money和sum,依次来统计不同部门工资的总额和所有部门的工资总额。在外层循环中,定义了一个sum变量用于记录所有部门的工资总额。在内层循环中,定义了一个money变量用于记录当前部门的工资总额。

  在判断节点是否属于当前部门后,除了打印该节点的个人信息,还将节点的工资累加到money变量中。内层循环结束后,将money变量累加到sum变量中,并输出当前部门的工资总额。外层循环结束后,输出所有部门的工资总额。

static vector<string> Depname={ "教务处","学生处","人事处","总务处","纪检部"};
static int Depname_nember = Depname.size();
int sum = 0;
for (int i = 0; i < Depname_nember; i++)
{
	SNode* _cur = _head->_next;
	int money = 0;
	printf("\t\t%s:\n", (Depname[i]).c_str());
	while (_cur != _head)
	{
		if (_cur->_depname == Depname[i])
		{
			Print_Person_Information(_cur);
			money += _cur->_salary;
		}

		_cur = _cur->_next;
	}
	sum += money;
	printf("%s工资总计:%d\n", (Depname[i]).c_str(), money);
	cout << endl << endl;
}
printf("所有部门工资统计:%d\n", sum);

            

2.5链表的删除

链表中单个节点的删除:

  这段代码实现了通过用户输入的编号来删除节点。 删除操作和之前链表删除的逻辑类似,先查找再删除。我们定义了两个指针prev和cur,分别指向链表的头部和第一个节点。如果链表为空,函数输出一条消息并直接返回。否则,函数遍历链表,查找编号与输入编号匹配的节点。

  如果找到匹配的节点,函数执行双向循环链表删除操作,将前一个节点的_next指针指向当前节点的下一个节点,并将下一个节点的_prev指针指向当前节点的前一个节点。

  函数释放当前节点的内存,并将cur指针更新为前一个节点的下一个节点。如果遍历完整个链表都没有找到匹配的节点,函数输出一条消息表示删除失败。最后,函数输出一条消息表示删除成功或失败。

  注意:这里在输入之前进行了这个操作:cin.ignore(256, '\n');

  这段代码的作用是忽略输入流中的字符,直到遇到换行符(\n)为止,最多忽略256个字符。

  在C++中,cin.ignore()函数用于从输入流中忽略(即丢弃)一个或多个字符。它接受两个参数:第一个参数是要忽略的最大字符数,第二个参数是终止字符。在这个例子中,256是要忽略的最大字符数,\n是终止字符。

  这段代码通常用于清除输入缓冲区中的残留字符,例如在读取用户输入之前清除上一次输入留下的换行符或其他无关字符。这样可以确保接下来的输入操作不会受到这些残留字符的干扰。

//删除个人信息
void Erase_Person_Information()
{
	cin.ignore(256, '\n');
	cout << "请输入要删除人的编号:\n";
	int num = 0,flag = -1;
	cin >> num;

	SNode* prev = _head;
	SNode* cur = _head->_next;

	if (cur == _head)
	{
		cout << "该系统中信息为空,无法删除\n";
		return;
	}

	while (cur != _head)
	{
		if (cur->_no == num)
		{
			SNode* tmp = cur;
			flag = 1;

			//双向循环链表删除操作
			cur->_next->_prev = prev;
			prev->_next = cur->_next;

			cur = prev->_next;

			//string类型的成员变量是单个对象,而不是数组,
			//所以应该使用delete来释放它们的内存。
			//如果使用delete[]来释放string对象的内存,会导致未定义的行为和可能的内存泄漏。
			delete tmp;
			tmp = nullptr;
		}
		else
		{
			//遍历后面元素
			prev = prev->_next;
			cur = cur->_next;
		}
	}
	if (flag==1) cout << "删除成功\n";
	else cout << "删除失败,没有此人\n";
}

  
链表所有节点的删除:

  实现和析构函数类似:

  函数先检查链表是否为空。如果链表为空,函数输出一条消息并直接返回。如果链表不为空,函数将头节点的_next指针指向自身,将头节点的_prev指针指向自身,以便在删除过程中保持链表的完整性。然后,我们遍历链表,逐个删除节点并释放其内存,以避免内存泄漏。 最后,函数输出一条消息表示删除成功。

  和析构函数不同是是,我们再这里添加一个给用户输入二次确认的机会,防止误删操作,函数首先提示用户确认是否要删除所有信息,并输入Y以确认。如果用户输入的不是Y,函数输出一条消息并直接返回。

//删除所有信息
void Erase_All_Informations()
{
	cout << "是否要删除所有信息!\n删除请输入[Y]\n";
	char ch;
	cin.ignore(256, '\n');
	cin >> ch;
	if (ch != 'Y')
	{
		cout << "删除失败\n";
		return;
	}

	SNode* cur = _head->_next;

	if (cur == _head)
	{
		cout << "该系统中信息为空\n";
		return;
	}

	_head->_next = _head;
	_head->_prev = _head;

	//挨个删除链表中的元素,释放空间,避免内存泄漏
	while (cur != _head)
	{
		SNode* tmp = cur;
		cur = cur->_next;

		delete tmp;
		tmp = nullptr;
	}

	cout << "删除成功\n";
}

            

2.6链表的修改

按编号查找并且修改工资:

  修改的实现逻辑和查找逻辑完全一样,注意一些细节即可。

  函数首先提示用户输入要调整工资的节点的编号。然后,函数提示用户输入调整后的工资金额。函数定义了一个指针cur,指向链表的第一个节点,并定义了一个布尔变量flag,用于记录是否找到了要调整的节点。

  接着,我们遍历链表,查找编号与输入编号匹配的节点。如果找到匹配的节点,函数将节点的工资更新为输入的工资,并将flag设置为true。如果遍历完整个链表都没有找到匹配的节点,函数输出一条消息表示没有找到该节点。最后,函数输出一条消息表示调整成功或失败。

//调整工资
void Adjust_Person_Salary()
{
	cin.ignore(256, '\n');
	cout << "请输入要调整工资的人的编号:\n";
	int num = 0;
	cin >> num;

	cin.ignore(256, '\n');
	cout << "请输入调整后的工资金额:\n";
	int sal = 0;
	cin >> sal;

	SNode* cur = _head->_next;
	bool flag = false;

	//寻找需要更改信息的节点
	while (cur != _head)
	{
		if (cur->_no == num)
		{
			flag = true;
			cur->_salary = sal;
		}

		cur = cur->_next;
	}

	if (flag) cout << "修改成功\n";
	else cout << "没有此人\n";
}

            

2.7链表的排序(仿函数实现)

  链表的排序可以使用很多的排序算法,但这里我们实现vector+sort+仿函数实现不同类型的排序功能。

  C++STL库中提供了很多的容器和算法,我们可以尝试将自定义类型SNode放入vector中,用vector的迭代器进行sort排序:

  函数我们创建了一个空的vector< SNode >,用于存储链表中的节点信息。然后函数遍历链表中的每个节点,将节点的信息复制到vector中。函数使用sort算法对vector进行排序。

//按编号排序
void Print_Person_No_Information()
{
	vector<SNode> v;
	SNode* cur = _head->_next;
	while (cur != _head)
	{
		v.push_back(*cur);
		cur = cur->_next;
	}
	sort(v.begin(), v.end());
	_sort_print(v);
}

  那么就有问题来了,vector中的元素是自定义类型SNode,不是内置类型,不可以使用sort直接进行排序,因为sort不知道我们对于SNode排序的依据是什么,所以我们引入仿函数来解决这个问题。

  
  什么是仿函数?

  仿函数(functor),就是行为类似函数的对象,即它们是具有类似函数调用操作的类的对象。C++的仿函数(functors)是函数对象(functionobjects),它们是重载了“()”运算符的类的对象。由于重载了“()”运算符,因此它们的使用形式看起来就像普通函数一样。

  
  我们定义了一个名为Compare_No的结构体,并在其中重载了operator()操作符。 重载的operator()操作符接受两个Staff_Information_Node类型的常量引用作为参数,并返回一个布尔值。操作符的比较逻辑是比较两个节点的编号,如果第一个节点的编号小于第二个节点的编号,则返回true,否则返回false。

struct Compare_No
{
	//重载operator()操作符以实现函数比较操作
	bool operator()(const Staff_Information_Node& p1,
	 const Staff_Information_Node& p2)
	{
		return p1._no < p2._no;
	}
};

  
排序的依据是Compare_No仿函数:

sort(v.begin(), v.end(), Compare_No());

最后,函数调用_sort_print函数打印出排序后的人员名单。

//打印排序结果(vector)
void _sort_print(vector<SNode>& v)
{
	int len = v.size();
	if (len == 0)
	{
		cout << "该系统中信息为空\n";
		return;
	}

	cout << "人事信息系统人员名单\n";
	printf("———————————————————————————————————-----——-———\n");
	printf("编号   姓名   性别   部门     职称       工资\n");
	for (int i = 0; i < len; i++)
	{
		Print_Person_Information(&v[i]);
	}
	printf("——————————————————————————————————————------——\n");
}

            

3.全部源码

测试结果:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
            

LinkList.h

#pragma once

//<Simple Campus Personnel Information System>
//<This system is based on data structure-leading bidirectional loop linked list>
//<Crocodile Sweet Potato Balls>

#include<assert.h>
#include<string>
#include<vector>
#include<algorithm>
#include<list>

static vector<string> Depname={ "教务处","学生处","人事处","总务处","纪检部"};
static int Depname_nember = Depname.size();

//打印菜单
void Init_menu()
{
	printf("————————————————————————————————————————\n");
	printf("|         校园人事信息系统菜单         |\n");
	printf("|   ————————————————————————————————   |\n");
	printf("|       输入1:增加一个职工信息        |\n");
	printf("|       输入2:显示所有职工信息        |\n");
	printf("|       输入3:按部门分类显示信息      |\n");
	printf("|       输入4:按部门工资显示信息      |\n");
	printf("|       输入5:删除职工信息            |\n");
	printf("|       输入6:删除所有职工信息        |\n");
	printf("|       输入7:按编号调整工资          |\n");
	printf("|       输入8:按职工编号排序          |\n");
	printf("|       输入9:按职工工资排序          |\n");
	printf("|       输入0:退出系统                |\n");
	printf("————————————————————————————————————————\n");
}

//职工信息节点
typedef struct Staff_Information_Node
{
	size_t _no;       //编号
	char _name[100];  //姓名
	char _sex[10];    //性别
	string _depname;  //部门
	string _title;    //职称
	size_t _salary;   //工资
	struct Staff_Information_Node* _prev;//前驱节点
	struct Staff_Information_Node* _next;//后继节点

	//职工信息节点的构造函数    初始化列表
	Staff_Information_Node(size_t no, const char* name, const char* sex,
		const string& depname, const string& title, size_t salary,
		Staff_Information_Node* prev = nullptr, Staff_Information_Node* next = nullptr)
		:_no(no)
		, _depname(depname)
		, _title(title)
		, _salary(salary)
		, _next(next)
	{
		//assert (typeid(_no).name() == typeid(no).name());
		memcpy(_name, name, 100);//用内存函数初始化数组,实现深拷贝
		memcpy(_sex, sex, 10);
	}
}SNode;

namespace staff_linklist
{
	//链表类
	class LinkList_Information
	{
	public:
		//构造函数
		LinkList_Information()
		{
			_head = new SNode(0, "头结点", "000", "000", "000", 000);//初始化头结点
			_head->_prev = _head;
			_head->_next = _head;

			_size = 0;
		}

		//析构函数  
		~LinkList_Information()
		{
			SNode* cur = _head->_next;
			_size = 0;

			//挨个删除链表中的元素,释放空间,避免内存泄漏
			while (cur != _head)
			{
				SNode* tmp = cur;
				cur = cur->_next;

				delete tmp;
				tmp = nullptr;
			}

			delete _head; // 释放头结点的内存  
			_head = nullptr;
		}

		//创建一个新的节点
		SNode* Create_SNode()
		{
			size_t no, salary;
			char name[100], sex[10];
			string depname, title;
			cout << "请输入编号、姓名、性别、部门、职称、工资" << endl;

			//如果使用cin想要接收char类型的变量,但是却使用了int类型的变量来接收,这可能会导致死循环
			while (!(cin >> no >> name >> sex >> depname >> title >> salary))
			{
				cin.clear();
				cin.ignore(256, '\n'); // ignore to next line  
				cerr << "输入失败,请重新输入:" << endl;
				//return nullptr;
			}

			//检查,如果输入没有该部门,就重新输入
			for (int i = 0; i < Depname_nember; i++)
			{
				//if (Depname[i] == (depname).c_str())
				if (Depname[i] == depname)
				{
					break;
				}

				if (i == Depname_nember - 1)
				{
					cout << "没有该部门,输入失败,请重新输入:\n";
					return Create_SNode();
				}
			}

			//创建新节点
			SNode* newnode = new SNode(no, name, sex, depname, title, salary);
			if (newnode == nullptr)
			{
				perror("创建节点失败\n");
				return nullptr;
			}

			return newnode;
		}

		//尾插
		void push_back()
		{
			SNode* newnode = Create_SNode();//创建节点
			if (newnode == nullptr)
			{
				perror("创建节点失败\n");
				return;
			}

			//找到尾结点
			SNode* tail = _head->_prev;

			//连接尾结点
			tail->_next = newnode;
			newnode->_prev = tail;

			//链接头结点
			_head->_prev = newnode;
			newnode->_next = _head;

			_size++;

			cout << "添加成功\n";
		}

		//打印个人信息
		void Print_Person_Information(SNode* cur)
		{
			printf("%-6d %-6s %-6s %-8s %-10s %-5d\n", cur->_no, cur->_name,
				cur->_sex, (cur->_depname).c_str(), (cur->_title).c_str(), cur->_salary);
		}

		//打印函数
		void Print_Information()
		{
			SNode* cur = _head->_next;

			if (cur == _head)
			{
				cout << "该系统中信息为空\n";
				return;
			}

			cout << "人事信息系统人员名单\n";
			printf("———————————————————————————————————-----——-———\n");
			printf("编号   姓名   性别   部门     职称       工资\n");
			while (cur != _head)
			{
				Print_Person_Information(cur);
				cur = cur->_next;
			}
			printf("——————————————————————————————————————------——\n");

		}

		//打印部门信息
		void Print_Depname_Information()
		{
			SNode* cur = _head->_next;

			if (cur == _head)
			{
				cout << "该系统中信息为空\n";
				return;
			}

			cout << "人事信息系统人员名单\n";
			printf("———————————————————————————————————-----——-———\n");
			printf("编号   姓名   性别   部门     职称       工资\n\n");
			for (int i = 0; i < Depname_nember; i++)
			{
				//双重循环,依次打印各部门的信息
				SNode* _cur = _head->_next;
				printf("\t\t%s:\n", (Depname[i]).c_str());
				while (_cur != _head)
				{
					//判断部门
					if (_cur->_depname == Depname[i])
					{
						Print_Person_Information(_cur);
					}

					_cur = _cur->_next;
				}
				cout << endl << endl;
			}
			printf("——————————————————————————————————————------——\n");
		}

		//打印工资信息
		void Print_Depname_Salary()
		{
			SNode* cur = _head->_next;

			if (cur == _head)
			{
				cout << "该系统中信息为空\n";
				return;
			}

			cout << "人事信息系统人员名单\n";
			printf("———————————————————————————————————-----——-———\n");
			printf("编号   姓名   性别   部门     职称       工资\n\n");
			int sum = 0;
			for (int i = 0; i < Depname_nember; i++)
			{
				SNode* _cur = _head->_next;
				int money = 0;
				printf("\t\t%s:\n", (Depname[i]).c_str());
				while (_cur != _head)
				{
					if (_cur->_depname == Depname[i])
					{
						Print_Person_Information(_cur);
						money += _cur->_salary;
					}

					_cur = _cur->_next;
				}
				sum += money;
				printf("%s工资总计:%d\n", (Depname[i]).c_str(), money);
				cout << endl << endl;
			}
			printf("所有部门工资统计:%d\n", sum);
			printf("——————————————————————————————————————------——\n");
		}

		//删除个人信息
		void Erase_Person_Information()
		{
			cin.ignore(256, '\n');
			cout << "请输入要删除人的编号:\n";
			int num = 0,flag = -1;
			cin >> num;

			SNode* prev = _head;
			SNode* cur = _head->_next;

			if (cur == _head)
			{
				cout << "该系统中信息为空,无法删除\n";
				return;
			}

			while (cur != _head)
			{
				if (cur->_no == num)
				{
					SNode* tmp = cur;
					flag = 1;

					//双向循环链表删除操作
					cur->_next->_prev = prev;
					prev->_next = cur->_next;

					cur = prev->_next;

					//string类型的成员变量是单个对象,而不是数组,
					//所以应该使用delete来释放它们的内存。
					//如果使用delete[]来释放string对象的内存,会导致未定义的行为和可能的内存泄漏。
					delete tmp;
					tmp = nullptr;
				}
				else
				{
					//遍历后面元素
					prev = prev->_next;
					cur = cur->_next;
				}
			}
			if (flag==1) cout << "删除成功\n";
			else cout << "删除失败,没有此人\n";
		}

		//删除所有信息
		void Erase_All_Informations()
		{
			cout << "是否要删除所有信息!\n删除请输入[Y]\n";
			char ch;
			cin.ignore(256, '\n');
			cin >> ch;
			if (ch != 'Y')
			{
				cout << "删除失败\n";
				return;
			}

			SNode* cur = _head->_next;

			if (cur == _head)
			{
				cout << "该系统中信息为空\n";
				return;
			}

			_head->_next = _head;
			_head->_prev = _head;

			//挨个删除链表中的元素,释放空间,避免内存泄漏
			while (cur != _head)
			{
				SNode* tmp = cur;
				cur = cur->_next;

				delete tmp;
				tmp = nullptr;
			}

			cout << "删除成功\n";
		}

		//调整工资
		void Adjust_Person_Salary()
		{
			cin.ignore(256, '\n');
			cout << "请输入要调整工资的人的编号:\n";
			int num = 0;
			cin >> num;

			cin.ignore(256, '\n');
			cout << "请输入调整后的工资金额:\n";
			int sal = 0;
			cin >> sal;

			SNode* cur = _head->_next;
			bool flag = false;

			//寻找需要更改信息的节点
			while (cur != _head)
			{
				if (cur->_no == num)
				{
					flag = true;
					cur->_salary = sal;
				}

				cur = cur->_next;
			}

			if (flag) cout << "修改成功\n";
			else cout << "没有此人\n";
		}

		//仿函数
		struct Compare_No
		{
			//重载operator()操作符以实现函数比较操作
			bool operator()(const Staff_Information_Node& p1, const Staff_Information_Node& p2)
			{
				return p1._no < p2._no;
			}
		};
		struct Compare_Name
		{
			bool operator()(const Staff_Information_Node& p1, const Staff_Information_Node& p2)
			{
				return p1._name > p2._name;
			}
		};
		struct Compare_Salary
		{
			bool operator()(const Staff_Information_Node& p1, const Staff_Information_Node& p2)
			{
				return p1._salary > p2._salary;
			}
		};

		//打印排序结果(vector)
		void _sort_print(vector<SNode>& v)
		{
			int len = v.size();
			if (len == 0)
			{
				cout << "该系统中信息为空\n";
				return;
			}

			cout << "人事信息系统人员名单\n";
			printf("———————————————————————————————————-----——-———\n");
			printf("编号   姓名   性别   部门     职称       工资\n");
			for (int i = 0; i < len; i++)
			{
				Print_Person_Information(&v[i]);
			}
			printf("——————————————————————————————————————------——\n");
		}

		//按编号排序
		void Print_Person_No_Information()
		{
			vector<SNode> v;
			SNode* cur = _head->_next;
			while (cur != _head)
			{
				v.push_back(*cur);
				cur = cur->_next;
			}
			sort(v.begin(), v.end(), Compare_No());
			_sort_print(v);
		}

		//按工资排序
		void Print_Person_Salary_Information()
		{
			vector<SNode> v;
			SNode* cur = _head->_next;
			while (cur != _head)
			{
				v.push_back(*cur);
				cur = cur->_next;
			}
			sort(v.begin(), v.end(), Compare_Salary());
			_sort_print(v);
		}

	private:
		SNode* _head;//头结点、哨兵位
		size_t _size;
	};
}

            

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

#include"LinkList.h"

void system_start()
{
	int option = 0, flag = 0;
	staff_linklist::LinkList_Information lt;
	while(1)
	{
		//system("cls");
		Init_menu();
		cout << "请输入:";
		if (cin >> option);
		switch (option)
		{
		case(1):
			lt.push_back();
			break;
		case(2):
			lt.Print_Information();
			break;
		case(3):
			lt.Print_Depname_Information();
			break;
		case(4):
			lt.Print_Depname_Salary();
			break;
		case(5):
			lt.Erase_Person_Information();
			break;
		case(6):
			lt.Erase_All_Informations();
			break;
		case(7):
			lt.Adjust_Person_Salary();
			break;
		case(8):
			lt.Print_Person_No_Information();
			break;
		case(9):
			lt.Print_Person_Salary_Information();
			break;
		case(0):
			flag = 1;
			break;
		default:
			cout << "输入错误,请重新输入\n";
			break;
		}
		if (flag == 1)
		{
			cout << "退出成功\n";
			break;
		}
	} 
}

int main()
{
	system_start();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1064650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Arxdbg读取cad扩展属性

xdlist可以读取CAD实体属性&#xff0c;子实体的扩展属性看不到。 下载arxdbg,appload加载。 鼠标右键即可看见&#xff0c;选Entity Info。 查看xdata,dxfdata都可以看到扩展信息。

Python 无废话-办公自动化Excel修改数据

如何修改Excel 符合条件的数据&#xff1f;用Python 几行代码搞定。 需求&#xff1a;将销售明细表的产品名称为PG手机、HW手机、HW电脑的零售价格分别修改为4500、5500、7500&#xff0c;并保存Excel文件。如下图 Python 修改Excel 数据&#xff0c;常见步骤&#xff1a; 1&…

jmeter和性能测试

一。性能测试的概念 1.性能&#xff1a;就是软件质量属性中的 “ 效率 ” 特性 2.效率特性&#xff1a; 时间特性&#xff1a;指系统处理用户请求的响应时间 资源特性&#xff1a;指系统在运行过程中&#xff0c;系统资源的消耗情况 CPU 内存 磁盘IO&#xff08;磁盘的写…

鸡群优化(CSO)算法(含MATLAB代码)

先做一个声明&#xff1a;文章是由我的个人公众号中的推送直接复制粘贴而来&#xff0c;因此对智能优化算法感兴趣的朋友&#xff0c;可关注我的个人公众号&#xff1a;启发式算法讨论。我会不定期在公众号里分享不同的智能优化算法&#xff0c;经典的&#xff0c;或者是近几年…

TCP VS UCP

程序员写网络程序&#xff0c;主要编写的应用层代码&#xff01; 真正要发这个数据&#xff0c;需要上层协议调用下层协议&#xff0c;应用层要调用传输层&#xff0c;则传输层给应用层提供一组api&#xff0c;统称为&#xff1a;soket api 基于UDP的api 基于TCP的api 这两个协…

Godot 初学

前言 因为9月份 Unity一顿安装计费的骚操作&#xff0c;导致世界开发者对于Unity 随意修改开发条例&#xff0c;追溯之前开发游戏版本感到愤怒。Unity是全球游戏使用率超过50%的引擎&#xff0c;Unity和Unreal是最主流的第三方游戏引擎。除非你是大厂可以自研引擎&#xff0c;…

阿里云服务器活动价格及配置整理表(多配置报价)

2023年阿里云服务器租用费用&#xff0c;阿里云轻量应用服务器2核2G3M带宽轻量服务器一年108元&#xff0c;2核4G4M带宽轻量服务器一年297.98元12个月&#xff0c;CS云服务器e系列2核2G配置182元一年、2核4G配置365元一年、2核8G配置522元一年&#xff0c;阿里云u1服务器2核4G、…

复习Day11:链表part04: 206. 反转链表、92. 反转链表II、25. K 个一组翻转链表、148. 排序链表

我用的方法是在leetcode再过一遍例题&#xff0c;明显会的就复制粘贴&#xff0c;之前没写出来就重写&#xff0c;然后从拓展题目中找题目来写。辅以Labuladong的文章看。然后刷题不用CLion了&#xff0c;使用leetcode自带的IDE模拟面试环境。 哈希表章节的题目思路很清晰&…

Java多线程之等待唤醒机制及案例代码演示

生产者和消费者&#xff08;等待唤醒机制&#xff09; 等待唤醒机制常见方法代码演示等待唤醒机制(阻塞队列方式实现)额外扩展 等待唤醒机制 生产者和消费者是一个十分经典的多线程协作模式 举个小栗子来说明一下消费者和生产者的等待唤醒过程: 常见方法 void wait() 当前…

因为计算机中找不到mfc140.dll无法启动修复步骤分享

mfc140.dll是Microsoft Foundation Class Library&#xff08;微软基础类库&#xff09;的一个组件&#xff0c;它是许多Windows应用程序&#xff08;尤其是使用MFC编写的程序&#xff09;所必需的动态链接库。MFC&#xff08;Microsoft Foundation Classes&#xff09;是一个用…

Pytorch笔记之分类

文章目录 前言一、导入库二、数据处理三、构建模型四、迭代训练五、模型评估总结 前言 使用Pytorch进行MNIST分类&#xff0c;使用TensorDataset与DataLoader封装、加载本地数据集。 一、导入库 import numpy as np import torch from torch import nn, optim from torch.uti…

10.5汇编语言整理

【汇编语言相关语法】 1.汇编语言的组成部分 1.伪操作&#xff1a;不参与程序的执行&#xff0c;但是用于告诉编译器程序该怎么编译 .text .global .end .if .else .endif .data 2.汇编指令 编译器将一条汇编指令编译成一条机器码&#xff0c;在内存里一条指令占4字节内存&…

c++---模板篇

1、模板 概念&#xff1a;模板就是建立通用的模具&#xff0c;大大提高复用性 特点&#xff1a; 模板不可以直接使用&#xff0c;它只是一个框架模板的通用并不是万能的 1.1、函数模板 C另一种编程思想称为泛型编程&#xff0c;主要利用的技术就是模板C提供两种模板机制&a…

数据结构与算法(四):哈希表

参考引用 Hello 算法 Github&#xff1a;hello-algo 1. 哈希表 1.1 哈希表概述 哈希表&#xff08;hash table&#xff09;&#xff0c;又称散列表&#xff0c;其通过建立键 key 与值 value 之间的映射&#xff0c;实现高效的元素查询 具体而言&#xff0c;向哈希表输入一个键…

Linux CentOS7 vim宏操作

vim的macro就是用来解决重复的问题。在vim寄存器的文章里面已经对macro有所涉及&#xff0c;macro的操作都是以文本的方式存放在寄存器中。 宏是一组命令的集合&#xff0c;应用极其广泛&#xff0c;包括MS Office中的word编辑器&#xff0c;excel编辑器和各种文本编辑器&…

Pytorch笔记之回归

文章目录 前言一、导入库二、数据处理三、构建模型四、迭代训练五、结果预测总结 前言 以线性回归为例&#xff0c;记录Pytorch的基本使用方法。 一、导入库 import numpy as np import matplotlib.pyplot as plt import torch from torch.autograd import Variable # 定义求…

ESP32/ESP8266在线刷写Sonoff Tasmota固件以及配置简要

ESP32/ESP8266在线刷写Sonoff Tasmota固件以及配置简要 &#x1f4cd;原项目Github地址&#xff1a;https://github.com/arendst/Tasmota/tree/v13.1.0&#x1f4d1;官方文档介绍&#xff1a;https://tasmota.github.io/docs/&#x1f6a9;(✨推荐方式✨)在线固件刷写地址&…

strcpy函数详解:字符串复制的利器

目录 一&#xff0c;strcpy函数的简介 二&#xff0c;strcpy函数的实现原理 三&#xff0c;strcpy函数的注意事项 四&#xff0c;strcpy函数的模拟实现 一&#xff0c;strcpy函数的简介 strcpy函数是C语言中的字符串复制函数&#xff0c;其原型如下&#xff1a; char * str…

Linux中的wc命令

2023年10月6月&#xff0c;周五晚上 目录 wc命令的主要功能和用法如下:统计文件行数、字数和字节数只统计行数只统计字数只统计字节数 wc命令在Linux/Unix系统中是word count的缩写,它用来统计文件的行数、字数和字节数。 wc命令的主要功能和用法如下: 统计文件行数、字数和字…

英语四六级高频核心词(故事版)

第一组&#xff1a;" A Century of Community Effort to Improve Quality of Life and Climate" In the early years of the 20th century, a small community found itself facing a decade of challenges. The most pressing issue was the mental quality of life…