一、拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序
拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
(1) 选择一个入度为0的顶点并输出之;
(2) 从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,否则输出的顶点序列就是一种拓扑序列。
算法代码如下:
package _不带权图;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
//拓扑排序
public class _拓扑排序 {
static int n;//顶点个数
static int[] indegree;//用于记录顶点入度的信息
static LinkedList<Integer>[] list;//邻接表存图
//初始化
static void init() {
indegree = new int[n + 1];
list = new LinkedList[n + 1];
for (int i = 0; i < n + 1; i++)
list[i] = new LinkedList<Integer>();
}
//拓扑排序
static void topo() {
Queue<Integer> q = new LinkedList<>();//用队列存储入度为0的顶点
for (int i = 1; i <= n; i++) {//找出起点
if (indegree[i] == 0) {
q.offer(i);
}
}
while (!q.isEmpty()) {
int u = q.poll();
System.out.println(u);
for (int i = 0; i < list[u].size(); i++) {
int v = list[u].get(i);
indegree[v]--;//与u顶点相连的顶点度数减一
if(indegree[v]==0) {//如果有度数为0的点,入队
q.offer(v);
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int m = sc.nextInt();
init();
int u, v;
for (int i = 1; i <= m; i++) {
u = sc.nextInt();
v = sc.nextInt();
list[u].add(v);
indegree[v]++;
}
topo();
}
}
二 、欧拉回路与欧拉路径
从七桥问题开始
若图G中存在这样一条路径,使得恰好通过图G中每条边一次,则称该路径为欧拉路径。
若该路径是一个环路,则称为欧拉回路。
具有欧拉回路的图称为欧拉图。
具有欧拉路径但不具有欧拉回路的图称为半欧拉图。
即
无向图欧拉路径:
* 连通图,图中有2个奇度顶点,或无奇度顶点。 如果有2个奇度顶点,那么这2个顶点是欧拉路径的端点。
无向图欧拉回路:
* 连通图,图中不存在奇度顶点。
有向图欧拉路径:
所有点的入度和出度相同,或者 有一个顶点出度与入度差为1,一个顶点出度与入度差为-1,其余入度与出度顶点相等(即差值为0)。
有向图欧拉回路:
所有顶点的入度与出度相等。
无向图
代码如下:
package _不带权图;
import java.util.LinkedList;
import java.util.Scanner;
//无向连通图
public class _欧拉回路和欧拉路径 {
static int n, m, cnt;
static int[] degree;
static boolean[] vis;
static LinkedList<Integer>[] list;
static void init() {
degree = new int[n + 1];
vis = new boolean[n + 1];
list = new LinkedList[n + 1];
for (int i = 0; i <= n; i++)
list[i] = new LinkedList<>();
}
// 判断图是否连通
static void dfs(int u) {
vis[u] = true;
cnt++;
for (int i = 0; i < list[u].size(); i++) {
int v = list[u].get(i);
if (!vis[v]) {
dfs(v);
}
}
}
static void euler() {
dfs(1);
//判断图是否连通
if (cnt != n) {
System.out.println("It not euler");
return;
}
int cntodd = 0;// 记录度为奇数点的个数
for (int i = 1; i <= n; i++) {
if (degree[i] % 2 == 1) {
cntodd++;
}
}
if (cntodd == 0) {//无奇度顶点
System.out.println("有euler回路");
} else if (cntodd == 2) {//奇度顶点个数为2
System.out.println("有欧拉路径");
} else {
System.out.println("无欧拉路径");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
init();
int u, v;
for (int i = 0; i < m; i++) {
u = sc.nextInt();
v = sc.nextInt();
list[u].add(v);
list[v].add(u);
degree[u]++;
degree[v]++;
}
euler();
}
}
有向图
代码如下:
package _不带权图;
import java.util.LinkedList;
import java.util.Scanner;
public class _有向图的欧拉回路和欧拉路径判断 {
static int n, m;
static int[] degree;
static LinkedList<Integer>[] list;
//初始化
static void init() {
degree = new int[n + 1];
list = new LinkedList[n + 1];
for (int i = 0; i < n + 1; i++) {
list[i] = new LinkedList<>();
}
}
static int euler() {
int first = 0, last = 0;// first代表差值为-1的点,last代表差值为1的点
for (int i = 1; i <= n; i++) {
if (degree[i] < -1 || degree[i] > 1) {//差值为0
System.out.println("无欧拉路径");
return 0;
} else if (degree[i] == -1) {
if (first != 0) {//first已经被赋值,即有2个差值为-1的点
System.out.println("无欧拉路径");
return 0;
} else {
first = i;
}
} else if (degree[i] == 1) {
if (last != 0) {
System.out.println("No euler path");
return 0;
} else {
last = i;
}
}
}
if (first == 0 && last == 0) {
System.out.println("有欧拉回路");
} else if (first != 0 && last != 0) {
System.out.println("有欧拉路径");
return first;
}
return 0;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
init();
int u, v;
for (int i = 0; i < m; i++) {
u = sc.nextInt();
v = sc.nextInt();
list[u].add(v);
degree[u]--;
degree[v]++;
}
euler();
}
}
计算无向图的欧拉路经可以用“套圈法”、基于深度优先搜索来实现。从一个合适的顶点出发进行深度优先搜索。
当搜到一个顶点u时:
1.如果此时没有顶点与该顶点相连,那么就将u加入路径中并回溯;
2.如果有顶点v与顶点u相连,那么就删除无向边<u,v>,并继续搜索顶点v。将所有和u相邻的顶点遍历完之后,将u加入路径中。
无向图求出欧拉回路的节点:
package _不带权图;
import java.util.Scanner;
public class _无向图欧拉回路搜索 {
static int n, m, maxn = 100;
static int[][] mat = new int[maxn][maxn];
static int[] match = new int[maxn];// 顶点剩余的度
static void solve(int u) {
if (match[u] > 0) {
for (int i = 0; i < n; i++) {
if (mat[u][i] > 0) {
mat[u][i]--;
mat[i][u]--;
solve(i);
}
}
}
System.out.println(u);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
}
}
计算有向图的欧拉路经同样可以用“套圈法”。和无向图唯一不同的是,在记录方案的时候将顶点编号插入栈中,并在最后将栈中的元素依次输出。也就是说,将之前输出的顺序逆置。
有向图:
package _不带权图;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.Stack;
public class _欧拉回路搜索算法 {
static int n, m;
static int[] degree;
static LinkedList<Integer>[] list;
static Stack<Integer> stack=new Stack<>();
static void init() {
degree=new int[n+1];
list=new LinkedList[n+1];
for(int i=0;i<=n;i++)
list[i]=new LinkedList<Integer>();
}
//寻找欧拉回路的起点
public static int Euler() {
int first = 0, last = 0;
for (int i = 1; i <= n; i++) {
if (degree[i] < -1 || degree[i] > 1) {
return 0;
} else if (degree[i] == 1) {
if (first != 0)
return 0;
else
first = i;
} else if (degree[i] == -1) {
if (last != 0)
return 0;
else
last = i;
}
}
if (first == 0 && last == 0)
return 1;
else if (first != 0 && last != 0)
return first;
else
return 0;
}
//判断是否有欧拉回路
static boolean euler_find() {
int start;
start=Euler();
if(start==0)
return false;
else {
dfs(start);
return true;
}
}
//dfs一遍,找出欧拉路径,用栈存储路径节点
public static void dfs(int u) {
for(int i=0;i<list[u].size();i++) {
int v=list[u].get(i);
dfs(v);
}
stack.add(u);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
init();
for (int i = 0; i < m; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
list[u].add(v);
degree[v]++;
degree[u]--;
}
if(!euler_find()) {
System.out.println("No has");
}else {
while(!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}
}