数据结构复习之图(拓扑排序and加权无向图)

1.4 有向图

有向图与无向图类似,只是边有了方向而已

基本概念:

  • 出度:由某个顶点指出的边的个数称为该顶点的出度
  • 入度:指向某个顶点的边的个数称为该顶点的入度
  • 有向路径:由一系列顶点组成,对于其中的每个顶点都存在一条有向边,从该顶点指向下一个顶点
  • 有向环:起点和终点相同的有向图

手搓有向图:

package GraphTest;

import LinearTest.QueueTest;

public class DiGraph {
    private final int V;
    private int E;
    private QueueTest<Integer>[] adj;
    public DiGraph(int V){
        this.V = V;
        this.E = E;
        //创建邻接表数组
        this.adj = new QueueTest[V];
        //创建邻接表
        for (int i = 0;i<adj.length;i++){
            adj[i] = new QueueTest<>();
        }
    }
    public int V(){
        return V;
    }
    public int E(){
        return E;
    }
    //增加边v->w
    public void addEdge(int v,int w){
        adj[v].enqueue(w);
        E++;
    }
    //获取由定点v所指出的所有定点
    public QueueTest<Integer> adj(int v){
        return adj[v];
    }
    //该有向图的反转图
    public DiGraph reverse(){
        //创建新的有向图对象
        DiGraph r = new DiGraph(V);
        //将由i->w的边改变为由w->i的边,就可以将图反向
        for(int i = 0;i<V;i++){
            for (Integer w : adj[i]) {
                addEdge(w,i);
            }
        }
        return r;
    }
}

1.5 拓扑排序

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

检测有向图中的环:

要使用拓扑排序实现就要保证图中没有环的存在

代码如下:

package GraphTest;

public class DirectedCycle {
    //标记定点是否已被访问
    private boolean[] marked;
    //记录图中是否有环
    private boolean hasCycle;
    //索引代表顶点,使用栈的思想,
    // 记录当前顶点有没有已经处于正在搜索的有向路径上
    private boolean[] onStack;
    //创建一个检测环对象,检测图G中是否有环
    public DirectedCycle(DiGraph G){
        //创建一个标记是否访问的数组
        marked = new boolean[G.V()];
        //创建一个搜索栈数组
        onStack = new boolean[G.V()];
        //默认图中无环
        hasCycle = false;
        //遍历图中每一个顶点,判断是否有环
        for(int v=0;v<G.V();v++){
            //如果有环直接跳出循环,不能进行拓扑排序
            if(hasCycle){
                break;
            }
            //判断当前顶点是否已被访问
            if(!marked[v]){
                //如果没有被访问则调用dfs算法判断是否有环
                dfs(G,v);
            }
        }
    }
    private void dfs(DiGraph G,int v){
        //把当前结点标记为已搜索
        marked[v] = true;
        //将当前结点放入搜索栈中
        onStack[v] = true;
        //获取当前结点的邻接表
        for (Integer w : G.adj(v)) {
            //判断邻接表中w顶点是否被访问
            if(!marked[w]){
                dfs(G,w);
            }
            //如果当前遍历结点已经在搜索路径的栈中,则返回有环即可
            if(onStack[w]){
                hasCycle = true;
                //当前顶点搜索完毕,让当前顶点出栈
                onStack[w] = false;
                return ;
            }
        }
    }
    //返回当前图中是否存在环
    public boolean hasCycle(){
        return hasCycle;
    }
    public static void main(String[] args) {
        DiGraph G = new DiGraph(5);
        G.addEdge(3,0);
        G.addEdge(0,2);
        G.addEdge(2,1);
        G.addEdge(1,0);
        G.addEdge(1,4);
    DirectedCycle directedCycle = new DirectedCycle(G);
        System.out.println(directedCycle.hasCycle());
    }
}

顶点排序:

利用深度优先搜索搜索路径把终点放入栈中,然后返回上一节点如果该结点没有子节点也放入栈中,利用栈后进先出的特点就可以得到一个从起点到终点的结点顺序
在这里插入图片描述

package GraphTest;

import java.util.Stack;

public class DepthFirstOrder {
    //创建标记顶点是否被搜索过的数组
    private boolean[] marked;
    //声明搜索栈,储存搜索的顶点序列
    private Stack<Integer> reversePost;
    //创建一个检测环对象,判断图G中是否有环
    public DepthFirstOrder(DiGraph G){
        marked = new boolean[G.V()];
        //创建一个搜索栈
        reversePost = new Stack<Integer>();
        //遍历搜索图中每一个顶点
        for (int v=0;v<G.V();v++){
            if(!marked[v]){
                dfs(G,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);
    }
    public Stack<Integer> reversePost(){
        return reversePost;
    }

    public static void main(String[] args) {
        DiGraph G = new DiGraph(6);
        G.addEdge(0,2);
        G.addEdge(0,3);
        G.addEdge(2,4);
        G.addEdge(3,4);
        G.addEdge(4,5);
        G.addEdge(1,3);
        DepthFirstOrder depthFirstOrder = new DepthFirstOrder(G);
        Stack<Integer> integers = depthFirstOrder.reversePost();
        for (Integer integer : integers) {
            System.out.println(integer);
        }
    }
}

拓扑排序:

先检测是否有环、之后顶点排序就是拓扑排序

package GraphTest;

import java.util.Stack;

public class TopoLogical {
    //顶点的拓扑排序结果集
    private Stack<Integer> order;
    //构造拓扑排序对象
    public TopoLogical(DiGraph G){
        //判断是否有环
        DirectedCycle dCycle = new DirectedCycle(G);
        //如果没环进行顶点排序
        if(!dCycle.hasCycle()){
            DepthFirstOrder depthFirstOrder = new DepthFirstOrder(G);
            order = depthFirstOrder.reversePost();
        }
    }
    //判断是否有环
    private boolean isCycle(){
        return order==null;
    }
    //获取拓扑排序的所有顶点
    public Stack<Integer> order(){
        return order;
    }

    public static void main(String[] args) {
        DiGraph G = new DiGraph(6);
        G.addEdge(0,2);
        G.addEdge(0,3);
        G.addEdge(2,4);
        G.addEdge(3,4);
        G.addEdge(4,5);
        G.addEdge(1,3);
        TopoLogical topoLogical = new TopoLogical(G);
        Stack<Integer> order = topoLogical.order();
        for (Integer w : order) {
            System.out.println(w+" ");
        }
    }
}

1.6 加权无向图

为每一条边关联一个权重值,这样可以根据权重获得最优解

加权无向图边的表示:

package GraphTest;

/**
 * 有向边的表示类
 */
public class Edge implements Comparable<Edge> {
    //顶点一
    private final int v;
    //顶点二
    private final int w;
    //当前边的权重
    private final double wight;
    public Edge(int v,int w,double wight){
        this.v = v;
        this.w = w;
        this.wight = wight;
    }
    //获取边的权重
    public double wight(){
        return wight;
    }
    //获取边上的一个点
    public int either(){
        return v;
    }
    //获取边上除了顶点vertex外的另外一个顶点
    public int other(int vertex){
        if(vertex==v){
            return w;
        }else{
            return v;
        }
    }

    @Override
    public int compareTo(Edge that) {
        int cmp;
        //当前边的权重大于that边
        if(this.wight()>that.wight()){
            cmp = 1;
        }else if(this.wight()<that.wight()){
            cmp = -1;
        }else{
            cmp = 0;
        }
        return cmp;
    }
}

加权无向图:

package GraphTest;

import LinearTest.QueueTest;


public class EdgeWeightedGraph {
    //顶点总数
    private final int V;
    //边的总数
    private int E;
    //邻接表,因为是加权无向图,泛型不是Integer
    private QueueTest<Edge>[] adj;
    public EdgeWeightedGraph(int V){
        this.V = V;
        this.E = 0;
        this.adj = new QueueTest[V];
        //为每个顶点初始化一个邻接表
        for(int i=0;i<adj.length;i++){
            adj[i] = new QueueTest<Edge>();
        }
    }
    //获取图中顶点的数量
    public int V(){
        return V;
    }
    //获取图中边的数量
    public int E(){
        return E;
    }
    //向加权无向图中添加一条边e
    public void addEdge(Edge e){
        int w = e.either();
        int v = e.other(w);
        //因为是无向图,所有边应该出现在两个顶点的邻接表中
        adj[w].enqueue(e);
        adj[v].enqueue(e);
        E++;
    }
    //获取和顶点v关联的所有边
    public QueueTest<Edge> adj(int v){
        return adj[v];
    }
    //获取加权无向图的所有边
    public QueueTest<Edge> edges(){
        //创建一个队列存储所有边
        QueueTest<Edge> allEdge = new QueueTest<>();
        /*因为是无向图,所以如果每个边都加入队列会出现重复
        因为顶点是有小到大,所以每次判断一下,小顶点放入,
        大顶点边不放入,就可以避免重复
        */
        //获取所有顶点
        for(int v=0;v<V;v++){
            //获取所有顶点的邻接表
            for (Edge w : adj[v]) {
                //获取w边的除了v顶点的另一个顶点
                if(w.other(v)<v){
                    allEdge.enqueue(w);
                }
            }
        }
        return allEdge;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值