数据结构:关键路径,利用DFS遍历每一条关键路径JAVA语言实现

     这是我们学校做的数据结构课设,要求分别输出关键路径,我查遍资料java版的只能找到关键路径,但是无法分别输出关键路径

     c++有可以分别输出的,所以在明白思想后自己写了一个java版的

     函数带有输入函数也有已经存进去的图

    

    如上图关键路径被分别输出(采用了DFS算法):

     例:AOE 图如下:

  

 

      算法设计如下:

       1. 首先,要求关键路径,要先要先写拓扑排序,如果图中有环,就无法进行关键路径的求解,直接跳出。

   拓扑排序:利用栈stack,先将入度为0事件节点的加入栈中,然后编历后面的活动节点,每次给活动节点的入度减一,然后将入度为0的加入栈       stack中,每次出栈的加入栈stack中,stack1中的元素拓扑排序的逆序,然后根据核心算法:

    if(etv[p.getData()]+p1.getInfo()>etv[p1.getAdjvex()]){
    etv[p1.getAdjvex()]=etv[p.getData()]+p1.getInfo();
    }

计算出事件的最早发生时间的事件存入etv

    然后根据count来判断图中有没有回路。

2. 然后对拓扑排序的逆序求事件的最晚发生时间,根据核心法:

     if (ltv[p1.getAdjvex()]-p1.getInfo()<ltv[p.getData()]){
    ltv[p.getData()]=Math.abs(ltv[p1.getAdjvex()]-p1.getInfo());
        }

    算出事件的最晚发生时间存入数组ltv中。

3. 接着求活动的最晚发生时间el和活动的最早发生时间ee

         ee=etv[i];
    el=ltv[pn.getAdjvex()]-pn.getInfo();

    ee和el相等的时候,本活动就为关键活动,关键路径算出来了。

     解决输出每条关键路径:

4. 输出每一条关键路径:利用DFS算法(回溯算法),

图在遍历以前已经将关键路径用visited数组标记了,

故遍历的时候只遍历关键路径,

原理如图:

 

visited0的时候为关键节点,然后遍历此节点进入递归:

while (p!=null){
     if(visited[p.getAdjvex()]!=1){
        /**
         * 是关键路径就入栈
         */
        stack2.push(p);
        DFS(p.getAdjvex());
        /**
         * 遇见死路回退的时候出栈
         */
        stack2.pop();
    }

本算法类似走迷宫,并且每走一个点,就把这个点入栈,并且将visited数组中的本节点的值存为1,代表遍历过了:

                visited[k]=1;

然后递归进入下一个节点,遇见终点就先遍历并输出栈中的元素:

if(k==finall) {
     for (int i=0;i<stack2.size;i++) {
        System.out.print(stack2.peekTravel(i).getName() + "->");
        }
    } 

一边退回一边出栈:

  DFS(p.getAdjvex());
        /**
         * 遇见死路回退的时候出栈
         */
        stack2.pop();

即栈中元素只到分岔点,退回上一次的分岔点,然后递归进入下一条路,直到遍历结束,然后输出了全部的关键路径。

                 

 

DFS算法全部代码:

/**
 * 为了输出每条关键路径用到DFS算法
 * @param k 起点再数组中的下标
 */
public void DFS(int k){
    visited[k]=1;
    lineNode p=new lineNode();
    p=vexList[k].getNext();
    /**
     * 终点永远是关键节点
     */
    visited[finall]=0;
    /**
     * 如果走到终点遍历栈的元素
     */
    if(k==finall) {
        for (int i=0;i<stack2.size;i++) {
            System.out.print(stack2.peekTravel(i).getName() + "->");
        }
    }
    /**
     * 遍历节点后面的链表
     */
    while (p!=null){
        if(visited[p.getAdjvex()]!=1){
            /**
             * 是关键路径就入栈
             */
            stack2.push(p);
            DFS(p.getAdjvex());
            /**
             * 遇见死路回退的时候出栈
             */
            stack2.pop();
        }
        System.out.println();
        p=p.getNext();
    }
}

DFS算法流程图:

           

                   (流程图有问题,左端回退出栈少了循环)

 源代码如下:

      

package dataproject.importanRoad;
import dataproject.stack.myArrayStack;
import java.util.Scanner;
/**
 * @author 李正阳  17060208112
 */
public class lessonWord {
    /**
     * 顶点数组
     */
    private vertexNode[] vexList;
    /**
     * etv 事件最早发生时间(即顶点)
     * ltv 事件最晚发生时间
     */
    private int etv[], ltv[];
    /**
     * 拓扑排序所用到的栈
     */
    private myArrayStack<vertexNode> stack=new myArrayStack<>();
    private myArrayStack<vertexNode> stack1=new myArrayStack<>();
    /**
     * 为了输出关键路径,标明哪些是关键节点
     */
    static int MAX_VERTEXNUM = 100;
    static int [] visited = new int[MAX_VERTEXNUM];
    /**
     *关键路径用到的栈队,数组
     */
    private myArrayStack<lineNode> stack2=new myArrayStack<>();
    private static int finall;
    /**
     * 使用键盘输入来构建图构建图
     */
    public void createGraph(){
        vertexNode pl1=new vertexNode();
        lineNode pl=new lineNode();
        int n;
        Scanner in=new Scanner(System.in);
        System.out.println("请输入共有多少个节点:");
        n=in.nextInt();
        vexList = new vertexNode[n];
        for (int i=0;i<n;i++){
            int p;
            /**
             * 构建节点
             */
            System.out.println("节点"+(i+1)+"节点存在数组中的位置 "+"节点名字");
            vertexNode v= new vertexNode( in.nextInt(), null, in.nextLine());
            pl1=v;
            pl=v.getNext();
            System.out.println("请输入此节点后有多少个边:");
            p=in.nextInt();
            for (int j=0;j<p;j++){
                /**
                 * 构建活动即事件
                 */
                System.out.println("边"+(j+1)+"后续节点在数组中的位置 "+"权值 "+"边的名字");
                lineNode a= new lineNode(in.nextInt(),in.nextInt(), in.nextInt(), null,in.next(),false);
                if(j==0){
                    pl1.setNext(a);
                    pl=pl1.getNext();
                }else {
                    pl.setNext(a);
                    pl=pl.getNext();
                }
            }
            vexList[i]=v;
        }
    }

    /**
     * 存储下的图,和我画出的图一样,方便测试
     */
    public void craeteGraph() {
        /**
         * 所有节点即事件
         */
        vertexNode v1 = new vertexNode( 0, null, "v1");
        vertexNode v2 = new vertexNode( 1, null, "v2");
        vertexNode v3 = new vertexNode(2, null, "v3");
        vertexNode v4 = new vertexNode( 3, null, "v4");
        vertexNode v5 = new vertexNode(4, null, "v5");
        vertexNode v6 = new vertexNode(5, null, "v6");
        vertexNode v7 = new vertexNode(6, null, "v7");
        vertexNode v8 = new vertexNode(7, null, "v8");
        vertexNode v9 = new vertexNode(8, null, "v9");
        /**
         * v1节点
         * a1活动,a2活动,a3活动
         */
        lineNode v12 = new lineNode(0,1, 6, null,"a1",false);
        lineNode v13 = new lineNode(0,2, 4, null,"a2",false);
        lineNode v14 = new lineNode(0,3, 5, null,"a3",false);
        lineNode a12=new lineNode(0,9,5,null,"a12",false);
        v1.setNext(v12);
        v12.setNext(v13);
        v13.setNext(v14);
        v14.setNext(a12);
        /**
         * v2节点
         * a4活动
         */
        lineNode v25 = new lineNode(1, 4, 1, null,"a4",false);
        v2.setNext(v25);
        /**
         * v3节点
         * a5活动
         */
        lineNode v35 = new lineNode(2,4, 1, null,"a5",false);
        v3.setNext(v35);
        /**
         * v4节点
         * a6活动
         */
        lineNode v46 = new lineNode(3,5, 2, null,"a6",false);
        v4.setNext(v46);
        /**
         * v5节点
         * a7活动 a8活动
         */
        lineNode v57 = new lineNode(4,6, 9, null,"a7",false);
        lineNode v58 = new lineNode(4,7, 7, null,"a8",false);
        v5.setNext(v57);
        v57.setNext(v58);
        /**
         * v6节点
         * a9活动
         */
        lineNode v68 = new lineNode(5,7, 4, null,"a9",false);
        v6.setNext(v68);
        /**
         * v7节点
         * a10活动
         */
        lineNode v79 = new lineNode(6,8, 2, null,"a10",false);
        v7.setNext(v79);
        /**
         * v8节点
         * a11活动
         */
        lineNode v89 = new lineNode(7,8, 4, null,"a11",false);
        v8.setNext(v89);
        /**
         * v10节点
         *
         */
        vertexNode v10=new vertexNode(9,null,"西安");
        /**
         * v11节点
         */
        vertexNode v11=new vertexNode(10,null,"北京");
        lineNode a13=new lineNode(8,10,6,null,"a13",false);
        lineNode a14=new lineNode(9,8,7,null,"a14",false);
        v11.setNext(a14);
        v10.setNext(a13);

        /**
         * 对象数组:vexList,保存节点构建了图
         */
        vexList = new vertexNode[11];
        vexList[0] = v1;
        vexList[1] = v2;
        vexList[2] = v3;
        vexList[3] = v4;
        vexList[4] = v5;
        vexList[5] = v6;
        vexList[6] = v7;
        vexList[7] = v8;
        vexList[8] = v9;
        vexList[9]=v10;
        vexList[10]=v11;
    }


    /**
     * 拓扑排序
     * @return true 排序成功 false 失败
     */
    public boolean topologicalSort() {
        /**
         * 计算入度:初始化所有节点的入度
         */
        for (int i=0;i<vexList.length;i++){
            vexList[i].setIn(0);
        }
        /**
         * 遍历每个节点后面的链表,然后就给弧尾顶点加一
         */
        for (int i=0;i<vexList.length;i++){
            lineNode p=new lineNode();
            p=vexList[i].getNext();
            while (p!=null){
                vertexNode vertexNode=new vertexNode();
                vertexNode=vexList[p.getAdjvex()];
                vertexNode.setIn(vertexNode.getIn()+1);
                p=p.getNext();
            }
        }

        /**
         * 计数:用来判断是否无环
         */
        int count=0;
        vertexNode p = new vertexNode();
        lineNode p1 = new lineNode();
        System.out.println("拓扑排序:");
        /**
         * 对事件最早发生时间数组初始化
         */
        etv=new int[vexList.length];
        for (int i=0;i<etv.length;i++){
            etv[i]=0;
        }
        /**
         * 将度为0的入栈
         */
        for (int i = 0; i < vexList.length; i++) {
            if (vexList[i].in == 0) {
                stack.push(vexList[i]);
            }
        }
        /**
         * 遍历领接表里面边结点,遍历到入度就减一
         */
        while (!stack .empty()) {
            p=stack.pop();
            count++;
            /**
             * 拓扑排序的逆序加入栈2中
             */
            stack1.push(p);
            System.out.print(p.getName()+" ");
            if(p.getNext()!=null){
                p1=p.getNext();
            }
            /**
             * 核心算法计算事件最早发生时间etv
             */
            while (p1!=null){
                vexList[p1.getAdjvex()].setIn(vexList[p1.getAdjvex()].getIn()-1);
                if(vexList[p1.getAdjvex()].getIn()==0){
                    stack.push(vexList[p1.getAdjvex()]);
                }
                if(etv[p.getData()]+p1.getInfo()>etv[p1.getAdjvex()]){
                    etv[p1.getAdjvex()]=etv[p.getData()]+p1.getInfo();
                }
                p1=p1.getNext();
            }
        }
        /**
         * 计数小于节点数就有回路
         * 等于就无回路
         */
        if(count!=vexList.length){
            System.out.println();
            System.out.println("图有回路!!");
            return true;
        }else {
            System.out.println();
            System.out.println("图无回路!!");
        }
        return false;
    }

    /**
     * 关键路径的方法
     */
    public void criticalPath() {
        /**
         * 活动的最早发生时间 ee
         * 活动发生的最晚时间 el
         * p 指针扫描事件节点
         * p1 扫描活动节点
         */
        int ee,el;
        vertexNode p = new vertexNode();
        lineNode p1 = new lineNode();
        /**
         * 先进行拓扑排序判断图有没有环
         */
        if (topologicalSort()){
            return;
        }
        /**
         * 初始化ltv数组
         */
         finall=stack1.peek().getData();
        ltv=new int[vexList.length];
        for (int i=0;i<vexList.length;i++){
            ltv[i]=etv[finall];
        }
        /**
         * 已经获得了拓扑排序的逆序stack2,所以对逆序求最晚发生时间
         */
        while (!stack1.empty()){
            p=stack1.pop();
            if(p.getNext()!=null){
                p1=p.getNext();
            }
            while (p1!=null){
                if (ltv[p1.getAdjvex()]-p1.getInfo()<ltv[p.getData()]){
                    ltv[p.getData()]=ltv[p1.getAdjvex()]-p1.getInfo();
                }
                p1=p1.getNext();
            }
        }

        for (int i=0;i<visited.length;i++){
            visited[i]=1;
        }
        System.out.print("关键活动为:");
        /**
         * 求ee,el和关键路径 count1表示关键活动的数量
         */
        int count1=0;
        for (int i=0;i<vexList.length;i++){
            lineNode pn=new lineNode();
            pn=vexList[i].getNext();
            while (pn!=null){
                ee=etv[i];
                el=ltv[pn.getAdjvex()]-pn.getInfo();
                if(ee==el){
                    count1++;
                   visited[vexList[i].getData()]= 0;
                    System.out.print(pn.getName()+",");
                }
                pn=pn.getNext();
            }

        }
        System.out.println();
        System.out.println("最大时间:"+ltv[finall]);
           System.out.println();
    }

    /**
     * 为了输出每条关键路径用到DFS算法
     * @param k 起点再数组中的下标
     */
    public void DFS(int k){
        visited[k]=1;
        lineNode p=new lineNode();
        p=vexList[k].getNext();
        /**
         * 终点永远是关键节点
         */
        visited[finall]=0;
        /**
         * 如果走到终点遍历栈的元素
         */
        if(k==finall) {
            for (int i=0;i<stack2.size;i++) {
                System.out.print(stack2.peekTravel(i).getName() + "->");
            }
        }
        /**
         * 遍历节点后面的链表
         */
        while (p!=null){
            if(visited[p.getAdjvex()]!=1){
                /**
                 * 是关键路径就入栈
                 */
                stack2.push(p);
                DFS(p.getAdjvex());
                /**
                 * 遇见死路回退的时候出栈
                 */
                stack2.pop();
            }
            System.out.println();
            p=p.getNext();
        }
    }

    /**
     * 活动节点
     */
    class lineNode {
        /**
         * 存储该顶点对应的下标
         */
        private int adjvex;
        /**
         * 存储权值
         */
        private int info;
        /**
         * 指向下一个节点
         */
        private lineNode next;
        /**
         * 活动的名字
         */
        private String name;

        public boolean isMark() {
            return mark;
        }

        public void setMark(boolean mark) {
            this.mark = mark;
        }

        /**
         * 标识位:标识有没有遍历过
         */
        private boolean mark;

        /**
         * 弧头
         *
         */
        private int head;

        public int getHead() {
            return head;
        }

        public void setHead(int head) {
            this.head = head;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public lineNode(int head,int adjvex, int info, lineNode next, String name,boolean mark) {
            this.adjvex = adjvex;
            this.info = info;
            this.next = next;
            this.name=name;
            this.mark=mark;
            this.head=head;
        }
        public lineNode() {

        }

        public int getAdjvex() {
            return adjvex;
        }

        public void setAdjvex(int adjvex) {
            this.adjvex = adjvex;
        }

        public int getInfo() {
            return info;
        }

        public void setInfo(int info) {
            this.info = info;
        }

        public lineNode getNext() {
            return next;
        }

        public void setNext(lineNode next) {
            this.next = next;
        }
    }

    /**
     * 事件节点
     */
    class vertexNode {


        /**
         * 节点的名字
         */
        private String name;
        /**
         * 入度
         */
        private int in;
        /**
         * 储存顶点数组的下标
         */
        private int data;
        /**
         * 边的节点的头指针
         */

        private lineNode next;

        public int getIn() {
            return in;
        }

        public int getData() {
            return data;
        }

        public lineNode getNext() {
            return next;
        }

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

        public void setData(int data) {
            this.data = data;
        }

        public void setNext(lineNode next) {
            this.next = next;
        }


        public vertexNode( int data, lineNode next, String name) {

            this.data = data;
            this.next = next;
            this.name = name;
        }

        public vertexNode() {

        }
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
    public static void main(String[] args) {
        lessonWord a=new lessonWord();
        a.craeteGraph();
//        a.createGraph();
        a.criticalPath();
        a.DFS(0);
    }
}
栈:
package dataproject.stack;
import dataproject.importanRoad.lessonWord;
import java.util.Arrays;
/**
 * @author 李正阳  17060208112
 * @param <E> 泛型
 */
public class myArrayStack<E> {
    private int DEFAULT_SIZE = 16;//定义栈的初始默认长度
    private int capacity;//保存顺序栈的长度
    public int size;//保存顺序栈中元素的个数
    private Object[] elementData;//定义一个数组用于保存顺序栈中的元素

    public myArrayStack() {
        capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }

    //以指定的大小来创建栈
    public myArrayStack(int initSize){
        capacity = 1;
        while(capacity < initSize) {
            capacity <<= 1;//将capacity设置成大于initSize的最小2次方
            elementData = new Object[capacity];
        }
    }

    //返回当前顺序栈中元素的个数
    public int length() {
        return size;
    }

    public E pop() {
        if(empty()) {
            throw new IndexOutOfBoundsException("栈空,不能出栈");
        }
        E oldValue = (E)elementData[size - 1];
        elementData[--size] = null;//让垃圾回收器及时回收,避免内存泄露
        return oldValue;
    }

    public void push(E element) {
        ensureCapacity(size + 1);
        elementData[size++] = element;
    }


    private void ensureCapacity(int minCapacity){
        if(minCapacity > capacity){
            while(capacity < minCapacity) {
                capacity <<= 1;
                elementData = Arrays.copyOf(elementData, capacity);
            }
        }
    }

    //获取栈顶元素,不会将栈顶元素删除
    public E peek() {
        if(size == 0) {
            throw new ArrayIndexOutOfBoundsException("栈为空");
        }
        return (E)elementData[size - 1];
    }

    /**
     * @param d 这个元素的位置
     * @return 返回d所处的元素
     */
    public E peekTravel(int d) {
        if(size == 0) {
            throw new ArrayIndexOutOfBoundsException("栈为空");
        }
        return (E)elementData[d];
    }
    public boolean empty() {
        return size == 0;
    }
    public void clear() {
        for(int i = 0; i < size; i++) {
            elementData[i] = null;
        }
        size = 0;
    }

    public static void main(String[] args) {

    }

}

流程图:

 

posted @ 2019-02-07 12:38  dark_Souls  阅读(1497)  评论(0编辑  收藏  举报