蓝桥杯备赛第三篇(图论)

1.邻接表

	static class Edge {
        int next;
        int value;
        public Edge(int next, int value) {
            this.next = next;
            this.value = value;
        }
    }
    static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();
    public static void addEgde(int from, int to, int value) {
        if (!graph.containsKey(from)) {
            graph.put(from, new LinkedList<>());
        }
        if (!graph.containsKey(to)) {
            graph.put(to, new LinkedList<>());
        }
        graph.get(from).add(new Edge(to, value));
        graph.get(to).add(new Edge(from, value));
    }

2.dijkstra

求单源最短路,不能求带有负权边

import java.util.*;
public class Main{
    static class Edge {
        int next;
        int value;
        public Edge(int next, int value) {
            this.next = next;
            this.value = value;
        }
    }
    static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();
    //用于记录起始点到每个点的最短距离
    static int[] distance;
    //用于记录每个点是否被标记过,如果被标记过说明这个点已经是最短路径了
    static boolean[] visited;
    static int N;
    static int M;
    public static void addEgde(int from, int to, int value) {
        if (!graph.containsKey(from)) {
            graph.put(from, new LinkedList<>());
        }
        if (!graph.containsKey(to)) {
            graph.put(to, new LinkedList<>());
        }
        graph.get(from).add(new Edge(to, value));
        graph.get(to).add(new Edge(from, value));
    }
    public static void dijkstra(int start) {
        Arrays.fill(distance, Integer.MAX_VALUE);
        Arrays.fill(visited, false);
        distance[start] = 0;
        int N = graph.size();
        for (int i = 0; i < N; i++) {
            //1.找到distance的最小值
            int min = Integer.MAX_VALUE;
            int min_index = -1;
            for (int j = 0; j < distance.length; j++) {
                if (!visited[j] && min > distance[j]) {
                    min = distance[j];
                    min_index = j;
                }
            }
            if (min_index == -1) {
                return;
            }
            //2.标记min_index=true
            visited[min_index] = true;
            //3.更新从min_index出发的边
            LinkedList<Edge> edges = graph.get(min_index);
            for (int j = 0; j < edges.size(); j++) {
                int next = edges.get(j).next;
                int value = edges.get(j).value;
                if (!visited[next] && distance[min_index] + value < distance[next]) {
                    distance[next] = distance[min_index] + value;
                }
            }
        }
        //输出结果
        System.out.println(Arrays.toString(distance));
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        int start = scanner.nextInt();
        for (int i = 0; i < 9; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            int value = scanner.nextInt();
            addEgde(from, to, value);
        }
        distance = new int[N + 1];
        visited = new boolean[N + 1];
        dijkstra(start);
    }
}

 3.dijkstra优化版本

import java.util.*;
public class Main {
    static class Edge {
        int next;
        int value;
        public Edge(int next, int value) {
            this.next = next;
            this.value = value;
        }
    }
    static class Distance implements Comparable {
        int node;
        int dis;
        public Distance(int node, int dis) {
            this.node = node;
            this.dis = dis;
        }
        @Override
        public int compareTo(Object o) {
            Distance object = (Distance) o;
            return Integer.compare(this.dis, object.dis);
        }
    }
    static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();
    static int N;
    static int M;
    static int[] distance;
    static boolean[] visited;
    static PriorityQueue<Distance> queue = new PriorityQueue<>();
    public static void addEdge(int from, int to, int value) {
        if (!graph.containsKey(from)) {
            graph.put(from, new LinkedList<>());
        }
        if (!graph.containsKey(to)) {
            graph.put(to, new LinkedList<>());
        }
        graph.get(from).add(new Edge(to, value));
        graph.get(to).add(new Edge(from, value));
    }
    public static void dijkstra(int start) {
        Arrays.fill(distance, Integer.MAX_VALUE);
        distance[start] = 0;
        //0.初始优先队列
        queue.add(new Distance(start, 0));
        while (!queue.isEmpty()) {
            //1.从优先队列中弹出优先级最高的,找到最小的distance,然后标记
            Distance dis = queue.poll();
            if (visited[dis.node]) continue;
            int min_index = dis.node;
            int min_dis = dis.dis;
            visited[min_index] = true;
            //2.更新从min发出的所有边
            LinkedList<Edge> edges = graph.get(min_index);
            for (Edge edge : edges) {
                int next = edge.next;
                int value = edge.value;
                if (!visited[next] && distance[next] > min_dis + value) {
                    distance[next] = min_dis + value;
                    //3.压入优先队列
                    //虽然优先队列中会出现一个点的多个distance,但是后面的用不到
                    queue.add(new Distance(next, distance[next]));
                }
            }
        }
        for (int i = 1; i < distance.length; i++) {
            System.out.print(distance[i] + " ");
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        int start = scanner.nextInt();
        for (int i = 0; i < M; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            int value = scanner.nextInt();
            addEdge(from, to, value);
        }
        distance = new int[N + 1];
        visited = new boolean[N + 1];
        dijkstra(start);
    }
}

4.Floyd

作用:可以用于求带负权边的多源最短路径,并且只能用于邻接矩阵。
Floyd是采用动态规划的思想实现的。
设dp[k][i][j]表示只用前 k 个点时,i 到 j 之间的最短路径是dp[k][i][j]
初始化:dp[0][i][j]表示点与点之间的距离
状态转移:dp[k][i][j] = min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]),注意k是最外层循环
然后进行空间压缩:dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j])

 

import java.util.*;
public class Main{
    static int N;
    static int M;
    static int[][] distance;
    public static void floyd() {
        for (int k = 1; k <= N; k++) {
            for (int i = 1; i <= N; i++) {
                for (int j = 1; j <= N; j++) {
                    distance[i][j] = Math.min(distance[i][j], distance[i][k] + distance[k][j]);
                }
            }
        }
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= N; j++) {
                System.out.print(distance[i][j] + " ");
            }
            System.out.println();
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        distance = new int[N + 1][N + 1];
        //初始化
        for (int i = 1; i < N + 1; i++) {
            Arrays.fill(distance[i], Integer.MAX_VALUE);
        }
        for (int i = 1; i <= N; i++) {
            distance[i][i] = 0;
        }
        //输入数据
        for (int i = 0; i < N; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            int value = scanner.nextInt();
            distance[from][to] = value;
            distance[to][from] = value;
        }
        floyd();
    }
}

5.SPFA

作用:
1.用于求带负权的边的单元最短路径,但是不能有负环
2.可以用来判断是否存在负环
判断负环:给每一个节点记录一个count,用来记录该结点入队的次数,如果count>n就说明存在负环

import java.util.*;
public class SPFA {
    static class Edge {
        int next;
        int value;
        public Edge(int next, int value) {
            this.next = next;
            this.value = value;
        }
    }
    static int[] distance;
    static int[] count;
    static HashMap<Integer, LinkedList<Edge>> graph = new HashMap<>();
    static int N;
    static int M;
    public static void addNode(int node) {
        if (!graph.containsKey(node)) {
            graph.put(node, new LinkedList<>());
        }
    }
    public static void addEdge(int from, int to, int value) {
        if (!graph.containsKey(from)) {
            addNode(from);
        }
        if (!graph.containsKey(to)) {
            addNode(to);
        }
        graph.get(from).add(new Edge(to, value));
        graph.get(to).add(new Edge(from, value));
    }
    public static void spfa(int start) {
        Arrays.fill(distance, Integer.MAX_VALUE);
        distance[start] = 0;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.add(start);
        count[start] = 1;
        while (!queue.isEmpty()) {
            //1.出队
            int node = queue.removeFirst();
            LinkedList<Edge> edges = graph.get(node);
            //2.遍历所有出边
            for (Edge edge : edges) {
                int next = edge.next;
                int value = edge.value;
                //3.如果可以更新则更新
                if (distance[next] > distance[node] + value) {
                    distance[next] = distance[node] + value;
                    //4.发现没在队列里面就添加进去
                    if (!queue.contains(next)) {
                        queue.add(next);
                        //5.判断是否有负环
                        count[next]++;
                        if (count[next] > N) {
                            System.out.println("存在负环");
                            return;
                        }
                    }
                }
            }
        }
        System.out.println(Arrays.toString(distance));
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        for (int i = 0; i < M; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            int value = scanner.nextInt();
            addEdge(from, to, value);
        }
        distance = new int[N + 1];
        count = new int[N + 1];
        spfa(1);
    }
}

6.拓扑排序

拓扑排序只能用于有向无环图。如果程序结束,inDegree数字中存在非零元素,说明存在环路。

import java.util.*;
public class Main {
    public static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();
    static int N;
    static int M;
    static int[] inDegree;
    static boolean[] visited;
    public static void addEdge(int from, int to) {
        if (!graph.containsKey(from)) {
            graph.put(from, new LinkedList<>());
        }
        if (!graph.containsKey(to)) {
            graph.put(to, new LinkedList<>());
        }
        graph.get(from).add(to);
    }
    public static void topo() {
        //如果要求字典序最小,那么可以使用优先队列实现
        LinkedList<Integer> queue = new LinkedList<>();
        for (int i = 1; i <= N; i++) {
            if (inDegree[i] == 0) {
                queue.add(i);
            }
        }
        while (!queue.isEmpty()) {
            int node = queue.removeFirst();
            System.out.print(node+" ");
            LinkedList<Integer> edges = graph.get(node);
            for (Integer edge : edges) {
                inDegree[edge]--;
                if (inDegree[edge]==0){
                    queue.add(edge);
                }
            }
        }
        System.out.println(Arrays.toString(inDegree));
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        inDegree = new int[N + 1];
        visited = new boolean[N + 1];
        for (int i = 0; i < M; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            inDegree[to]++;
            addEdge(from, to);
        }
        topo();
    }
}

7.欧拉回路与欧拉回路(有向图)

欧拉回路:是指通过图的每一条边恰好一次,且能回到起点的路径。要求起始点和终点一致。
对于一个有向图来说:
①如果每个节点都有入度-出度=0,则该图存在欧拉回路

 

欧拉路径:是指通过图的每一条边恰好一次,但不要求能回到起点的路径。不要求起始点和终点一致。
对于一个有向图来说:
①如果每个节点都有入度-出度=0,那么该图一定存在欧拉路径;
②如果只有一个节点入度-出度=1,只有一个入度-出度=-1,其余结点入度-出度=0,那么该图一定存在欧拉路径。

 注意:如果有欧拉回路,那么求出的欧拉路径就是欧拉回路

 

public class Main {
    static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();
    static int N;
    static int M;
    //对于有向图而言
    static int[] inDegree;
    static int[] outDegree;
    //记录欧拉路径的起点,如果有欧拉回路的话,起点无所谓,就当做1
    static int oula_start = 1;
    static int oula_end = 1;
    //记录欧拉路径
    //如果有欧拉回路,那么求得的欧拉路径就是欧拉回路
    static LinkedList<Integer> path = new LinkedList<>();
    static void addEdge(int from, int to) {
        if (!graph.containsKey(from)) {
            graph.put(from, new LinkedList<>());
        }
        if (!graph.containsKey(to)) {
            graph.put(to, new LinkedList<>());
        }
        graph.get(from).add(to);
    }
    static boolean have() {
        int count_fu1 = 0;
        int count_1 = 0;
        boolean result = false;
        for (int i = 1; i <= N; i++) {
            if (inDegree[i] - outDegree[i] == 1) {
                count_1++;
                oula_end = i;
            } else if (inDegree[i] - outDegree[i] == -1) {
                oula_start = i;
                count_fu1++;
            }
        }
        if ((count_fu1 == 0 && count_1 == 0)) {
            System.out.println("有欧拉回路");
            result = true;
        } else if (count_fu1 == 1 && count_1 == 1) {
            System.out.println("没有欧拉回路,有欧拉路径");
            System.out.println("起点为" + oula_start);
            System.out.println("终点为" + oula_end);
            result = true;
        } else {
            System.out.println("没有欧拉路径");
        }
        return result;
    }
    public static void findPath(int node) {
        LinkedList<Integer> edges = graph.get(node);
        while (!edges.isEmpty()) {
            int next = edges.get(0);
            edges.remove(Integer.valueOf(next));
            findPath(next);
        }
        path.add(node);
    }
    //有向图
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        inDegree = new int[N + 1];
        outDegree = new int[N + 1];
        for (int i = 0; i < M; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            addEdge(from, to);
            outDegree[from]++;
            inDegree[to]++;
        }
        //1.判断是否有欧拉路径
        have();
        //2.求解欧拉路径
        //无向图求出的路径可以直接打印,有向图求出的路径需要逆序输出
        findPath(oula_start);
        //3.判断最后的边数
        if (path.size() == M + 1) {
            //逆序输出
            for (int i = path.size() - 1; i >= 0; i--) {
                System.out.print(path.get(i) + " ");
            }
        } else {
            System.out.println("没有欧拉路径");
        }
    }
}

8.欧拉回路和欧拉路径(无向图)

欧拉回路:是指通过图的每一条边恰好一次,且能回到起点的路径。要求起始点和终点一致
对于一个无向图来说:
①如果每个节点的度数都是偶数,则该图存在欧拉回路;

欧拉路径:是指通过图的每一条边恰好一次,但不要求能回到起点的路径。不要求起始点和终点一致.
对于一个无向图来说:
①如果每个节点的度数都是偶数,那么该图一定存在欧拉路径;
②如果只有两个度数为奇数的节点,那么该图一定存在欧拉路径;

public class Main {
    static HashMap<Integer, LinkedList<Integer>> graph = new HashMap<>();
    static int N;
    static int M;
    static int oula_strat = 1;
    static int oula_end = 1;
    static int[] Degree;
    static LinkedList<Integer> path = new LinkedList<>();
    static void addEdge(int from, int to) {
        if (!graph.containsKey(from)) {
            graph.put(from, new LinkedList<>());
        }
        if (!graph.containsKey(to)) {
            graph.put(to, new LinkedList<>());
        }
        graph.get(from).add(to);
        graph.get(to).add(from);
    }
    public static void have() {
        boolean first = true;
        int odd_count = 0;
        for (int i = 1; i <= N; i++) {
            if (Degree[i] % 2 != 0) {
                odd_count++;
                if (first) {
                    oula_strat = i;
                    first = false;
                } else {
                    oula_end = i;
                }
            }
        }
        if (odd_count == 0) {
            System.out.println("存在欧拉回路");
        } else if (odd_count == 2) {
            System.out.println("不存在欧拉回路,但存在欧拉路径,起点为:" + oula_strat + ",终点为:" + oula_end);
        } else {
            System.out.println("不存在欧拉路径");
        }
    }
    static void findPath(int node) {
        LinkedList<Integer> edges = graph.get(node);
        while (!edges.isEmpty()) {
            int next = edges.get(0);
            //注意无向图这里,需要删除两次边
            edges.remove(Integer.valueOf(next));
            graph.get(next).remove(Integer.valueOf(node));

            findPath(next);
        }
        //注意:是在回溯之前加入
        path.add(node);
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        N = scanner.nextInt();
        M = scanner.nextInt();
        Degree = new int[N + 1];
        for (int i = 0; i < M; i++) {
            int from = scanner.nextInt();
            int to = scanner.nextInt();
            addEdge(from, to);
            Degree[from]++;
            Degree[to]++;
        }
        //1.判断是否存在欧拉回路或者欧拉路径
        have();
        //2.找欧拉回路或者欧拉路径
        findPath(oula_strat);
        //3.判断最后的边数
        if (path.size() == M + 1) {
            System.out.println(path.toString());
        } else {
            System.out.println("没有欧拉路径");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值