左神基础算法笔记-六

1. 图的表达、存储和生成方式

图分为有向图和无向图,无向图可以用有向图来表示。

  • 邻接表

    上图用邻接表表示如下:

    1:2

    2:3,4

    3:4

    4:null

    如果上图为无向图,则邻接表表示如下:

    1:2

    2:1,3,4

    3:2,4

    4:2,3

  • 邻接矩阵

    上图用邻接矩阵表示如下:

    1:(2,7),(3,5)

    2:(3,2)

    3:null

  • 图中点的存储方式

    /**
    * in 入度:代表几条边进入该点
    * out 出度:代表几条边从该点出发
    **/
    public class Node {
    public int value;
    public int in;
    public int out;
    public ArrayList<Node> nexts;
    public ArrayList<Edge> edges;

    public Node(int value) {
    this.value = value;
    in = 0;
    out = 0;
    nexts = new ArrayList<>();
    edges = new ArrayList<>();
    }
    }
  • 图中边的存储方式

    /**
    * weight 边的权重
    * from 边出发的边
    * to 边到达的点
    **/
    public class Edge {
    public int weight;
    public Node from;
    public Node to;

    public Edge(int weight, Node from, Node to) {
    this.weight = weight;
    this.from = from;
    this.to = to;
    }

    }
  • 图的存储方式

    /**
    * nodes 点集,Integer 是点中封装的数据项
    * edges 边集
    */
    public class Graph {
    public HashMap<Integer,Node> nodes;
    public HashSet<Edge> edges;

    public Graph() {
    nodes = new HashMap<>();
    edges = new HashSet<>();
    }
    }
  • 图的生成方式

    /**
    * 创建点集
    * 创建边
    * 建立点到点的关系
    * 更改点的入度、出度
    * 建立点和边的关系
    * 创建边集
    */
    public class GraphGenerator {

    public static Graph createGraph(Integer[][] matrix) {
    Graph graph = new Graph();
    for (int i = 0; i < matrix.length; i++) {
    Integer weight = matrix[i][0];
    Integer from = matrix[i][1];
    Integer to = matrix[i][2];
    // 如果点不存在,建立新点
    if (!graph.nodes.containsKey(from)) {
    graph.nodes.put(from, new Node(from));
    }
    if (!graph.nodes.containsKey(to)) {
    graph.nodes.put(to, new Node(to));
    }
    // 建立新边
    Node fromNode = graph.nodes.get(from);
    Node toNode = graph.nodes.get(to);
    Edge newEdge = new Edge(weight, fromNode, toNode);
    // 更新点的 nexts 集,并更新出入度
    fromNode.nexts.add(toNode);
    fromNode.out++;
    toNode.in++;
    // 更新点的 edges 集,并更新图的 edges 集
    fromNode.edges.add(newEdge);
    graph.edges.add(newEdge);
    }
    return graph;
    }

    }

2. 图的宽度优先遍历

  • 利用队列实现
  • 从源结点开始依次按照宽度进队列,然后弹出
  • 每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
  • 直到队列变空
/**
* HashSet 用来注册进过队列的点,防止点重复进入队列
*/
public class Code_01_BFS {

public static void bfs(Node node) {
if (node == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
HashSet<Node> map = new HashSet<>();
queue.add(node);
map.add(node);
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.println(cur.value);
for (Node next : cur.nexts) {
if (!map.contains(next)) {
map.add(next);
queue.add(next);
}
}
}
}

}

3. 图的深度优先遍历

  • 利用栈实现
  • 从源节点开始把节点按照深度放入栈,然后弹出
  • 每弹出一个点,把该节点下一个没有进过栈的邻接点放入栈
  • 直到栈变空
/**
* HashSet 用来注册进入栈的节点
* 源节点入栈并打印,源节点弹出
* 遍历源节点的所有邻接点
* 找到其中没入过栈的节点 x,将源节点和 x 压回栈中并打印 x
*/
public class Code_02_DFS {

public static void dfs(Node node) {
if (node == null) {
return;
}
Stack<Node> stack = new Stack<>();
HashSet<Node> set = new HashSet<>();
stack.add(node);
set.add(node);
System.out.println(node.value);
while (!stack.isEmpty()) {
Node cur = stack.pop();
for (Node next : cur.nexts) {
if (!set.contains(next)) {
stack.push(cur);
stack.push(next);
set.add(next);
System.out.println(next.value);
break;
}
}
}
}

}

4. 拓扑排序

适用范围:

  • 有向图
  • 有入度为 0 的节点
  • 没有环
  • 应用:编译过程等
/**
* 通过不断的删除入度为 0 的节点来完成拓扑排序
* 每次删除都会出现新的入度为 0 的节点
* inmap 存储节点入度和节点
* zeroInQueue 表示入度为 0 的节点队列
* @param graph
* @return
*/
public static List<Node> sortedTopology(Graph graph) {
HashMap<Node, Integer> inMap = new HashMap<>();
Queue<Node> zeroInQueue = new LinkedList<>();
for (Node node : graph.nodes.values()) {
inMap.put(node, node.in);
if (node.in == 0) {
zeroInQueue.add(node);
}
}
List<Node> result = new ArrayList<>();
while (!zeroInQueue.isEmpty()) {
Node cur = zeroInQueue.poll();
result.add(cur);
for (Node next : cur.nexts) {
inMap.put(next, inMap.get(next) - 1);
if (inMap.get(next) == 0) {
zeroInQueue.add(next);
}
}
}
return result;
}

5. 最小生成树的两种算法

K算法和P算法都只能应用于无向图。

  • 最小生成树:保证所有点连通的情况下,所有边的权重之和最小
  1. Kruskal

    从最小权重的边开始考察,如果形成回路则丢弃该边,直到所有节点被连通。

    /**
    * 使用并查集,刚开始每个节点各自成一个集合
    * 使用小根堆来存放边
    * 如果边的 from 和 to 属于同一集合则表示形成了回路,不考虑该边
    * 否则对边的 from 和 to 执行 union 操作
    */
    //undirected graph only
    public class Code_04_Kruskal {

    // Union-Find Set
    public static class UnionFind {
    private HashMap<Node, Node> fatherMap;
    private HashMap<Node, Integer> rankMap;

    public UnionFind() {
    fatherMap = new HashMap<Node, Node>();
    rankMap = new HashMap<Node, Integer>();
    }

    private Node findFather(Node n) {
    Node father = fatherMap.get(n);
    if (father != n) {
    father = findFather(father);
    }
    fatherMap.put(n, father);
    return father;
    }

    public void makeSets(Collection<Node> nodes) {
    fatherMap.clear();
    rankMap.clear();
    for (Node node : nodes) {
    fatherMap.put(node, node);
    rankMap.put(node, 1);
    }
    }

    public boolean isSameSet(Node a, Node b) {
    return findFather(a) == findFather(b);
    }

    public void union(Node a, Node b) {
    if (a == null || b == null) {
    return;
    }
    Node aFather = findFather(a);
    Node bFather = findFather(b);
    if (aFather != bFather) {
    int aFrank = rankMap.get(aFather);
    int bFrank = rankMap.get(bFather);
    if (aFrank <= bFrank) {
    fatherMap.put(aFather, bFather);
    rankMap.put(bFather, aFrank + bFrank);
    } else {
    fatherMap.put(bFather, aFather);
    rankMap.put(aFather, aFrank + bFrank);
    }
    }
    }
    }

    public static class EdgeComparator implements Comparator<Edge> {

    @Override
    public int compare(Edge o1, Edge o2) {
    return o1.weight - o2.weight;
    }

    }

    public static Set<Edge> kruskalMST(Graph graph) {
    UnionFind unionFind = new UnionFind();
    unionFind.makeSets(graph.nodes.values());
    PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
    for (Edge edge : graph.edges) {
    priorityQueue.add(edge);
    }
    Set<Edge> result = new HashSet<>();
    while (!priorityQueue.isEmpty()) {
    Edge edge = priorityQueue.poll();
    if (!unionFind.isSameSet(edge.from, edge.to)) {
    result.add(edge);
    unionFind.union(edge.from, edge.to);
    }
    }
    return result;
    }
    }
  2. Prim

    /**
    * 任选一个点出发,将该点加入点集(HashSet)
    * 将该点的所有边加入小根堆中和边集中(HashSet),弹出边如出现新的点则保留
    * 将新出现的点的所有边加入小根堆和边集
    */
    // undirected graph only
    public class Code_05_Prim {

    public static class EdgeComparator implements Comparator<Edge> {

    @Override
    public int compare(Edge o1, Edge o2) {
    return o1.weight - o2.weight;
    }

    }

    public static Set<Edge> primMST(Graph graph) {
    PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(
    new EdgeComparator());
    HashSet<Node> set = new HashSet<>();
    Set<Edge> result = new HashSet<>();
    for (Node node : graph.nodes.values()) {
    if (!set.contains(node)) {
    set.add(node);
    for (Edge edge : node.edges) {
    priorityQueue.add(edge);
    }
    while (!priorityQueue.isEmpty()) {
    Edge edge = priorityQueue.poll();
    Node toNode = edge.to;
    if (!set.contains(toNode)) {
    set.add(toNode);
    result.add(edge);
    for (Edge nextEdge : toNode.edges) {
    priorityQueue.add(nextEdge);
    }
    }
    }
    }
    }
    return result;
    }

    }

6. Dijkstra算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值