从0开始的算法(数据结构和算法)基础(七)

       图(graph) 是一种非线性数据结构,由顶点(vertex) 和边 (Edge) 组成。他们之间的相关性没有那么高,链表的线性关系,树状图的派生关系。自由度较高就代表复杂度比较高。
       顶点 (Vertex):图中的基本单位,通常用来表示对象一个节点
       边 (Edge):连接两个顶点的线,可以是有向(Directed)或无向(Undirected)。

图论

图论是数学和计算机科学中的一个重要分支,专门研究图的性质和结构。图是一种非线性数据结构,由顶点(也称为节点)和边(连接顶点的线)组成。图论在许多实际应用中扮演着关键角色,如网络设计、社交网络分析和路线规划等。

  • 社交关系:用户之间的朋友关系。
  • 交通网络:城市之间的道路连接。

2. 图的类型

图可以根据不同的特性进行分类:

  • 无向图:边没有方向,两个顶点之间的关系是双向的。例如:A和B是朋友关系。

  • 有向图:边有方向,表示从一个顶点到另一个顶点的单向关系。例如:用户A关注用户B。

  • 加权图:每条边都有一个权重,通常表示成本、距离或时间。

  • 无环图(DAG):没有回路的图,通常在任务依赖关系中使用。

二、图的表示方法

图的表示方法主要有两种:邻接矩阵和邻接表。

1. 邻接矩阵

邻接矩阵是一个二维数组,其中的元素表示图中顶点之间的连接情况。对于稀疏图会浪费空间。
在图论中,边的添加和删除、顶点的添加和删除是基本的操作。使用邻接矩阵表示图时,这些操作的时间复杂度和具体实现方法如下:

(1)邻接矩阵的结构

假设我们有一个图,使用邻接矩阵 adjMat 来表示。该矩阵的大小为 ( V \times V ),其中 ( V ) 是图中顶点的数量。矩阵的元素 adjMat[i][j] 表示从顶点 ( i ) 到顶点 ( j ) 的边的权重(若无边则为0或无穷大)。

(2) 添加/删除边
  • 添加边:在邻接矩阵中修改指定的边,只需更新 adjMat[i][j]adjMat[j][i]。时间复杂度为 ( O(1) )。

    // 添加边
    void addEdge(int[][] adjMat, int u, int v, int weight) {
        adjMat[u][v] = weight;
        adjMat[v][u] = weight; // 无向图
    }
    
  • 删除边:同样,只需将 adjMat[i][j]adjMat[j][i] 设为无穷大(或0)。时间复杂度为 ( O(1) )。

    // 删除边
    void removeEdge(int[][] adjMat, int u, int v) {
        adjMat[u][v] = Integer.MAX_VALUE; // 或0
        adjMat[v][u] = Integer.MAX_VALUE; // 无向图
    }
    
(3)添加顶点
  • 添加顶点:在邻接矩阵的尾部添加一行一列,并将所有新元素初始化为无穷大(或0)。时间复杂度为 ( O(V2) )。

    // 添加顶点
    int[][] addVertex(int[][] adjMat, int V) {
        int[][] newAdjMat = new int[V + 1][V + 1];
        
        for (int i = 0; i < V; i++) {
            for (int j = 0; j < V; j++) {
                newAdjMat[i][j] = adjMat[i][j];
            }
        }
        // 初始化新行和新列
        for (int i = 0; i < V + 1; i++) {
            newAdjMat[V][i] = Integer.MAX_VALUE; // 或0
            newAdjMat[i][V] = Integer.MAX_VALUE; // 或0
        }
        
        return newAdjMat;
    }
    
(4) 删除顶点
  • 删除顶点:在邻接矩阵中删除一行和一列。若删除的是首行首列,需要将其他元素向左上移动。时间复杂度为 ( O(V^2) )。

    // 删除顶点
    int[][] removeVertex(int[][] adjMat, int V, int vertex) {
        int[][] newAdjMat = new int[V - 1][V - 1];
        int newRow = 0, newCol;
        
        for (int i = 0; i < V; i++) {
            if (i == vertex) continue; // 跳过删除的顶点
            newCol = 0;
            for (int j = 0; j < V; j++) {
                if (j == vertex) continue; // 跳过删除的顶点
                newAdjMat[newRow][newCol] = adjMat[i][j];
                newCol++;
            }
            newRow++;
        }
        
        return newAdjMat;
    }
    
(5)初始化
  • 初始化:传入 ( V ) 个顶点,初始化长度为 ( V ) 的顶点列表 vertices,使用 ( O(V) ) 时间;初始化大小为 ( V \times V ) 的邻接矩阵 adjMat,使用 ( O(V^2) ) 时间。

    // 初始化
    int[][] initializeGraph(int V) {
        int[][] adjMat = new int[V][V];
        for (int i = 0; i < V; i++) {
            for (int j = 0; j < V; j++) {
                if (i == j) {
                    adjMat[i][j] = 0; // 自环权重为0
                } else {
                    adjMat[i][j] = Integer.MAX_VALUE; // 或0
                }
            }
        }
        return adjMat;
    }
    

2. 邻接表

邻接表为每个顶点维护一个列表,列出与其直接相连的顶点。对于大型稀疏图,这种表示方法更加节省空间。

三、图的基本算法

1. 深度优先搜索(DFS)

深度优先搜索是一种用于遍历或搜索图的算法。它从某个顶点开始,尽可能深入到每个分支,然后回溯。

Java实现

import java.util.*;

class Graph {
    private Map<String, List<String>> graph;

    public Graph() {
        graph = new HashMap<>();
    }

    public void addEdge(String u, String v) {
        graph.putIfAbsent(u, new ArrayList<>());
        graph.putIfAbsent(v, new ArrayList<>());
        graph.get(u).add(v);
        graph.get(v).add(u);  // 无向图
    }

    public void dfs(String start) {
        Set<String> visited = new HashSet<>();
        dfsHelper(start, visited);
    }

    private void dfsHelper(String vertex, Set<String> visited) {
        visited.add(vertex);
        System.out.print(vertex + " ");

        for (String neighbor : graph.get(vertex)) {
            if (!visited.contains(neighbor)) {
                dfsHelper(neighbor, visited);
            }
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        Graph g = new Graph();
        g.addEdge("A", "B");
        g.addEdge("A", "C");
        g.addEdge("B", "D");
        g.addEdge("C", "D");

        System.out.println("DFS Traversal:");
        g.dfs("A");
    }
}

2. 广度优先搜索(BFS)

广度优先搜索是一种图的遍历方法,从某个顶点开始,逐层访问其邻接顶点。

Java实现

import java.util.*;

class Graph {
    private Map<String, List<String>> graph;

    public Graph() {
        graph = new HashMap<>();
    }

    public void addEdge(String u, String v) {
        graph.putIfAbsent(u, new ArrayList<>());
        graph.putIfAbsent(v, new ArrayList<>());
        graph.get(u).add(v);
        graph.get(v).add(u);  // 无向图
    }

    public void bfs(String start) {
        Set<String> visited = new HashSet<>();
        Queue<String> queue = new LinkedList<>();
        visited.add(start);
        queue.add(start);

        while (!queue.isEmpty()) {
            String vertex = queue.poll();
            System.out.print(vertex + " ");

            for (String neighbor : graph.get(vertex)) {
                if (!visited.contains(neighbor)) {
                    visited.add(neighbor);
                    queue.add(neighbor);
                }
            }
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        Graph g = new Graph();
        g.addEdge("A", "B");
        g.addEdge("A", "C");
        g.addEdge("B", "D");
        g.addEdge("C", "D");

        System.out.println("\nBFS Traversal:");
        g.bfs("A");
    }
}

3. Dijkstra算法

Dijkstra算法用于加权图,找到从源顶点到其他顶点的最短路径。

Java实现

import java.util.*;

class Graph {
    private Map<String, Map<String, Integer>> graph;

    public Graph() {
        graph = new HashMap<>();
    }

    public void addEdge(String u, String v, int weight) {
        graph.putIfAbsent(u, new HashMap<>());
        graph.putIfAbsent(v, new HashMap<>());
        graph.get(u).put(v, weight);
        graph.get(v).put(u, weight);  // 无向图
    }

    public Map<String, Integer> dijkstra(String start) {
        Map<String, Integer> distances = new HashMap<>();
        PriorityQueue<Node> minHeap = new PriorityQueue<>(Comparator.comparingInt(node -> node.distance));

        for (String vertex : graph.keySet()) {
            distances.put(vertex, Integer.MAX_VALUE);
        }
        distances.put(start, 0);
        minHeap.add(new Node(start, 0));

        while (!minHeap.isEmpty()) {
            Node current = minHeap.poll();

            for (Map.Entry<String, Integer> entry : graph.get(current.vertex).entrySet()) {
                String neighbor = entry.getKey();
                int weight = entry.getValue();
                int distance = current.distance + weight;

                if (distance < distances.get(neighbor)) {
                    distances.put(neighbor, distance);
                    minHeap.add(new Node(neighbor, distance));
                }
            }
        }
        return distances;
    }

    private static class Node {
        String vertex;
        int distance;

        Node(String vertex, int distance) {
            this.vertex = vertex;
            this.distance = distance;
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        Graph g = new Graph();
        g.addEdge("A", "B", 1);
        g.addEdge("A", "C", 4);
        g.addEdge("B", "C", 2);
        g.addEdge("B", "D", 5);
        g.addEdge("C", "D", 1);

        Map<String, Integer> result = g.dijkstra("A");
        System.out.println("\nDijkstra's shortest path from A:");
        for (Map.Entry<String, Integer> entry : result.entrySet()) {
            System.out.println("Distance to " + entry.getKey() + ": " + entry.getValue());
        }
    }
}

4. Kruskal算法

Kruskal算法用于求解最小生成树。

Java实现

import java.util.*;

class UnionFind {
    private Map<String, String> parent = new HashMap<>();
    private Map<String, Integer> rank = new HashMap<>();

    public void add(String u) {
        parent.put(u, u);
        rank.put(u, 0);
    }

    public String find(String u) {
        if (!u.equals(parent.get(u))) {
            parent.put(u, find(parent.get(u)));  // 路径压缩
        }
        return parent.get(u);
    }

    public void union(String u, String v) {
        String rootU = find(u);
        String rootV = find(v);

        if (!rootU.equals(rootV)) {
            if (rank.get(rootU) > rank.get(rootV)) {
                parent.put(rootV, rootU);
            } else if (rank.get(rootU) < rank.get(rootV)) {
                parent.put(rootU, rootV);
            } else {
                parent.put(rootV, rootU);
                rank.put(rootU, rank.get(rootU) + 1);
            }
        }
    }
}

class Graph {
    private List<Edge> edges = new ArrayList<>();

    public void addEdge(String u, String v, int weight) {
        edges.add(new Edge(u, v, weight));
    }

    public List<Edge> kruskal() {
        edges.sort(Comparator.comparingInt(edge -> edge.weight));
        UnionFind uf = new UnionFind();
        List<Edge> mst = new ArrayList<>();

        for (Edge edge : edges) {
            uf.add(edge.u);
            uf.add(edge.v);
        }

        for (Edge edge : edges) {
            if (uf.find(edge.u) != uf.find(edge.v)) {
                uf.union(edge.u, edge.v);
                mst.add(edge);
            }
        }
        return mst;
    }

    private static class Edge {
        String u;
        String v;
        int weight;

        Edge(String u, String v, int weight) {
            this.u = u;
            this.v = v;
            this.weight = weight;
        }
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        Graph g = new Graph();
        g.addEdge("A", "B", 1);
        g.addEdge("A", "C", 3);
        g.addEdge("B", "C", 1);
        g.addEdge("B", "D", 4);
        g.addEdge("C", "D", 1);

        List<Graph.Edge> mst = g.kruskal();
        System.out.println("\nKruskal's Minimum Spanning Tree:");
        for (Graph.Edge edge : mst) {
            System.out.println(edge.u + " -- " + edge.v + " == " + edge.weight);
        }
    }
}

四、图的应用

图论在现实生活中的应用广泛,以下是一些例子:

  1. 社交网络:分析用户之间的关系,推荐朋友。

    • 例如,Facebook使用图算法来分析用户之间的连接,提供好友推荐。
  2. 地图导航:计算城市之间的最短路径。

    • Google Maps使用图算法来查找最优路线,考虑交通状况和距离。
  3. 网络流:优化网络中的数据流量。

    • 如最大流算法用于找出网络中可传输的最大数据流量。
  4. 任务调度:管理项目任务之间的依赖关系。

    • 在项目管理中,DAG用于表示任务先后顺序,帮助制定合理的调度计划。

持续更新中~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值