Topological sorting - "Data Structure" class-based

Topological sorting

Related concepts

Baidu Encyclopedia of the first gave the concept:

  For a directed acyclic graph (Directed Acyclic Graph referred to as the DAG) G be topologically sorted, all the vertices of G is arranged in a linear sequence, such that any pair of vertices FIG u and v, if the edge (u, v) ∈ E (G), the linear sequence u appears before v. Typically, such a sequence is called a linear sequence satisfy topological order (Topological Order), acronym topology sequence. Briefly, to give a total order on the set of a partial order on a set, this operation is called Topological Sort.

  For example popular - Scheduling scheme. Before topology sequence satisfy a condition of the occurrence of certain events arranged in this event - only to learn after the corresponding basic course (such as a high number of lines generations), in order to learn advanced courses (such as artificial intelligence), and it is therefore generate topological sequence . For example, there are courses and Advanced Placement courses as follows:

course Advanced Placement
data structure discrete mathematics
discrete mathematics advanced mathematics
advanced mathematics no

Obviously, you want to learn data structures, we must first learn discrete mathematics, discrete mathematics want to learn, we must first know the number of high knowledge base, and the high number of university as a basic course, you do not need prerequisites.
  Well, according to a topological sequence discharge of this curriculum it is:

Discrete Mathematics mathematics → → Data Structure

  Topological topological sorting sequence obtained is not necessarily the only, for example, do not need to join in the top Pre-knowledge of "Fundamentals of Programming" course, this level is high and the number of courses side by side relationship, then, both of which are prerequisites it's the same. Thus the topology of unique sequences not present.

algorithm design

  Prerequisite accordance with the idea that we should start from class less dependent on the course, gradually learning. Like DNF to play the role of the upgrade of skills, different roles have different skills after awakening, and want to get advanced skills, you must first learn its dependence on low-level skills.
  Therefore, you may never learn the dependence of the course begins, gradually unlock "skill tree." Then, one approach is to generate the topology sequence (using stored information can stack or queue):

  1. Traversing all courses will no Pre-dependent (000-degree) all point onto the stack;
  2. Determining whether the stack is empty, if not empty, a pop-up program added to the topology and sequence;
  3. Since this course is unlocked, it is to depend on the course it did not rely on this. Therefore, it is of course dependent on the degree are -1-1-1;
  4. It is determined in dependence of the course -1-1-1 after penetration, is changed into 000-degree, and if so, push the course;
  5. repeat 2 4 2→4 , until the stack is empty so far.

  In addition, topological sorting the figure can not exist in the loop - in fact well understood, imagine there is a loop course with its own means as prerequisites - which is obviously ridiculous.


Code

  The "Data Structures" course set I chose about topological sort of problem, along the following topics:
  a given course number, course name, credits, prerequisites and constraints: the number of semester credits each semester and ceiling, gives a row lesson plan.

1. The logical structure design:
Here Insert Picture Description
  adjacency table section shows the dependence of course I do topic. Here I use a hash table way to reduce the number and then indexed by the search program marked its adjacent exemplar brought under head O ( n ) O (n) time complexity.

2. A O V AOV network FIG.

Here Insert Picture Description
3. Code

#include<iostream>
#include<map>
#include<fstream>
#include<stack>
#include<vector>
#include<string>
#include<sstream>
#include<windows.h>
using namespace std;
typedef int ScoreType;
typedef string Number;
struct CourseInfo {								   // 单个课程的信息 
	string courseName;							   // 课程名称 
	ScoreType credit;						       // 课程学分 
	int inDgree;							       // 每个点的入度 
	CourseInfo(string courseName = "NULL", ScoreType credit = 0, int inDgree = 0) {
		this->courseName = courseName;
		this->credit = credit;
		this->inDgree = inDgree;
	}
};
struct CourseNode {								   // 单个课程节点 
	Number courseNumber;						   // 课程编号 
	struct CourseNode *next;
	CourseNode(Number courseNumber = "NULL") {
		this->courseNumber = courseNumber;
		next = NULL;
	}
};
struct CourseList {
	vector<CourseNode*> courseNet;  			   // 课程邻接表 
	vector<CourseNode*> tailPoint;    			   // 记录每行的尾指针
	vector<CourseInfo> courseInfo;   			   // 课程信息
	map<Number, int> hm;	     	 			   // 哈希表,用来记录编号与下标的关系,如201706020115对应下标 1号 
	int vertexNum;					 			   // 当前课程数量
	CourseList() {
		vertexNum = 0;
	}
	void CreateHashMap(stringstream &ss);		   // 建立索引哈希表
	void CreateAdjList(string buf, int index);     // 建立邻接表
};
struct UserInterface {
	CourseList course;
	vector<string> topoOrder;         			   // 拓扑序列:C1->C2->C3...

	bool JudgeLogicError(int count);  			   // 判断课程逻辑是否有误,拓扑图不能有环 
	void LoadingCourseInfo();
	void ReadFromFile();
	void GetTopologicalOrder();
	void ShowTopoOrder();             			   // 显示拓扑序列方案
	void ShowCourseInfo();
	void UserOption();
	char ShowOption();
};
int main()
{
	UserInterface user;
	user.UserOption();
	system("pause");
	return 0;
}
void CourseList::CreateHashMap(stringstream &ss) { // 记录编号与下标的映射、课程名称、课程学分
	string cName;
	Number cNumber;
	CourseInfo cInfo;
	ss >> cNumber >> cInfo.courseName >> cInfo.credit;
	courseInfo.push_back(cInfo);
	hm[cNumber] = vertexNum++;					   // 将课程号与下标建立哈希映射,减少顺序检索带来的时间复杂度
	CourseNode *cn = new CourseNode(cNumber);
	courseNet.push_back(cn);
	tailPoint.push_back(cn);
}
void CourseList::CreateAdjList(string buf, int index) {
	vector<string> mark;
	string str;
	// 解析字符串
	int len = buf.size(), c = 0, flag = 1;
	if (buf[0] != 'C') {
		flag = 0;
	}
	for (int i = 0;flag && i < len;i++) {
		if (buf[i] != ',') {
			str += buf[i];
		}
		else {
			mark.push_back(str);
			str.clear();
		}
	}
	if (flag) {
		mark.push_back(str);
	}
	// 根据 hm[courseNumber] 进行下标索引
	int size = mark.size(), cnt = 0;
	for (int i = 0;flag && i < size;i++) {
		cnt++;
		CourseNode * newNode = new CourseNode(courseNet[index]->courseNumber);
		int n = hm[mark[i]];
		tailPoint[n]->next = newNode;
		tailPoint[n] = tailPoint[n]->next;
	}
	courseInfo[index].inDgree += cnt;
}
bool UserInterface::JudgeLogicError(int count) {   // 判断课程是否存在回路 
	return bool(count != course.vertexNum);
}
void UserInterface::GetTopologicalOrder() {
	stack<CourseNode *> st;
	for (int i = 0;i < course.vertexNum;i++) {     // 将入度为 0 的点压入栈中
		if (!course.courseInfo[i].inDgree)
			st.push(course.courseNet[i]);
	}
	int cnt = 0;
	while (!st.empty()) {                          // 拓扑的核心,也可用队列存储
		CourseNode *p, *temp = st.top();		   // 若优化方案,考虑到先修课程并行学习,可以做多个辅助栈以达到模拟并行的目的 
		st.pop();
		topoOrder.push_back(temp->courseNumber);
		p = temp->next;
		while (p) {
			int index = course.hm[p->courseNumber];
			if (!(--course.courseInfo[index].inDgree)) {
				st.push(course.courseNet[index]);
			}
			p = p->next;
		}
		cnt++;
	}
	if (JudgeLogicError(cnt)) {
		cout << "There are logical errors in course information!";
		system("pause");
		exit(0);
	}
}
char UserInterface::ShowOption() {
	system("cls");
	char ch;
	cout << "    请选择操作:  " << endl;
	cout << "1.显示全部课程信息" << endl;
	cout << "2.显示学期课程安排" << endl;
	cout << "3.退出自动排课系统" << endl;
	cin >> ch;
	return ch;
}
void UserInterface::UserOption() {
	char op;
	int flag = 1;
	LoadingCourseInfo();
	while (op = ShowOption()) {
		switch (op)
		{
		case '1':
			ShowCourseInfo();Sleep(5000);break;
		case '2':
			if (flag) {
				GetTopologicalOrder();
				flag = 0;
			}
			ShowTopoOrder();Sleep(10000);break;
		case '3':
			cout << "谢谢使用,再见!" << endl;
			system("pause");exit(0);break;
		default:
			break;
		}
	}
}
void UserInterface::LoadingCourseInfo() {
	ReadFromFile();
}
void UserInterface::ReadFromFile() {
	const string fileName = "courseInfo.txt";
	fstream fin;
	try {
		fin.open(fileName, ios::in | ios::out);
		if (!fin)
			throw "File Open Error!";
	}
	catch (const char *Warning)
	{
		cout << Warning << endl;
		system("pause");
		exit(0);
	}
	//成功打开文件
	string line;
	int flag = 0;
	while (getline(fin, line))
	{
		if (!flag) {		                   	   // 跳过第一行(标题属性) 
			flag = 1;
			continue;
		}
		stringstream ss(line);
		course.CreateHashMap(ss);
	}
	fin.clear();
	fin.seekg(0);
	// 再读一遍,此时根据课程依据的先序,创建邻接表
	flag = 0;
	int st = 0;
	while (getline(fin, line))
	{
		if (!flag) {						       // 跳过第一行(标题属性) 
			flag = 1;
			continue;
		}
		string buf;
		stringstream ss(line);
		for (int j = 1;j <= 4;j++) ss >> buf;      // 吞掉前面3个字符串,只留下先修课程做解析
		course.CreateAdjList(buf, st);
		st++;
	}
	fin.close();
}
void UserInterface::ShowCourseInfo() {
	//显示全部信息
	for (int i = 0;i < course.vertexNum;i++) {
		string name = course.courseNet[i]->courseNumber;
		cout << course.hm[name] << ": " << name << " " << course.courseInfo[i].courseName << " ";
		cout << course.courseInfo[i].credit << endl;;
	}
}
void UserInterface::ShowTopoOrder() {
	int verNum = course.vertexNum, size; 		   // verNum 和 Size 均为总共课程数量
	int flag = 0;
	size = verNum;
	cout << "课程学习序列为:" << endl;
	for (int i = 0;i < size;i++) {
		int index = course.hm[topoOrder[i]];
		cout << course.courseInfo[index].courseName << "(" << topoOrder[i] << ") ";
		if (!((i + 1) % 5))
			cout << endl;
		if (--verNum)
			cout << "→ ";
	}
	cout << endl;

	// 增加学期数和每学期学分上限的约束 
	int semester = 7, hasLearned = 0, ceiling = 9;
	int curScore = 0, index = 0, t = 1;
	for (int i = 0;i < size;i++) {
		if (t) {
			cout << "第 " << ++index << " 学期:" << endl;
			t = 0;
		}
		int temp = course.courseInfo[course.hm[topoOrder[i]]].credit;
		if ((hasLearned <= size - semester)) {
			if (curScore <= ceiling - temp) {      // 预判 
				cout << course.courseInfo[course.hm[topoOrder[i]]].courseName << " ";
				hasLearned++;
				curScore += temp;
			}
			else {							       // 如果不满足上面的约束条件,回退到上一次并清空状态,开始下一学期的排课 
				cout << endl;
				hasLearned--;
				i--;
				curScore = 0;
				t = 1;
			}
		}
		else {
			cout << endl;
			hasLearned--;
			i--;
			curScore = 0;
			t = 1;

		}
	}
	cout << endl;
}

Guess you like

Origin blog.csdn.net/cprimesplus/article/details/94365048