图面试专题

一、概念

和二叉树的区别:图可能有环

常见概念

  1. 顶点(Vertex): 图中的节点或点。
  2. 边(Edge): 顶点之间的连接线,描述节点之间的关系。
  3. 有向图(Directed Graph): 边具有方向性的图,边有箭头表示方向。
  4. 无向图(Undirected Graph): 边没有方向性的图。
  5. 路径(Path): 顶点序列,通过边连接的顶点序列。
  6. 回路(Cycle): 闭合的路径,起点和终点相同的路径。
  7. 连通图(Connected Graph): 图中任意两个顶点之间都存在路径的图。
  8. 强连通图(Strongly Connected Graph): 有向图中任意两个顶点之间都存在双向路径的图。
  9. 连通分量(Connected Component): 无向图中的极大连通子图。
  10. 树(Tree): 无环连通图,任意两个节点都有唯一路径。
  11. 森林(Forest): 多个不相交树的集合。
  12. 度(Degree): 顶点的度是指与该顶点相关联的边的数量。
  13. 权重(Weight): 边或者顶点上的数值,表示边的代价或者顶点的属性。

邻接矩阵

ABCD
A0正无穷58
B正无穷09正无穷
C5904
D8正无穷40

邻接表法

Nodeweight
AC5
AD8
CB9
CD4
BC9
DA8
DC4

二、算法题

1、套路模板


/**
 * @author jeb_lin
 * 22:27 2023/11/29
 */
public class Node {
    public int value; // 可以改成 String
    public int in;// 入度
    public int out;// 出度
    public ArrayList<Node> nexts; // 多个后继节点
    public ArrayList<Edge> edges; // 多条边,该节点指出去的

    public Node(int value){
        this.value = value;
        this.in = 0;
        this.out = 0;
        this.nexts = new ArrayList<>();
        this.edges = new ArrayList<>();
    }


    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int getIn() {
        return in;
    }

    public void setIn(int in) {
        this.in = in;
    }

    public int getOut() {
        return out;
    }

    public void setOut(int out) {
        this.out = out;
    }

    public ArrayList<Node> getNexts() {
        return nexts;
    }

    public void setNexts(ArrayList<Node> nexts) {
        this.nexts = nexts;
    }

    public ArrayList<Edge> getEdges() {
        return edges;
    }

    public void setEdges(ArrayList<Edge> edges) {
        this.edges = edges;
    }
}

/**
 * @author jeb_lin
 * 22:27 2023/11/29
 */
public class Edge {
    public Node from;
    public Node to;
    public int weight;

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



    // 复写下面这俩,是为了放入Set的时候能正确去重
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || !(obj instanceof Edge)) {
            return false;
        }
        Edge edge = (Edge) obj;
        return this.weight == edge.weight && Objects.equals(edge.from, this.from) && Objects.equals(edge.to, this.to);
    }

    @Override
    public int hashCode() {
        return this.weight * this.from.hashCode() * this.to.hashCode();
    }


    public Node getFrom() {
        return from;
    }

    public void setFrom(Node from) {
        this.from = from;
    }

    public Node getTo() {
        return to;
    }

    public void setTo(Node to) {
        this.to = to;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}

/**
 * @author jeb_lin
 * 22:26 2023/11/29
 */
public class Graph {
    public HashMap<Integer,Node> nodes; // 该图上面的所有Node,nodeVal -> Node
    public HashSet<Edge> edges; // 该图上面的所有边

    public Graph(){
        this.nodes = new HashMap<>();
        this.edges = new HashSet<>();
    }

    public HashMap<Integer, Node> getNodes() {
        return nodes;
    }

    public void setNodes(HashMap<Integer, Node> nodes) {
        this.nodes = nodes;
    }

    public HashSet<Edge> getEdges() {
        return edges;
    }

    public void setEdges(HashSet<Edge> edges) {
        this.edges = edges;
    }
}

2、二维数组转化成图

012备注
0015Node0->Node1 ,weight=5
1123Node1->Node2 ,weight=3
2027Node0->Node2 ,weight=7

/**
     * 把二维数组转换成图
     * [
     *      [0,1,5], // 表示 node0 -> node1 ,weight = 5
     *      [1,2,3],
     *      [0,2,7]
     * ]
     *
     * @param matrix
     * @return
     */
    public static Graph createGraph(int[][] matrix) {
        Graph graph = new Graph();
        HashMap<Integer, Node> nodes = new HashMap<>(); // 该图上面的所有Node,nodeVal -> Node
        HashSet<Edge> edges = new HashSet<>(); // 该图上面的所有边

        graph.setEdges(edges);
        graph.setNodes(nodes);
        for (int i = 0; i < matrix.length; i++) {
            int[] row = matrix[i];
            if (!nodes.containsKey(row[0])) {
                nodes.put(row[0], new Node(row[0]));
            }
            if (!nodes.containsKey(row[1])) {
                nodes.put(row[1], new Node(row[1]));
            }
            Node from = nodes.get(row[0]);
            Node to = nodes.get(row[1]);
            from.setOut(from.getOut() + 1);
            to.setIn(to.getIn() + 1);
            from.getNexts().add(to);

            Edge edgeTemp = new Edge(from, to, row[2]);
            from.getEdges().add(edgeTemp);
            if(!edges.contains(edgeTemp)){
                edges.add(edgeTemp);
            }
        }

        return graph;
    }

    public static void main(String[] args) {
        int[][] arr = {{0, 1, 5}, {1, 2, 3}, {0, 2, 7}};
        Graph graph = createGraph(arr);
        System.out.println("ok");
    }

3、图的广度优先遍历BFS

思路:

  1. 利用队列实现
  2. 从源节点开始依次按照宽度进队列,然后弹出
  3. 每弹出一个点,把该节点所有没有进过队列的邻接点放入队列
  4. 直到队列变空
private static void testBreadthFirstSearch() {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);

        node1.getNexts().add(node2);
        node1.getNexts().add(node3);
        node1.getNexts().add(node4);

        node2.getNexts().add(node1);
        node2.getNexts().add(node3);

        node3.getNexts().add(node1);
        node3.getNexts().add(node2);
        node3.getNexts().add(node4);
        node3.getNexts().add(node5);

        node4.getNexts().add(node1);
        node4.getNexts().add(node3);
        node4.getNexts().add(node5);

        node5.getNexts().add(node3);
        node5.getNexts().add(node4);

        breathFirstSearch(node1);

    }

    private static void breathFirstSearch(Node head) {
        System.out.println(" === breathFirstSearch ===");
        if(null == head){
            return;
        }
        Set<Node> nodeSet = new HashSet<>();
        LinkedList<Node> queue = new LinkedList<>();
        queue.offer(head);
        nodeSet.add(head);

        while(!queue.isEmpty()){
            Node temp = queue.poll();
            System.out.print(temp.value + ",");

            for (int i = 0; i < temp.getNexts().size(); i++) {
                Node nextTemp = temp.getNexts().get(i);
                if(!nodeSet.contains(nextTemp)){
                    queue.offer(nextTemp);
                    nodeSet.add(nextTemp);
                }
            }
        }
        System.out.println();

    }

4、 图的深度优先遍历DFS

思路:

  1. 利用栈实现
  2. 从源节点开始把节点按照深度放入栈,然后弹出
  3. 每弹出一个点,再把该节点入栈,再把该节点下一个没有进过栈的邻接点放入栈
  4. 直到栈变空

 private static void testDepthFirstSearch() {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);

        node1.getNexts().add(node2);
        node1.getNexts().add(node3);
        node1.getNexts().add(node4);

        node2.getNexts().add(node1);
        node2.getNexts().add(node3);

        node3.getNexts().add(node1);
        node3.getNexts().add(node2);
//        node3.getNexts().add(node4);
        node3.getNexts().add(node5);

        node4.getNexts().add(node1);
        node4.getNexts().add(node3);
        node4.getNexts().add(node5);

        node5.getNexts().add(node3);
        node5.getNexts().add(node4);

        depthFirstSearch(node1);
    }

    private static void depthFirstSearch(Node head) {
        if(null == head){
            return;
        }
        System.out.println(" === depthFirstSearch ===");
        Stack<Node> stack = new Stack<>();
        stack.push(head);
        Set<Node> nodeSet = new HashSet<>();
        nodeSet.add(head);

        System.out.print(head.value + ",");
        while (!stack.isEmpty()){
            Node tempNode = stack.pop();
            for (int i = 0; i < tempNode.getNexts().size(); i++) {
                Node nextTemp = tempNode.getNexts().get(i);
                if(!nodeSet.contains(nextTemp)){
                    stack.push(tempNode);
                    stack.push(nextTemp);
                    nodeSet.add(nextTemp);
                    System.out.print(nextTemp.value + ",");
                    break;
                }
            }
        }
        System.out.println();
    }

    private static void testBreadthFirstSearch() {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);

        node1.getNexts().add(node2);
        node1.getNexts().add(node3);
        node1.getNexts().add(node4);

        node2.getNexts().add(node1);
        node2.getNexts().add(node3);

        node3.getNexts().add(node1);
        node3.getNexts().add(node2);
        node3.getNexts().add(node4);
        node3.getNexts().add(node5);

        node4.getNexts().add(node1);
        node4.getNexts().add(node3);
        node4.getNexts().add(node5);

        node5.getNexts().add(node3);
        node5.getNexts().add(node4);

        breathFirstSearch(node1);

    }

5、图的拓扑排序算法

思路:

  1. 把入度是0的,入queue
  2. pop出来后打印,current的next那些节点,入度-1
  3. 判断next节点的入度,如果是0,入queue,继续pop
 /**
     * 拓扑排序
     */
    private static void testTopologySort() {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);

        node1.getNexts().add(node2);
        node1.getNexts().add(node3);
        node1.setOut(2);

        node2.getNexts().add(node3);
        node2.getNexts().add(node4);
        node2.setOut(2);
        node2.setIn(1);

        node3.getNexts().add(node4);
        node3.setOut(1);
        node3.setIn(2);

        node4.setIn(1);

        Graph graph = new Graph();
        graph.getNodes().put(node1.getValue(), node1);
        graph.getNodes().put(node2.getValue(), node2);
        graph.getNodes().put(node3.getValue(), node3);
        graph.getNodes().put(node4.getValue(), node4);

        topologySort(graph);

    }

    private static void topologySort(Graph graph) {
        System.out.println();
        System.out.println("=== topologySort ===");

        LinkedList<Node> queue = new LinkedList<>();
        // node节点 -> 入度
        // 为什么我要用Map,其实通过Node.getIn() 的 -1也能操作, 就是会污染你的Node
        Map<Node,Integer> node2In = new HashMap<>();

        graph.getNodes().values().forEach(node -> {
            if (node.getIn() == 0) {
                queue.offer(node);

            }
            node2In.put(node,node.getIn());
        });

        while (!queue.isEmpty()) {
            Node temp = queue.poll();
            System.out.print(temp.getValue() + ",");
            temp.getNexts().forEach(nextNode -> {
                int newIn = node2In.get(nextNode) - 1;
                node2In.put(nextNode,newIn);
                if (newIn == 0) {
                    queue.offer(nextNode);
                }
            });
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值