拓扑排序的概念待完善, 主要以LeetCode207-课程表为例子, 说明下拓扑排序.
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
这里我用java来实现的.
这个解法耗时相当长...当时击败了5.77%的用户┭┮﹏┭┮, 贴在这里主要是为了复习一遍图的知识:
如何根据边的关系构造邻接表结构的图,
如何寻找邻接表的入度,
邻接表结构的拓扑排序如何实现.
class Solution {
// 邻接表的定义
static class Graph {
int vertexNum;
int edgeNum;
VexNode[] vertexLinkList;
}
static class VexNode {
int node;
AdjNode firstPoint;
}
static class AdjNode {
int index;
AdjNode adjNode;
}
// 判断是否可能完成所有课程的学习
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (prerequisites.length == 0) return true;
return checkSelfLoop(prerequisites) && checkSimpleGraph(prerequisites);
}
/**
* 检查是否有自有环(自己指向自己)
*
* @param edge 边信息
* @return 自有环-false, 非自有环-true
*/
public boolean checkSelfLoop(int[][] edge) {
for (int[] ints : edge) {
if (ints[0] == ints[1]) {
return false;
}
}
return true;
}
/**
* 检查简单图或平行图是否有环
*
* @param edgeInfo 边信息
* @return 有环-false, 无环-true
*/
public boolean checkSimpleGraph(int[][] edgeInfo) {
Graph graph = createALGraph(edgeInfo);
int num = topologicalSorting(graph);
return num == graph.vertexNum;
}
/**
* 邻接表的拓扑排序
*
* @param graph 图
* @return 拓扑序列的顶点个数
*/
public int topologicalSorting(Graph graph) {
int topologicalSeqNum = 0;
int[] inDegrees = findInDegrees(graph);
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < graph.vertexNum; ++i) {
if (inDegrees[i] == 0) {
stack.push(graph.vertexLinkList[i].node);
}
}
while (!stack.empty()) {
int nodeIndex = searchALGVexIndex(graph, stack.pop());
topologicalSeqNum++;
AdjNode tempP = graph.vertexLinkList[nodeIndex].firstPoint;
while (tempP != null) {
inDegrees[tempP.index]--;
if (inDegrees[tempP.index] == 0) {
stack.push(graph.vertexLinkList[tempP.index].node);
}
tempP = tempP.adjNode;
}
}
return topologicalSeqNum;
}
/**
* 找入度
*
* @param graph 逆邻接表图
* @return 入度数组
*/
public int[] findInDegrees(Graph graph) {
int i;
int[] inDegrees = new int[graph.vertexNum];
for (i = 0; i < graph.vertexNum; ++i) {
AdjNode tempPoint = graph.vertexLinkList[i].firstPoint;
while (tempPoint != null) {
inDegrees[tempPoint.index]++;
tempPoint = tempPoint.adjNode;
}
}
return inDegrees;
}
/**
* 创建邻接表
*
* @param prerequisites 边信息
* @return 邻接表的图
*/
public Graph createALGraph(int[][] prerequisites) {
int i;
Graph graph = new Graph();
graph.edgeNum = prerequisites.length;
// 根据边信息获取部分顶点
int[] vertexArray = sortAndDistinct(prerequisites);
graph.vertexNum = vertexArray.length;
// 初始化邻接表
graph.vertexLinkList = new VexNode[graph.vertexNum];
for (i = 0; i < graph.vertexNum; i++) {
graph.vertexLinkList[i] = new VexNode();
}
for (i = 0; i < vertexArray.length; ++i) {
graph.vertexLinkList[i].node = vertexArray[i];
graph.vertexLinkList[i].firstPoint = null;
}
// 创建邻接表
for (i = 0; i < graph.edgeNum; ++i) {
int tailIndex = searchALGVexIndex(graph, prerequisites[i][1]);
int headIndex = searchALGVexIndex(graph, prerequisites[i][0]);
if (tailIndex != -1 && headIndex != -1) {
createNodeList(graph, tailIndex, headIndex);
} else {
// 抛异常, 该数据未在顶点集里找到
return null;
}
}
return graph;
}
/**
* 创建邻接表的结点表
*
* @param graph 图
* @param tailIndex 尾指针
* @param headIndex 头指针
*/
private void createNodeList(Graph graph, int tailIndex, int headIndex) {
AdjNode adjNode = new AdjNode();
adjNode.index = headIndex;
adjNode.adjNode = graph.vertexLinkList[tailIndex].firstPoint;
graph.vertexLinkList[tailIndex].firstPoint = adjNode;
}
/**
* 根据边的关系推出部分(或全部)有关系的顶点
* 并非所有顶点都有关系, 只需要找出有关系的顶点
*
* @param edge 边信息
* @return 有关系的顶点信息
*/
public int[] sortAndDistinct(int[][] edge) {
int i;
int j = 0;
int tempIndex = 0;
int[] distinctArray = new int[edge.length << 1];
for (i = 0; i < edge.length; ++i) {
distinctArray[j++] = edge[i][0];
distinctArray[j++] = edge[i][1];
}
Arrays.sort(distinctArray);
for (i = 0; i < edge.length << 1; ++i) {
if (distinctArray[tempIndex] != distinctArray[i]) {
distinctArray[++tempIndex] = distinctArray[i];
}
}
int[] resultArray = new int[tempIndex + 1];
for (i = 0; i <= tempIndex; ++i) {
resultArray[i] = distinctArray[i];
}
return resultArray;
}
/**
* 寻找给定结点的下标
*
* @param graph 图
* @param nodeData 结点数据
* @return 下标
*/
public int searchALGVexIndex(Graph graph, int nodeData) {
for (int i = 0; i < graph.vertexNum; ++i) {
if (nodeData == graph.vertexLinkList[i].node) {
return i;
}
}
return -1;
}
}