赞
踩
01表示在0-1之间存在一条边,以此类推
- package com.graph.adjacencyMatrix;
-
- import java.io.*;//为了使用File类和异常抛出
- import java.util.ArrayList;
- import java.util.Scanner;//为了读取File里面的内容
-
- public class AdjMatrix {
-
- private int V;//顶点
- private int E;//边
- private int[][] adj;//邻接矩阵
-
- public AdjMatrix(String filename){
-
- //用绝对路径可以避免一些相对路径时的bug,这里他说的把“。。。”这里替换成filename就是相对路径但是会报错
- File file = new File("C:/Users/81909/Desktop/JavaSE/基础语法/g.txt");
-
- //捕获一个异常
- try(Scanner scanner = new Scanner(file)){//scanner存了file里面的内容
-
- V = scanner.nextInt();//根据Scanner声明时括号里的内容来决定从哪里读取一个整型数字,先读V
- if(V<0) throw new IllegalArgumentException("V需要是正数");
- adj = new int[V][V];//创建一个V*V的二维数组
-
- E = scanner.nextInt();//第二个读的是E
- if(E<0) throw new IllegalArgumentException("E需要是正数");
- for(int i = 0; i < E; i ++){//往矩阵里填1
- int a = scanner.nextInt();
- validateVertex(a);//判断顶点合法性
- int b = scanner.nextInt();
- validateVertex(b);
-
- if(a==b) throw new IllegalArgumentException("self loop is detected");//判断是否自环边
- if(adj[a][b]==1) throw new IllegalArgumentException("paralle edges are detected");//判断是否平行边
-
- adj[a][b] = 1;
- adj[b][a] = 1;
- }
- }
- catch(IOException e){
- e.printStackTrace();//栈中信息打印一下
- }
- }
-
- private void validateVertex(int v){
- if(v<0||v>=V) throw new IllegalArgumentException("vertex"+v+"is invalid");
- }
-
- public int V(){return V;};//之所以不直接把它俩定义为public,是为了不让用户修改变量
- public int E(){return E;};
-
-
- //判断两点直接是否有边
- public boolean hasEdge(int v,int w){
- validateVertex(v);
- validateVertex(w);
- return adj[v][w]==1;
- }
-
- //返回和顶点v相邻的边
- public ArrayList<Integer> adj(int v){
-
- validateVertex(v);
- ArrayList<Integer>res=new ArrayList<>();//将和v相邻的所有顶点存到res里然后反悔哦
- for (int i = 0; i < V; i++) {
- if(adj[v][i]==1)
- res.add(i);
- }
- return res;
- }
-
- //返回顶点相对的度
- public int degree(int v){
- return adj(v).size();
- }
-
- @Override
- public String toString(){
- StringBuilder sb = new StringBuilder();
-
- sb.append(String.format("V = %d, E = %d\n", V, E));//先说有多少顶点,有多少边
- for(int i = 0; i < V; i ++){
- for(int j = 0; j < V; j ++)
- sb.append(String.format("%d ", adj[i][j]));//把行列式给填充进去
- sb.append('\n');
- }
- return sb.toString();
- }
-
- public static void main(String[] args){
-
- AdjMatrix adjMatrix = new AdjMatrix("g.txt");
- System.out.print(adjMatrix);
- }
- }

结果
- V = 7, E = 9
- 0 1 0 1 0 0 0
- 1 0 1 0 0 0 1
- 0 1 0 1 0 1 0
- 1 0 1 0 1 0 0
- 0 0 0 1 0 1 0
- 0 0 1 0 1 0 1
- 0 1 0 0 0 1 0
-
- Process finished with exit code 0
- package com.graph.adjacencyMatrix;
-
- import java.util.LinkedList;//邻接表是链表
- import java.io.*;
- import java.util.Scanner;
-
- public class AdjList {
-
- private int V;
- private int E;
- private LinkedList<Integer>[] adj;
-
- public AdjList(String filename){
-
- File file = new File("C:/Users/81909/Desktop/JavaSE/基础语法/g.txt");
-
- try(Scanner scanner = new Scanner(file)){
-
- V = scanner.nextInt();
- if(V<0) throw new IllegalArgumentException("V需要是正数");
- adj = new LinkedList[V];//创建一个空间为V的链表
- for (int i = 0; i < V; i++) {
- adj[i]=new LinkedList<Integer>();//为每一个元素申请空间,不写Integer这个泛型也可以
- }
-
- E = scanner.nextInt();
- if(E<0) throw new IllegalArgumentException("E需要是正数");
- for(int i = 0; i < E; i ++){
- int a = scanner.nextInt();
- validateVertex(a);
- int b = scanner.nextInt();
- validateVertex(b);
-
- if(a==b) throw new IllegalArgumentException("self loop is detected");
- if(adj[a].contains(b)) throw new IllegalArgumentException("paralle edges are detected");
-
- adj[a].add(b);
- adj[b].add(a);
- }
- }
- catch(IOException e){
- e.printStackTrace();
- }
- }
-
- private void validateVertex(int v){
- if(v<0||v>=V) throw new IllegalArgumentException("vertex"+v+"is invalid");
- }
-
- public int V(){return V;};
- public int E(){return E;};
-
-
- //判断两点直接是否有边
- public boolean hasEdge(int v,int w){
- validateVertex(v);
- validateVertex(w);
- return adj[v].contains(w);
- }
-
- //返回和顶点v相邻的边
- public LinkedList<Integer> adj(int v){
-
- validateVertex(v);
- return adj[v];
- }
-
- //返回顶点相对的度
- public int degree(int v){
- return adj(v).size();
- }
-
- @Override
- public String toString(){
- StringBuilder sb = new StringBuilder();
-
- sb.append(String.format("V = %d, E = %d\n", V, E));//先说有多少顶点,有多少边
- for(int v = 0; v < V; v ++){
- sb.append(String.format("%d:",v));//每一轮v相邻的顶点都有谁
- for(int w:adj[v])
- sb.append(String.format("%d ", w));//把行列式给填充进去
- sb.append('\n');
- }
- return sb.toString();
- }
-
- public static void main(String[] args){
-
- AdjList adjList = new AdjList("g.txt");
- System.out.print(adjList);
- }
- }

结果
- V = 7, E = 9
- 0:1 3
- 1:0 2 6
- 2:1 3 5
- 3:0 2 4
- 4:3 5
- 5:2 4 6
- 6:1 5
-
- Process finished with exit code 0
有的只有顶点没有边,用O(E)代替不可取
- package com.graph.adjacencyMatrix;
-
- import java.io.File;
- import java.io.IOException;
- import java.util.TreeSet;
- import java.util.Scanner;
-
- public class AdjSet {
-
- private int V;
- private int E;
- private TreeSet<Integer>[] adj;
-
- public AdjSet(String pathStr){
-
- File file = new File(pathStr);
-
- try(Scanner scanner = new Scanner(file)){
-
- V = scanner.nextInt();
- if(V < 0) throw new IllegalArgumentException("V must be non-negative");
- adj = new TreeSet[V];
- for(int i = 0; i < V; i ++)
- adj[i] = new TreeSet<Integer>();
-
- E = scanner.nextInt();
- if(E < 0) throw new IllegalArgumentException("E must be non-negative");
-
- for(int i = 0; i < E; i ++){
- int a = scanner.nextInt();
- validateVertex(a);
- int b = scanner.nextInt();
- validateVertex(b);
-
- if(a == b) throw new IllegalArgumentException("Self Loop is Detected!");
- if(adj[a].contains(b)) throw new IllegalArgumentException("Parallel Edges are Detected!");
-
- adj[a].add(b);
- adj[b].add(a);
- }
- }
- catch(IOException e){
- e.printStackTrace();
- }
- }
-
- private void validateVertex(int v){
- if(v < 0 || v >= V)
- throw new IllegalArgumentException("vertex " + v + "is invalid");
- }
-
- public int V(){
- return V;
- }
-
- public int E(){
- return E;
- }
-
- public boolean hasEdge(int v, int w){
- validateVertex(v);
- validateVertex(w);
- return adj[v].contains(w);
- }
-
- public Iterable<Integer> adj(int v){
- // public TreeSet<Integer> adj(int v){
- validateVertex(v);
- return adj[v];
- }
-
- public int degree(int v){
- validateVertex(v);
- return adj[v].size();
- }
-
- @Override
- public String toString(){
- StringBuilder sb = new StringBuilder();
-
- sb.append(String.format("V = %d, E = %d\n", V, E));
- for(int v = 0; v < V; v ++){
- sb.append(String.format("%d : ", v));
- for(int w : adj[v])
- sb.append(String.format("%d ", w));
- sb.append('\n');
- }
- return sb.toString();
- }
-
- public static void main(String[] args){
-
- AdjSet adjSet = new AdjSet("g.txt");
- System.out.print(adjSet);
- }
- }

树与图的优先遍历比较
- package com.graph.GraphDFS;
-
- import java.util.ArrayList;
-
- public class GraphDFS {
-
- private Graph G;
- private boolean[] visited;
-
- private ArrayList<Integer> order = new ArrayList<>();
-
- public GraphDFS(Graph G){//构造函数
-
- this.G = G;
- visited = new boolean[G.V()];//图中每一个顶点对应一个visited记录值
- dfs(0);
- }
-
- //递归遍历
- private void dfs(int v){
-
- visited[v] = true;
- order.add(v);
- for(int w: G.adj(v))
- if(!visited[w])
- dfs(w);
- }
-
- public Iterable<Integer> order(){
- return order;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("C:/Users/81909/Desktop/JavaSE/基础语法/g.txt");
- GraphDFS graphDFS = new GraphDFS(g);
- System.out.println(graphDFS.order());
- }
- }

结果
[0, 1, 3, 2, 6, 5, 4]
- package com.graph.GraphDFS;
-
- import java.util.ArrayList;
-
- public class GraphDFS {
-
- private Graph G;
- private boolean[] visited;
-
- private ArrayList<Integer> order = new ArrayList<>();
-
- public GraphDFS(Graph G){//构造函数
-
- this.G = G;
- visited = new boolean[G.V()];//图中每一个顶点对应一个visited记录值
- for (int v = 0; v < G.V(); v++) {
- if(!visited[v])
- dfs(v);
- }
-
- }
-
- //递归遍历
- private void dfs(int v){
-
- visited[v] = true;
- order.add(v);
- for(int w: G.adj(v))
- if(!visited[w])
- dfs(w);
- }
-
- public Iterable<Integer> order(){
- return order;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("C:/Users/81909/Desktop/JavaSE/基础语法/g.txt");
- GraphDFS graphDFS = new GraphDFS(g);
- System.out.println(graphDFS.order());
- }
- }

对象与结果
- 7 6
- 0 1
- 0 2
- 1 3
- 1 4
- 2 3
- 2 6
-
-
- [0, 1, 3, 2, 6, 4, 5]
- package com.graph.GraphDFS;
-
- import java.util.ArrayList;
-
- public class GraphDFS {
-
- private Graph G;
- private boolean[] visited;
-
- private ArrayList<Integer> pre = new ArrayList<>();
- private ArrayList<Integer> post = new ArrayList<>();
-
- public GraphDFS(Graph G){//构造函数
-
- this.G = G;
- visited = new boolean[G.V()];//图中每一个顶点对应一个visited记录值
- for (int v = 0; v < G.V(); v++) {
- if(!visited[v])
- dfs(v);
- }
-
- }
-
- //递归遍历
- private void dfs(int v){
-
- visited[v] = true;
- pre.add(v);
- for(int w: G.adj(v))
- if(!visited[w])
- dfs(w);
-
- post.add(v);
- }
-
- public Iterable<Integer> pre(){
- return pre;
- }
-
- public Iterable<Integer> post(){
- return post;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("C:/Users/81909/Desktop/JavaSE/基础语法/g.txt");
- GraphDFS graphDFS = new GraphDFS(g);
- System.out.println(graphDFS.pre());
- System.out.println(graphDFS.post());
- }
- }

结果
- [0, 1, 3, 2, 6, 4, 5]
- [6, 2, 3, 4, 1, 0, 5]
更进一步
- import java.util.ArrayList;
-
- public class CC {
-
- private Graph G;
- private int[] visited;
- private int cccount = 0;
-
- public CC(Graph G){
-
- this.G = G;
- visited = new int[G.V()];
- for(int i = 0; i < visited.length; i ++)
- visited[i] = -1;
-
- for(int v = 0; v < G.V(); v ++)
- if(visited[v] == -1){
- dfs(v, cccount);
- cccount ++;
- }
- }
-
- private void dfs(int v, int ccid){
-
- visited[v] = ccid;
- for(int w: G.adj(v))
- if(visited[w] == -1)
- dfs(w, ccid);
- }
-
- public int count(){
- // for(int e: visited)
- // System.out.print(e + " ");
- // System.out.println();
- return cccount;
- }
-
- //判断俩顶点是否在一个联通分量里面
- public boolean isConnected(int v, int w){
- G.validateVertex(v);
- G.validateVertex(w);
- return visited[v] == visited[w];
- }
-
- //返回每张图有多少联通分量,每个联通分量对应的顶点分别是谁
- public ArrayList<Integer>[] components(){
-
- ArrayList<Integer>[] res = new ArrayList[cccount];
- for(int i = 0; i < cccount; i ++)
- res[i] = new ArrayList<Integer>();
-
- for(int v = 0; v < G.V(); v ++)
- res[visited[v]].add(v);
- return res;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- CC cc = new CC(g);
- System.out.println(cc.count());
-
- System.out.println(cc.isConnected(0, 6));
- System.out.println(cc.isConnected(5, 6));
-
- ArrayList<Integer>[] comp = cc.components();
- for(int ccid = 0; ccid < comp.length; ccid ++){
- System.out.print(ccid + " : ");
- for(int w: comp[ccid])
- System.out.print(w + " ");
- System.out.println();
- }
- }
- }

- import java.util.ArrayList;
- import java.util.Collections;
-
- public class SingleSourcePath {
-
- private Graph G;
- private int s;
-
- private boolean[] visited;
- private int[] pre;
-
- public SingleSourcePath(Graph G, int s){
-
- G.validateVertex(s);
-
- this.G = G;
- this.s = s;
- visited = new boolean[G.V()];
- pre = new int[G.V()];
-
- dfs(s, s);
- }
-
- private void dfs(int v, int parent){
-
- visited[v] = true;
- pre[v] = parent;
- for(int w: G.adj(v))
- if(!visited[w])
- dfs(w, v);
- }
-
- //判断是不是和源在一起
- public boolean isConnectedTo(int t){
- G.validateVertex(t);
- return visited[t];
- }
-
- //输出路径
- public Iterable<Integer> path(int t){
-
- ArrayList<Integer> res = new ArrayList<Integer>();
- if(!isConnectedTo(t)) return res;//不能到达路径就返回空路径
-
- int cur = t;
- while(cur != s){
- res.add(cur);
- cur = pre[cur];
- }
- res.add(s);
-
- Collections.reverse(res);//颠倒过来,返回正序
- return res;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- SingleSourcePath sspath = new SingleSourcePath(g, 0);
- System.out.println("0 -> 6 : " + sspath.path(6));
- System.out.println("0 -> 5 : " + sspath.path(5));
- }
- }

- import java.util.ArrayList;
- import java.util.Collections;
-
- public class Path {
-
- private Graph G;
- private int s, t;
-
- private int[] pre;
- private boolean[] visited;
-
- public Path(Graph G, int s, int t){//源点与终止点
-
- G.validateVertex(s);
- G.validateVertex(t);
-
- this.G = G;
- this.s = s;
- this.t = t;
-
- visited = new boolean[G.V()];
- pre = new int[G.V()];
- for(int i = 0; i < pre.length; i ++)
- pre[i] = -1;
-
- dfs(s, s);
- for(boolean e: visited)
- System.out.print(e + " ");
- System.out.println();
- }
-
- private boolean dfs(int v, int parent){
-
- visited[v] = true;
- pre[v] = parent;
-
- if(v == t) return true;
-
- for(int w: G.adj(v))
- if(!visited[w])
- if(dfs(w, v))
- return true;
- return false;
- }
-
- public boolean isConnected(){
- return visited[t];
- }
-
- public Iterable<Integer> path(){
-
- ArrayList<Integer> res = new ArrayList<Integer>();
- if(!isConnected()) return res;
-
- int cur = t;
- while(cur != s){
- res.add(cur);
- cur = pre[cur];
- }
- res.add(s);
-
- Collections.reverse(res);
- return res;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- Path path = new Path(g, 0, 6);
- System.out.println("0 -> 6 : " + path.path());
-
- Path path2 = new Path(g, 0, 5);
- System.out.println("0 -> 5 : " + path2.path());
-
- Path path3 = new Path(g, 0, 1);
- System.out.println("0 -> 1 : " + path3.path());
- }
- }

- public class CycleDetection {
-
- private Graph G;
- private boolean[] visited;
- private boolean hasCycle = false;
-
- public CycleDetection(Graph G){
-
- this.G = G;
- visited = new boolean[G.V()];
- for(int v = 0; v < G.V(); v ++)
- if(!visited[v])
- if(dfs(v, v)){
- hasCycle = true;
- break;
- }
- }
-
- // 从顶点 v 开始,判断图中是否有环
- private boolean dfs(int v, int parent){
-
- visited[v] = true;
- for(int w: G.adj(v))
- if(!visited[w]){
- if(dfs(w, v)) return true;
- }
- else if(w != parent)
- return true;
- return false;
- }
-
- public boolean hasCycle(){
- return hasCycle;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- CycleDetection cycleDetection = new CycleDetection(g);
- System.out.println(cycleDetection.hasCycle());
-
- Graph g2 = new Graph("g2.txt");
- CycleDetection cycleDetection2 = new CycleDetection(g2);
- System.out.println(cycleDetection2.hasCycle());
- }
- }

- import java.util.ArrayList;
-
- public class BipartitionDetection {
-
- private Graph G;
-
- private boolean[] visited;
- private int[] colors;
- private boolean isBipartite = true;
-
- public BipartitionDetection(Graph G){
-
- this.G = G;
- visited = new boolean[G.V()];
- colors = new int[G.V()];
- for(int i = 0; i < G.V(); i ++)
- colors[i] = -1;
-
- for(int v = 0; v < G.V(); v ++)
- if(!visited[v])
- if(!dfs(v, 0)){
- isBipartite = false;
- break;
- }
- }
-
- private boolean dfs(int v, int color){
-
- visited[v] = true;
- colors[v] = color;
- for(int w: G.adj(v))
- if(!visited[w]){
- if(!dfs(w, 1 - color)) return false;
- }
- else if(colors[w] == colors[v])
- return false;
- return true;
- }
-
- public boolean isBipartite(){
- return isBipartite;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- BipartitionDetection bipartitionDetection = new BipartitionDetection(g);
- System.out.println(bipartitionDetection.isBipartite);
- // true
-
- Graph g2 = new Graph("g2.txt");
- BipartitionDetection bipartitionDetection2 = new BipartitionDetection(g2);
- System.out.println(bipartitionDetection2.isBipartite);
- // false
-
- Graph g3 = new Graph("g3.txt");
- BipartitionDetection bipartitionDetection3 = new BipartitionDetection(g3);
- System.out.println(bipartitionDetection3.isBipartite);
- // true
- }
- }

- import java.util.ArrayList;
- import java.util.LinkedList;
- import java.util.Queue;
-
- public class GraphBFS {
-
- private Graph G;
- private boolean[] visited;
-
- private ArrayList<Integer> order = new ArrayList<>();//广度优先没有前序后序,这里order代表遍历顺序
-
- public GraphBFS(Graph G){
-
- this.G = G;
- visited = new boolean[G.V()];//为visited开空间,有多少顶点就开多少个
- for(int v = 0; v < G.V(); v ++)//如果从0遍历就只能遍历0所在联通分量,所以需要从所有节点开始
- if(!visited[v])
- bfs(v);
- }
-
- private void bfs(int s){
-
- Queue<Integer> queue = new LinkedList<>();//Queue是一个接口,所以要具体一个实践的类,这里用链表类
- queue.add(s);
- visited[s] = true;//入队了就visited=true
- while(!queue.isEmpty()){//链表队列不为空时
- int v = queue.remove();//队首取出元素v
- order.add(v);//添加到order这个队列中
-
- for(int w: G.adj(v))//查看v顶点所有相邻顶点w
- if(!visited[w]){
- queue.add(w);//如果w没被遍历过,入队
- visited[w] = true;//设为已访问
- }
- }
- }
-
- public Iterable<Integer> order(){
- return order;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- GraphBFS graphBFS = new GraphBFS(g);
- System.out.println("BFS Order : " + graphBFS.order());
- }
- }

- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.LinkedList;
- import java.util.Queue;
-
- public class SingleSourcePath {
-
- private Graph G;
- private int s;
-
- private boolean[] visited;
- private int[] pre;
- //没有顺序问题,所以去除了BFS中的ArrayList<>order
- public SingleSourcePath(Graph G, int s){//创建对象时考虑源是谁
-
- this.G = G;
- this.s = s;
-
- visited = new boolean[G.V()];
- pre = new int[G.V()];//路径问题,记录pre这个信息,给pre开空间
- for(int i = 0; i < pre.length; i ++)//再赋初值
- pre[i] = -1;
-
- bfs(s);//单源问题,只需要从s出发就行
- }
-
- private void bfs(int s){
-
- Queue<Integer> queue = new LinkedList<>();
- queue.add(s);
- visited[s] = true;
- pre[s] = s;
- while(!queue.isEmpty()){
- int v = queue.remove();
-
- for(int w: G.adj(v))//求v的相邻节点w
- if(!visited[w]){
- queue.add(w);
- visited[w] = true;
- pre[w] = v;//w上一个节点是v
- }
- }
- }
-
- public boolean isConnectedTo(int t){//判断是不是连接到了t节点
- G.validateVertex(t);
- return visited[t];
- }
-
- public Iterable<Integer> path(int t){//求解路径
-
- ArrayList<Integer> res = new ArrayList<Integer>();//定义res用来之后存路径
- if(!isConnectedTo(t)) return res;//到达不了t就返回空
-
- int cur = t;//从终值t出发,记为cur
- while(cur != s){//只要还没回到起点s
- res.add(cur);//往存路径的res里add一个cur
- cur = pre[cur];//往前面一个点跑
- }
- res.add(s);//最后添加源点s
-
- Collections.reverse(res);//反转一下
- return res;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- SingleSourcePath sspath = new SingleSourcePath(g, 0);
- System.out.println("0 -> 6 : " + sspath.path(6));
- }
- }

- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.LinkedList;
- import java.util.Queue;
-
- // Unweighted Single Source Shortest Path
- public class USSSPath {
-
- private Graph G;
- private int s;
-
- private boolean[] visited;
- private int[] pre;
- private int[] dis;
-
- public USSSPath(Graph G, int s){
-
- this.G = G;
- this.s = s;
-
- visited = new boolean[G.V()];
- pre = new int[G.V()];
- dis = new int[G.V()];//给dis看空间
- for(int i = 0; i < G.V(); i ++) {//给他俩所有值初始化
- pre[i] = -1;
- dis[i] = -1;
- }
- bfs(s);
-
- for(int i = 0; i < G.V(); i ++)//遍历所有点,打印所有点的dis值,不联通时值为-1
- System.out.print(dis[i] + " ");
- System.out.println();
- }
-
- private void bfs(int s){
-
- Queue<Integer> queue = new LinkedList<>();
- queue.add(s);
- visited[s] = true;
- pre[s] = s;
- dis[s] = 0;//源点s到s的距离初始化为0
- while(!queue.isEmpty()){
- int v = queue.remove();
-
- for(int w: G.adj(v))
- if(!visited[w]){
- queue.add(w);
- visited[w] = true;
- pre[w] = v;
- dis[w] = dis[v] + 1;//距离为源点到v的距离+1
- }
- }
- }
-
- public boolean isConnectedTo(int t){
- G.validateVertex(t);
- return visited[t];
- }
-
- public int dis(int t){//源点到目标t的最短路径长度
- G.validateVertex(t);
- return dis[t];
- }
-
- public Iterable<Integer> path(int t){//源点到目标t的最短路径
-
- ArrayList<Integer> res = new ArrayList<Integer>();
- if(!isConnectedTo(t)) return res;
-
- int cur = t;
- while(cur != s){
- res.add(cur);
- cur = pre[cur];
- }
- res.add(s);
-
- Collections.reverse(res);
- return res;
- }
-
- public static void main(String[] args){
-
- Graph g = new Graph("g.txt");
- USSSPath ussspath = new USSSPath(g, 0);
- System.out.println("0 -> 6 : " + ussspath.path(6));
- System.out.println("0 -> 6 : " + ussspath.dis(6));
- }
- }

输入
- 7 12
- 0 1 2
- 0 3 7
- 0 5 2
- 1 2 1
- 1 3 4
- 1 4 3
- 1 5 5
- 2 4 4
- 2 5 4
- 3 4 1
- 3 6 5
- 4 6 7
代码
- import java.io.File;
- import java.io.IOException;
- import java.util.Map;
- import java.util.TreeMap;
- import java.util.Scanner;
-
-
- /// 暂时只支持无向带权图
- public class WeightedGraph implements Cloneable{
-
- private int V;
- private int E;
- private TreeMap<Integer, Integer>[] adj;//TreeMap比TreeSet多存一个值,可以当权,<顶点,权值类型>
- public WeightedGraph(String filename){
-
- File file = new File(filename);
-
- try(Scanner scanner = new Scanner(file)){
-
- V = scanner.nextInt();//读取顶点数
- if(V < 0) throw new IllegalArgumentException("V must be non-negative");
- adj = new TreeMap[V];//定义一个数组
- for(int i = 0; i < V; i ++)//为每个TreeMap开空间
- adj[i] = new TreeMap<Integer, Integer>();
-
- E = scanner.nextInt();
- if(E < 0) throw new IllegalArgumentException("E must be non-negative");
-
- for(int i = 0; i < E; i ++){
- int a = scanner.nextInt();
- validateVertex(a);
- int b = scanner.nextInt();
- validateVertex(b);
- int weight = scanner.nextInt();//读取权值
-
- if(a == b) throw new IllegalArgumentException("Self Loop is Detected!");
- if(adj[a].containsKey(b)) throw new IllegalArgumentException("Parallel Edges are Detected!");
- //adj的TreeMap里不包含以b元素为键的数据对,带权图处理平行边挺常见的,如果两个点直接有多条边,保留最短边
-
- adj[a].put(b, weight);//从a-b之间有一条边,权值为weight
- adj[b].put(a, weight);
- }
- }
- catch(IOException e){
- e.printStackTrace();
- }
- }
-
- public void validateVertex(int v){
- if(v < 0 || v >= V)
- throw new IllegalArgumentException("vertex " + v + "is invalid");
- }
-
- public int V(){
- return V;
- }
-
- public int E(){
- return E;
- }
-
- public boolean hasEdge(int v, int w){
- validateVertex(v);
- validateVertex(w);
- return adj[v].containsKey(w);
- }
-
- public Iterable<Integer> adj(int v){
- validateVertex(v);
- //不能直接返回TreeMap类的adj[v],TreeMap中封装了一个keySet方法,可以返回所有的键对应的集合
- return adj[v].keySet();
- }
-
- public int getWeight(int v, int w){//获得v-w边所对应的权值
-
- if(hasEdge(v, w)) return adj[v].get(w);//有边抛权,无边抛异常
- throw new IllegalArgumentException(String.format("No edge %d-%d", v, w));
- }
-
- public int degree(int v){
- validateVertex(v);
- return adj[v].size();//返回v所对顶点对应的度
- }
-
- //带权图基本不会有删边操作,把这个和clone移除基本不会影响
- public void removeEdge(int v, int w){
- validateVertex(v);
- validateVertex(w);
- if(adj[v].containsKey(w)) E --;
- adj[v].remove(w);
- adj[w].remove(v);
- }
-
- @Override
- public Object clone(){
-
- try{
- WeightedGraph cloned = (WeightedGraph) super.clone();
- cloned.adj = new TreeMap[V];
- for(int v = 0; v < V; v ++){
- cloned.adj[v] = new TreeMap<Integer, Integer>();//对每一个adj[v]开空间
- //遍历TreeMap,遍历它所有的键;Map接口下封装的Entry类,从adj[v]这个TreeMap中,
- // entrySet里面存的就是一个个键值数据对,把键和对应值放入entry中
- for(Map.Entry<Integer, Integer> entry: adj[v].entrySet())
- //adj[v]中put(键,值)
- cloned.adj[v].put(entry.getKey(), entry.getValue());
- }
- return cloned;
- }
- catch (CloneNotSupportedException e){
- e.printStackTrace();
- }
- return null;
- }
-
- @Override
- public String toString(){
- StringBuilder sb = new StringBuilder();
-
- sb.append(String.format("V = %d, E = %d\n", V, E));
- for(int v = 0; v < V; v ++){
- sb.append(String.format("%d : ", v));
- for(Map.Entry<Integer, Integer> entry: adj[v].entrySet())//从adj[v].entrySet()中取出键值数据对
- sb.append(String.format("(%d: %d) ", entry.getKey(), entry.getValue()));
- sb.append('\n');
- }
- return sb.toString();
- }
-
- public static void main(String[] args){
-
- WeightedGraph g = new WeightedGraph("g.txt");
- System.out.print(g);
- }
- }

运行结果
从最小权值开始选边
用了4的这条边,就导致和其他小边构成了一个环
贪心算法难点在于,它为什么是对的
可以用用数学归纳法证明
- public class WeightedEdge implements Comparable<WeightedEdge>{
-
- private int v, w, weight;
-
- public WeightedEdge(int v, int w, int weight){
- this.v = v;
- this.w = w;
- this.weight = weight;
- }
-
- public int getV(){return v;}
-
- public int getW(){return w;}
-
- public int getWeight(){return weight;}
-
- @Override
- public int compareTo(WeightedEdge another){
- return weight - another.weight;
- }
-
- @Override
- public String toString(){
- return String.format("(%d-%d: %d)", v, w, weight);
- }
- }

- import java.util.ArrayList;
-
- public class CC {//求带权图的联通分量个数
-
- private WeightedGraph G;
- private int[] visited;
- private int cccount = 0;
-
- public CC(WeightedGraph G){
-
- this.G = G;
- visited = new int[G.V()];
- for(int i = 0; i < visited.length; i ++)
- visited[i] = -1;
-
- for(int v = 0; v < G.V(); v ++)
- if(visited[v] == -1){
- dfs(v, cccount);
- cccount ++;
- }
- }
-
- private void dfs(int v, int ccid){
-
- visited[v] = ccid;
- for(int w: G.adj(v))
- if(visited[w] == -1)
- dfs(w, ccid);
- }
-
- public int count(){
- return cccount;
- }
-
- public boolean isConnected(int v, int w){
- G.validateVertex(v);
- G.validateVertex(w);
- return visited[v] == visited[w];
- }
-
- public ArrayList<Integer>[] components(){
-
- ArrayList<Integer>[] res = new ArrayList[cccount];
- for(int i = 0; i < cccount; i ++)
- res[i] = new ArrayList<Integer>();
-
- for(int v = 0; v < G.V(); v ++)
- res[visited[v]].add(v);
- return res;
- }
-
- }

- import java.util.ArrayList;
- import java.util.Collections;
-
- public class Kruskal {
-
- private WeightedGraph G;
- private ArrayList<WeightedEdge> mst;//最小生成树返回的是用ArrayList存储的一个个边
-
- public Kruskal(WeightedGraph G){//用户传来一个带权图G
-
- this.G = G;
- mst = new ArrayList<>();//开空间
-
- CC cc = new CC(G);//带权图中联通分量个数
- if(cc.count() > 1) return;//有断开区域,最小生成树是null
-
- //Kruskal
-
- ArrayList<WeightedEdge> edges = new ArrayList<>();//存储所有的边
- for(int v = 0; v < G.V(); v ++)//把所有边放入edges中
- for(int w: G.adj(v))
- if(v < w)//防止类似0-2,2-0重复遍历的问题
- edges.add(new WeightedEdge(v, w, G.getWeight(v, w)));
-
- Collections.sort(edges);//把边传进去排序,记得设定WeightedEdge为可比较的类
-
- //判断是否行成环
- UF uf = new UF(G.V());//有几个顶点UF中就有多少元素
- for(WeightedEdge edge: edges){
- int v = edge.getV();
- int w = edge.getW();
- if(!uf.isConnected(v, w)){//v和w不在一个集合时才连成边
- mst.add(edge);
- uf.unionElements(v, w);
- }
- }
- }
-
- public ArrayList<WeightedEdge> result(){
- return mst;
- }//返回最小生成树
-
- public static void main(String[] args){
-
- WeightedGraph g = new WeightedGraph("g.txt");
- Kruskal kruskal = new Kruskal(g);
- System.out.println(kruskal.result());
- }
- }

- public class UF{
-
- private int[] parent;
-
- public UF(int n){
-
- parent = new int[n];
- for(int i = 0 ; i < n ; i ++)
- parent[i] = i;
- }
-
- public int find(int p){
- if( p != parent[p] )
- parent[p] = find( parent[p] );
- return parent[p];
- }
-
- public boolean isConnected(int p , int q){
- return find(p) == find(q);
- }
-
- public void unionElements(int p, int q){
-
- int pRoot = find(p);
- int qRoot = find(q);
-
- if( pRoot == qRoot )
- return;
-
- parent[pRoot] = qRoot;
- }
- }

- import java.util.ArrayList;
-
- public class Prim {
-
- private WeightedGraph G;
- private ArrayList<WeightedEdge> mst;
-
- public Prim(WeightedGraph G){
-
- this.G = G;
- mst = new ArrayList<>();
-
- CC cc = new CC(G);
- if(cc.count() > 1) return;//判断联通图
-
- //Prim
- boolean[] visited = new boolean[G.V()];//切分在初始时,只有一个点切了
- visited[0] = true;
- for(int i = 1; i < G.V(); i ++){//共切V-1次
-
- WeightedEdge minEdge = new WeightedEdge(-1, -1, Integer.MAX_VALUE);//声明一个最大权值,用来用后面的小权值覆盖它
- for(int v = 0; v < G.V(); v ++)//每次切的时候扫描所有的边
- if(visited[v])//当一个边访问过了,再访问它所有的邻边
- for(int w: G.adj(v))
- if(!visited[w] && G.getWeight(v, w) < minEdge.getWeight())//如果没访问过,且权值小于minEdge
- minEdge = new WeightedEdge(v, w, G.getWeight(v, w));//更新一下minEdge这条边
- mst.add(minEdge);
- visited[minEdge.getV()] = true;
- visited[minEdge.getW()] = true;
- }
- }
-
- public ArrayList<WeightedEdge> result(){
- return mst;
- }
-
- public static void main(String[] args){
-
- WeightedGraph g = new WeightedGraph("g.txt");
- Prim prim = new Prim(g);
- System.out.println(prim.result());
- }
- }

输入
- 7 12
- 0 1 2
- 0 3 7
- 0 5 2
- 1 2 1
- 1 3 4
- 1 4 3
- 1 5 5
- 2 4 4
- 2 5 4
- 3 4 1
- 3 6 5
- 4 6 7
输出
优先队列中所有的横切边不一定合法
这里的1-5,2-5,使用时先判断,非法的话直接扔掉就行
- import java.util.ArrayList;
- import java.util.Queue;
- import java.util.PriorityQueue;
-
- public class Prim {
-
- private WeightedGraph G;
- private ArrayList<WeightedEdge> mst;
-
- public Prim(WeightedGraph G){
-
- this.G = G;
- mst = new ArrayList<>();
-
- CC cc = new CC(G);
- if(cc.count() > 1) return;//判断联通图
-
- //Prim
-
- boolean visited[] = new boolean[G.V()];//切分在初始时,只有一个点切了
- visited[0] = true;
- Queue pq = new PriorityQueue<WeightedEdge>();//声明优先队列,里面存储带权边
- for(int w: G.adj(0))//添加初始状态下的横切边,即所有和0相邻的边
- pq.add(new WeightedEdge(0, w, G.getWeight(0, w)));
-
- while(!pq.isEmpty()){//如果用V-1个循环来弄的话还要判断是否合法
-
- WeightedEdge minEdge = (WeightedEdge) pq.remove();//这个minEdge有可能是最短横切边
- if(visited[minEdge.getV()] && visited[minEdge.getW()])//如果minEdge两个端点都访问过,则非法,跳过它
- continue;
-
- mst.add(minEdge);//合法边加入mst最小生成树中
-
- //拓展切分,有可能产生新的最小横切边
- int newv = visited[minEdge.getV()] ? minEdge.getW() : minEdge.getV();//判断哪个点没有被访问
- visited[newv] = true;//没有访问的那个点设为已访问
- for(int w: G.adj(newv))//取出newv的所有邻边
- if(!visited[w])
- //把有可能的最小横切边加入优先队列,在下一次的循环中从优先队列中取出最小的那个边
- pq.add(new WeightedEdge(newv, w, G.getWeight(newv, w)));
- }
- }
-
- public ArrayList<WeightedEdge> result(){
- return mst;
- }
-
- public static void main(String[] args){
-
- WeightedGraph g = new WeightedGraph("g.txt");
- Prim prim = new Prim(g);
- System.out.println(prim.result());
- }
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。