Python 每日一记270@Java有向图顶点排序

顶点排序

将第一幅图排序成第二幅图的样子
在这里插入图片描述
在这里插入图片描述

java代码

核心是深度优先搜索,在无环的情况下,深度优先搜索,将最后搜索的先入栈,最后入栈,即优先度最低,出栈就是优先度最高的先出。

package mypackage;
import java.util.Iterator;

//队列类,用链表实现,后面有用
class Queue<T> implements Iterable<T>{
    //    节点个数,头节点,尾节点
    private int N;
    private Node head;
    private Node last;
    //节点类
    public class Node {
        public T data;
        public Node next;

        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }
    //构造方法,初始化
    public Queue() {
        this.N = 0;
        this.head = new Node(null,null);
        this.last = null;
    }
    //队列长度
    public int size(){
        return N;
    }
    //队列是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //入队列
    public void enqueue(T data){
//        如果队列为空,说明尾节点为空,让新节点为尾节点,头借点指向尾节点
        if (isEmpty()){
            last=new Node(data,null);
            head.next=last;
//            如果队列不为空,让新节点为尾节点,老的尾节点指向新尾节点
        }else {
            Node oldlast=last;
            last=new Node(data,null);
            oldlast.next=last;
        }
//        最后元素+1
        N++;
    }
    //出队列,注意先入先出,每次出的节点就是head指向的第一个节点,然后让head只想第二个节点即可
//    且注意,如果队列为空,要将last=null
    public T dequeue(){
//        如果为空,返回null
        if (isEmpty()){
            return null;
//            如果不为空,让head只想第二个节点,元素-1,且如果队列为空,要将last=null
        }else {
            Node oldfirst=head.next;
            head.next=oldfirst.next;
            N--;

            if (isEmpty()){
                last=null;
            }
//            返回弹出的元素
            return oldfirst.data;
        }
    }

    //    遍历
    @Override
    public Iterator iterator() {
        return new QIterator();
    }
    //    创建一个内部类实现Iterator接口
    public class QIterator implements Iterator {
        //        定义一个遍历的节点
        private Node n;

        public QIterator() {
//            初始化为0索引位置
            this.n = head;
        }

        //重写两个方法
        @Override
        public boolean hasNext() {
//            这个方法判断是否超出最大索引,如果超出会停止遍历
            return n.next != null;
        }

        @Override
        public Object next() {
//            这个方法会遍历得每个节点
            n = n.next;
            return n.data;
        }
    }
}

//栈类
class Stack<T> implements Iterable<T> {

    //节点类
    private static class Node<T> {
        T data;
        Node next;

        //构造方法
        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }
    //    成员变量,头节点,节点个数
    Node head;
    int  N;
    //    构造方法
    public Stack(){
//        初始head不指向任何节点,元素个数为零
        this.head=new Node(null,null);
        this.N=0;
    }

    //    节点个数
    public int size(){
        return N;
    }

    //  是否为空
    public boolean isempty (){
        return N==0;
    }

    //    压栈,即向链表中添加元素,注意因为栈是先进后出,每次添加节点应该都是添加在首节点后,再让新节点指向之前的第一个节点
//    第一次添加元素是让首节点指向新节点,接着再添加节点,就要让首节点指向新节点,新节点指向之前的第一个节点,依次类推
    public void push(T data){
        if (N==0){
            Node newnode=new Node(data,null);
            head.next=newnode;
            N++;
        }
        else {
            Node oldfirst=head.next;
            Node newnode=new Node(data,null);
            head.next=newnode;
            newnode.next=oldfirst;
            N++;
        }
    }

    //    出栈
//    如果为空,则弹出null,
//    如果不为空,弹出head后的第一个元素,让head指向第二个元素
    public T pop() {
        Node oldfirst = head.next;
        if (oldfirst==null) {
            return null;
        }
        else {
            head.next = oldfirst.next;
            N--;
            return (T) oldfirst.data;
        }
    }

    //    重写,用于遍历
    @Override
    public Iterator<T> iterator() {
        // 返回的Iterator对象,创建一个内部类实现这个接口
        return new SIterator();
    }

    //    创建一个内部类实现Iterator接口
    public class SIterator implements Iterator {
        //        定义一个遍历的节点
        private Node n;
        public SIterator(){
//            初始化为0索引位置
            this.n=head;
        }
        //重写两个方法
        @Override
        public boolean hasNext() {
//            这个方法判断是否超出最大索引,如果超出会停止遍历
            return n.next!=null;
        }

        @Override
        public Object next() {
//            这个方法会遍历得每个节点
            n=n.next;
            return n.data;
        }
    }
}

class Digraph<T>{
    private int V;//顶点数
    private int E;//边数
    private Queue<Integer>[] adj;//邻接表,用于装每个顶点的相连的顶点组成的队列,注意索引是每个顶点,这个索引对应的值是相连的顶点组成的队列
    //构造方法,传入顶点个数,初始化边数为0,
    public Digraph(int v) {
        this.V = v;
        this.E=0;
        this.adj=new Queue[v];//初始化队列数组,大小为顶点的个数
        for (int i = 0; i <adj.length ; i++) {
            adj[i]=new Queue<Integer>();//初始化数组的每个队列
        }
    }

    //获得顶点的个数
    public int getV(){
        return V;
    }

    //添加边,即将顶点v,w连接起来形成图,注意是有向图,只是有v指向w,adj[v].enqueue(w)
    //和无向图的主要区别
    public void addEdge(int v, int w){
        adj[v].enqueue(w);
        E++;//边数+1
    }

    //    获取边的个数
    public int getE(){
        return E;
    }

    //获取某个顶点相邻的所有顶点,返回这个队列即可
    public Queue<Integer> getAdj(int v){
        return adj[v];
    }

    //反向图,循环每一个顶点,再循环每个邻接表,让指向反转
    public Digraph reverse(){
        Digraph digraph=new Digraph(V);
        for (int v = 0;  v<V ; v++) {
            for (int w:adj[v]){
                digraph.addEdge(w,v);//本来是v指向w,现在改为w指向v即可
            }
        }
        return digraph;
    }
}

//检测是否有环的类,核心还是深度优先搜索
class IsCycle{
    private boolean[] marked;//是否被访问过
    private boolean hasCycle;//是有有环
    private boolean[] onStack;//是否在搜索的路径上

    //构造方法,调用了构造方法,就已经深度遍历完毕,本质上已经查找出了是否有环,再调用isHasCycle打印出来即可
    public IsCycle(Digraph digraph) {
        this.marked=new boolean[digraph.getV()];
        this.hasCycle=false;
        this.onStack=new boolean[digraph.getV()];

        //对每个顶点都使用深度优先搜索
        for (int v=0;v<digraph.getV();v++){
            if (!marked[v]){
                dfs(digraph,v);
                //有环了就不用再遍历其他节点了
                if (hasCycle==true){
                    return;
                }
            }
        }
    }

    //核心深度优先算法
    private void dfs(Digraph digraph, int v) {
        //对顶点v进行搜索,先标记为被访问过,且标记为在路径上了
        marked[v]=true;
        onStack[v]=true;
//        循环
        //逻辑是,深度遍历会遍历所有的与v直接或间接相通的顶点,并标记为已经访问过
        //如果没有被访问过深度遍历,如果没有被访问过,必然不会形成环啊,如果被访问过,才去检查是否在路径中,如果在,就表明有环
        for (Object w:digraph.getAdj(v)) {
            //如果没有被访问过,深度遍历
            if (!marked[(int)w]){
                dfs(digraph, (int)w);
                //如果被访问过,判断是否在路径中,如果在路径中,说明有环,直接退出
            }else {
                if (onStack[(int)w]){
                    hasCycle=true;
                    return;
                }
            }
        }
        //因为前面不仅对顶点v进行深度遍历,要对每个顶点进行深度遍历,
        // 因此在这个顶点v深度遍历完毕没有找到环,最后要将这个点标记为没在路径上,再重新深度遍历其他顶点,就是又开始一条新的路径
        onStack[v]=false;
    }

    public boolean isHasCycle(){
        return hasCycle;
    }

}

//顶点排序,核心是深度优先搜索,把顺序最后的顶点先入栈,出栈是后进先出,即优先度高的后入栈,先出栈,即排序完成
class Order{
    private boolean[] marked;//是否被访问过
    private Stack<Integer> node;//栈,用来装排序的顶点,注意是先进后出
//构造方法,构造方法后,顶点已经入栈
    public Order(Digraph digraph) {
        this.marked=new boolean[digraph.getV()];
        this.node=new Stack<Integer>();
        //对每个顶点都深度优先搜索
        for (int v = 0; v <digraph.getV() ; v++) {
            if (!marked[v]){
                dfs(digraph,v);
            }
        }
    }
//核心深度优先搜索算法
    public void dfs(Digraph digraph, int v) {
        marked[v]=true;
        for (Object w:digraph.getAdj(v)) {
            if (!marked[(int)w]) {
                dfs(digraph, (int) w);
            }
        }
        //最后搜索的最先入栈,最后出栈,优先级最低
        node.push(v);
    }
// 打印排序结果
    public void printOrder(){
        for (int n:node) {
            System.out.print(n+"-->");
        }
    }
}

//测试
public class MyJava {

    public static void main(String[] args){
        Digraph<Integer> digraph = new Digraph<>(6);
         digraph.addEdge(0,2);
        digraph.addEdge(2,4);
        digraph.addEdge(4,5);
        digraph.addEdge(0,3);
        digraph.addEdge(3,4);
        digraph.addEdge(1,3);
        System.out.println("顶点个数"+digraph.getV());
        System.out.println("边的条数"+digraph.getE());

        IsCycle isCycle=new IsCycle(digraph);
        System.out.println("此图是否有环:"+isCycle.isHasCycle());

        Order order=new Order(digraph);
        System.out.println("顶点排序");
        order.printOrder();
    }
}

在这里插入图片描述

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读