图的进阶

本文探讨了有向图的数据结构实现,包括顶点数、边数的管理,邻接表的构建,以及如何通过API实现添加边、获取邻接顶点和图反转。关键部分介绍了如何使用深度优先搜索检测环,并利用DFS进行顶点排序实现拓扑排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有向图

  • 定义 : 有向图是一副有方向性的图,是由一组顶点 和 有方向性的边 组成的
  • 出度 : 由某个顶点 指出 的边的个数
  • 入度 : 指向某个顶点的边的个数
  • 有向路径 : 由一系列顶点组成,对于其中的每个顶点都存在一条有向边,从它指向序列中的下一个顶点
  • 有向环 : 一条至少含有一条边,且起点和终点相同的有向路径

有向图的Api 的设计
在这里插入图片描述

import java.util.LinkedList;
import java.util.Queue;

/**
 * 有向图
 */
public class Digraph {
    /**
     * 记录顶点的数量
     */
    private final int V;

    /**
     * 记录边的数量
     */
    private int E;

    /**
     * 定义有向图的邻接表
     */
    private Queue <Integer>[] adj;

    public Digraph (int v) {
        this.V = v;
        this.E = 0;
        adj = new LinkedList[v];
        for (int i = 0; i < v; i++) {
            adj[i] = new LinkedList<>();
        }
    }
    public int V () {
        return V;
    }
    public int E () {
        return E;
    }

    /**
     * 添加一条 v -> w的有向边
     * @param v
     * @param w
     */
    public void addEage (int v , int w) {
        adj[v].add(w);
        ++E;
    }

    /**
     * 获取顶点v 指向的 所有顶点
     * @param v
     * @return
     */
    public Queue<Integer> adj (int v) {
        return adj[v];
    }

    /**
     * 将有向图 反转 后返回
     * @return
     */
    public Digraph reverse () {
        //创建一个反向图
        Digraph reverseDigraph = new Digraph(V);
        //获取原来有向图的每个结点
        for (int i = 0; i < V; i++) {
            //获取每个结点 邻接表的所有结点
            for (Integer w : adj[i]) {
                //反转图记录下 w -> v
                reverseDigraph.adj(w).add(i);
            }
        }
        return reverseDigraph;
    }
}

拓扑排序

给定一副有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向后面的元素,此时就可以证明出每个顶点的优先级

1、检测有向图中是否存在环

/**
 *  检查图中是否存在环
 */
public class DirectedCycle {
    /**
     * 索引代表顶点,用来记录顶点是否被搜索过
     */
    private boolean[] marked;

    /**
     * 判断图中是否有环
     */
    private boolean hasCycle;

    /**
     * 采用栈的思想,记录当前顶点是否已经存在 当前搜索的的路径上
     * 存在则可以判断 图中是存在环的
     */
    private boolean[] onStack;

    /**
     * 判断传入的有向图 是否存在环
     * @param G
     */
    public DirectedCycle (Digraph G) {
        marked = new boolean[G.V()];
        onStack = new boolean[G.V()];
        hasCycle = false;
        //因为不知道从那个点出发 可能存在环
        //所以需要从所有的顶点都出发搜索 判断是否存在环
        for (int i = 0; i < G.V(); i++) {
            dfs(G,i);
        }
    }

    /**
     * 采用深度搜索 判断有向图是否存在环
     * onStack 入栈出栈 然后判断当前搜索的顶点是否已经在搜索路径上
     *
     * @param G
     * @param v
     */
    private void dfs (Digraph G,int v) {
        //标记顶点已经搜索过
        marked[v] = true;
        for (Integer adj : G.adj(v)) {
            //判断v 是否已经在搜索的路径上了
            if(marked[adj] && onStack[adj]) {
                //存在环
                hasCycle = true;
            }else {
                //采用回溯的思路
                //让顶点入栈
                onStack[adj] = true;
                dfs(G,adj);
                //回溯 顶点出栈
                onStack[adj] = false;
            }
        }
    }

    /**
     * 判断是否存在环
     * @return
     */
    public boolean hasCycle(){
        return hasCycle;
    }

}
2、顶点排序

顶点排序 按照深度搜索的方式, 将顶点记录在线性数据结构中
下面是按照深度优先搜索 对有向图 进行顶点排序的api
在这里插入图片描述


/**
 * 深度优先搜索 的顶点排序
 */
public class DepthFirstOrder {
    /**
     * 索引代表顶点 ,用来记录顶点是否已经被搜索过了
     */
    private boolean[] marked;

    /**
     * 使用栈记录深度优先搜索下的顶点
     */
    private Stack<Integer> reversePost;

    public DepthFirstOrder (Digraph G) {
        marked = new boolean[G.V()];
        reversePost = new Stack<>();
        for (int i = 0; i < G.V(); i++) {
            //如果顶点已经被搜索过则不用
            if(!marked[i])
                dfs(G,i);
        }
    }

    /**
     * 基于深度优先搜索,生成顶点线性序列
     * @param G
     * @param v
     */
    private void dfs (Digraph G, int v) {
        //标记顶点已经被搜索过
        marked[v] =  true;
        for (Integer w : G.adj(v)) {
            if(!marked[w])
                dfs(G,w);
        }
        //记录到线性序列中
        reversePost.push(v);
    }

    /**
     * 获取顶点线性序列
     * @return
     */
    private Stack<Integer> ReversePost() {
        return reversePost;
    }
}
  • 拓扑排序
    拓扑排序的实现 就是先判断图中是否存在环,如果没有存在环的话,则按照深度优先搜索的顶点排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值