学院要求对每个专业的学生制定完备的教学计划,教学计划由课程组成,课程之间会有先后依赖关系(例如必须先学完《程序设计语言》后才能学习《数据结构》),假定每门课程需要一个学期学完,同一个学期可学习多门课程。请设计存储结构存储所有课程及其之间的依赖关系,并在此存储结构基础上完成如下操作:
\1. 输入并保存课程及课程之间的依赖关系。
\2. 判断课程能否顺利学完,若能,输出一个正确的学习顺序。
\3. 求学完这些课程最少需要几个学期,并给出一个初步的教学计划(每个学期学习哪几门课程)。
\4. 由于学生精力的限制,学校对每学期选课数的上限进行了约束,请判断约束后学生能够在原来的学期数内完成学习,若不能,此时最少需要几个学期学完这些课程**(选做)**。
package constant;
/**
* @author guo
*/
public interface MyConstants {
/**
* 最大容量
*/
int MaxInt = 20;
/**
* 最多学期数限制
*/
int maxStudy = 8;
}
package pojo;
/**
* @author guo
* 边结点
*/
public class ArcNode implements Cloneable{
public int adjVex;
public ArcNode nextArc;
@Override
public Object clone(){
ArcNode arcNode = null;
try {
arcNode = (ArcNode) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
arcNode.nextArc = (ArcNode) nextArc.clone();
return arcNode;
}
public ArcNode() {
adjVex = 0;
nextArc = null;
}
}
package pojo;
/**
* 课程信息
* @author guo
*/
public class Course implements Cloneable{
private String id;
private String name;
public Course() {
}
@Override
public Object clone() throws CloneNotSupportedException {
Course course = null;
try {
course = (Course)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return course;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package pojo;
import java.util.Scanner;
/**
* AOV图(课程)
* @author guo
*/
public class Graph implements Cloneable{
/**
* 顶点表
*/
public Vnode[] adjList;
/**
* 边数
*/
private int e;
/**
* 顶点数
*/
private int n;
public Graph(int number,int edge) {
adjList = new Vnode[number];
n = number;
e = edge;
}
@Override
public Object clone() {
Graph graph = null;
try {
graph = (Graph) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
graph.adjList = (Vnode[])adjList.clone();
return graph;
}
public void setN(int n) {
this.n = n;
}
/**
* 查找顶点位置
*/
public int locateVex(Course c){
for (int i = 0; i < n; i++) {
if (adjList[i].getData().getId().equals(c.getId())) {
return i;
}
}
return -1;
}
/**
* 建立有向图的邻接表
*/
public void creatAdj(){
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < n; i++) {
//录入顶点信息
System.out.println("请输入课程编号");
String id = scanner.next();
System.out.println("请输入课程名字");
String name = scanner.next();
Course c = new Course();
c.setId(id);
c.setName(name);
adjList[i] = new Vnode();
adjList[i].setIn(0);
adjList[i].setData(c);
adjList[i].setFirstArc(null);
}
System.out.println("输入依赖信息:");
for (int i = 0; i < e; i++) {
System.out.println("请输入第"+(i+1)+"条边的两个端点,端点间使用空格间隔即可");
String str1 = scanner.next();
String str2 = scanner.next();
Course c1 = new Course();
Course c2 = new Course();
c1.setId(str1);
c2.setId(str2);
int a = locateVex(c1);
int b = locateVex(c2);
if (a >= 0 && b >= 0){
ArcNode s = new ArcNode();
s.adjVex = b;
s.nextArc = adjList[a].getFirstArc();
adjList[a].setFirstArc(s);
adjList[b].setIn(adjList[b].getIn()+1);
}
}
}
}
package pojo;
import static constant.MyConstants.MaxInt;
/**
* @author guo
*/
public class Stack {
/**
* 用于存储课程的数组
*/
public Course[] stackArray;
/**
* 栈顶指针
*/
public int top;
public Stack() {
top = -1;
stackArray = new Course[MaxInt];
}
public Stack(int n){
top = -1;
stackArray = new Course[n];
}
public void push(Course course){
if (top == stackArray.length-1){
Course[] p = new Course[top*2+2];
for (int i = 0; i <= top; i++) {
p[i] = stackArray[i];
}
stackArray = p;
}
top++;
stackArray[top] = course;
}
public Course pop(){
if (top == -1){
System.out.println("栈已空,无法再删除元素!");
return null;
}
top--;
return stackArray[top+1];
}
}
package pojo;
/**
* @author guo
* 点结点
*/
public class Vnode implements Cloneable{
/**
* 入度域
*/
private int in;
/**
* 数据域
*/
private Course data;
/**
* 指针域
*/
private ArcNode firstArc;
public Vnode(){
in = -1;
data = null;
firstArc = null;
}
@Override
public Object clone() throws CloneNotSupportedException {
Vnode vnode = null;
try {
vnode = (Vnode)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
vnode.in = ((Vnode)vnode.clone()).getIn();
vnode.data = (Course)data.clone();
vnode.firstArc = (ArcNode)firstArc.clone();
return vnode;
}
public int getIn() {
return in;
}
public void setIn(int in) {
this.in = in;
}
public Course getData() {
return data;
}
public void setData(Course data) {
this.data = data;
}
public ArcNode getFirstArc() {
return firstArc;
}
public void setFirstArc(ArcNode firstArc) {
this.firstArc = firstArc;
}
}
package test;
import pojo.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import static constant.MyConstants.maxStudy;
/**
* @author guo
*/
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入课程总数和课程依赖边数");
System.out.println("顶点数:");
int number = scanner.nextInt();
System.out.println("依赖边数:");
int edge = scanner.nextInt();
Graph graph = new Graph(number,edge);
//创建AOV无环网
graph.creatAdj();
System.out.println("输入1:题目一:判断课程能否顺利学完,若能,输出一个正确的学习顺序");
System.out.println("输入2:题目二:求学完这些课程最少需要几个学期,并给出一个初步的教学计划(每个学期学习哪几门课程)");
System.out.println("输入3:题目三:由于学生精力的限制,学校对每学期选课数的上限进行了约束,请判断约束后学生能够在原来的学期数内完成学习,若不能,此时最少需要几个学期学完这些课程(选做)");
System.out.println("请输入指令:");
int x = scanner.nextInt();
switch (x){
case (1):
topSort1(graph);
break;
case (2):
topSort2(graph);
break;
case (3):
topSort3(graph);
break;
default:
return;
}
scanner.close();
}
/**
* 学习顺序
* @param graph
*/
public static void topSort1(Graph graph){
//建立空栈,用于存放入度为0的点
Stack stack = new Stack();
stack.top = 0;
int count = 0;
//对邻接表进行遍历,将其中度为0的点压入栈中
for (int i = 0; i < graph.adjList.length-1; i++) {
if (graph.adjList[i].getIn() == 0){
stack.push(graph.adjList[i].getData());
}
}
System.out.println("学习顺序为:");
while (stack.top!=0){
//出栈并输出一个元素
Course course = stack.pop();
System.out.print(course.getId()+":"+course.getName());
count++;
//所有与其有关系的点的入度均减一
ArcNode p = graph.adjList[graph.locateVex(course)].getFirstArc();
while (p != null){
int in = graph.adjList[p.adjVex].getIn()-1;
graph.adjList[p.adjVex].setIn(in);
//若入度减一后存在顶点的度减少为0,便将其入栈
if (graph.adjList[p.adjVex].getIn() == 0){
stack.push(graph.adjList[p.adjVex].getData());
}
p = p.nextArc;
}
if (count!=graph.adjList.length){
System.out.print("->");
}
}
//出栈的元素数量小于顶点表的长度时,则表明有环产生
if (count < graph.adjList.length){
System.out.println("有回路产生,即无法全部学完所有课程");
}
System.out.println();
}
/**
* 教学顺序
* @param graph
*/
public static void topSort2(Graph graph){
//建立空栈,用于存放入度为0的点
Stack stack = new Stack();
stack.top = 0;
int count = 0;
int day = 0;
//对邻接表进行遍历,将其中度为0的点压入栈中
for (int i = 0; i < graph.adjList.length-1; i++) {
if (graph.adjList[i].getIn() == 0){
stack.push(graph.adjList[i].getData());
}
}
while (stack.top!=0){
//使用ArrayList集合来存储每次栈中的元素
List<Course> list = new ArrayList<Course>();
//将所有度为零的元素全部出栈
while (stack.top!=0){
Course course = stack.pop();
list.add(course);
count++;
}
day++;
System.out.println("第"+day+"个学期的学习:");
for (Course c : list) {
System.out.print(c.getId()+" "+c.getName()+" ");
}
System.out.println();
for (int i = 0; i < list.size(); i++) {
ArcNode p = graph.adjList[graph.locateVex(list.get(i))].getFirstArc();
//所有与其有关系的点的入度均减一
while (p != null){
graph.adjList[p.adjVex].setIn(graph.adjList[p.adjVex].getIn()-1);
//若入度减一后存在顶点的度减少为0,便将其入栈
if (graph.adjList[p.adjVex].getIn() == 0){
stack.push(graph.adjList[p.adjVex].getData());
}
p = p.nextArc;
}
}
}
//若循环结束后day值大于学期最大值常数,则表明无法再大学期间学完所有课程
if (day>maxStudy){
System.out.println("不能在大学四年8个学期内学完");
}else {
System.out.println("以上为初步教学顺序");
}
}
/**
* 选课上限限制
* @param graph
*/
public static void topSort3(Graph graph){
Stack stack = new Stack();
int limit = 0;
stack.top = 0;
int day = 0;
Scanner scanner = new Scanner(System.in);
System.out.println("请设置选课上限:");
limit = scanner.nextInt();
//对邻接表进行遍历,将其中度为0的点压入栈中
for (int i = 0; i < graph.adjList.length-1; i++) {
if (graph.adjList[i].getIn() == 0){
stack.push(graph.adjList[i].getData());
}
}
while (stack.top!=0){
int count = 0;
List<Course> list = new ArrayList<Course>();
//使用集合存储出栈的元素,但出栈和加入集合的元素数量应小于limit且栈不为空
while (stack.top!=0 && count < limit){
Course course = stack.pop();
list.add(course);
count++;
}
day++;
System.out.println("第"+day+"个学期的学习:");
for (Course c : list) {
System.out.print(c.getId()+" "+c.getName()+" ");
}
System.out.println();
for (int i = 0; i < list.size(); i++) {
ArcNode p = graph.adjList[graph.locateVex(list.get(i))].getFirstArc();
//所有与其有关系的点的入度均减一
while (p != null){
graph.adjList[p.adjVex].setIn(graph.adjList[p.adjVex].getIn()-1);
//若入度减一后存在顶点的度减少为0,便将其入栈
if (graph.adjList[p.adjVex].getIn() == 0){
stack.push(graph.adjList[p.adjVex].getData());
}
p = p.nextArc;
}
}
}
//若学期数大于学期最大值常数,则表明无法在学完所有课程,并输出所有的学期以及所上的课程
if (day>maxStudy){
System.out.println("需要"+day+"个学期才能学完"+"不能在大学四年8个学期内学完");
}else {
System.out.println("以上为限制选课教学顺序");
}
}
}