图——深度优先和广度优先

一.图的遍历

图的遍历,即是对结点的访问,从图中的任一顶点出发,对图中的所有顶点访问一次且只访问一次。一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略:

1.深度优先遍历:体现了栈的思想,假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可从图中某个顶点发v 出发,访问此顶点,然后依次从v 的未被访问的邻接点出发深度优先遍历图,直至图中所有和v 有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止,类似于树的先根遍历,是树的先根遍历的推广;

2.广度优先遍历:体现了队列的思想 ,假设从图中某顶点v 出发,在访问了v 之后依次访问v 的各个未曾访问过和邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程中以v 为起始点,由近至远,依次访问和v 有路径相通且路径长度为1,2,…的顶点,类似于树的按层次遍历;


 二.图的结构模型

1.邻接矩阵:使用二维数组实现,空间上会造成浪费。

2.领接表:使用数组+链表实现,顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储。

3.十字链表:在有向图中,对于邻接表来说,计算顶点的入度是不方便的,因此可修改调整节点结构,构建十字链表,方便计算顶点的出度和入度。


三.图的遍历实现

1.深度优先

(1)访问顶点v;

(2)从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;

(3)重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

①递归实现

(1)访问顶点v;visited[v]=1;//算法执行前visited[n]=0

(2)w=顶点v的第一个邻接点;

(3)while(w存在)  

           if(w未被访问)

                   从顶点w出发递归执行该算法; 
           w=顶点v的下一个邻接点;

②栈实现

(1)栈S初始化;visited[n]初始化为0;

(2)访问顶点v;visited[v]=1;顶点v入栈S

(3)while(栈S非空)

            x=栈S的顶元素(出栈);

            while(x的邻接点w)

      if(w存在并未被访问)

                         访问w;visited[w]=1;

                         w进栈;

2.广度优先

队列实现:与栈实现基本相同,只是使用的是队列。


 

四.代码示例

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;

public class graphTraverse {

    public static void bfsMethod(int[][] adjMatrix, int sourceNode){
        //利用队列
        Queue<Integer> queue = new LinkedList<Integer>();
        int numOfNodes = adjMatrix[sourceNode].length;
        int[] isVisited = new int[numOfNodes];//没访问过的初始化为0

        isVisited[sourceNode] = 1;
        queue.add(sourceNode);

        int i = 0;//index表示
        int element = 0;//当前元素

        while(!queue.isEmpty()){
            element = queue.remove();
            System.out.print(element+" ");
            i = 0;
            while(i < numOfNodes){
                if(adjMatrix[element][i] == 1 && isVisited[i] == 0){
                    queue.add(i);//按照最先遍历到的1进入队列为原则, 如果有好几个1在一行,也就是一个顶点与多个顶点相连
                    isVisited[i] = 1;
                }
                i++;
            }
        }
    }

    public static void dfsMethod(int [][] adjMatrix, int sourceNode){
        //利用栈,因为需要回溯,弹出一个节点的同时把它的儿子压入栈中,把儿子弹出来时,把孙子压入栈中,所以就可以按深度了
        Stack<Integer> stack = new Stack<Integer>();
        int numOfNodes = adjMatrix[sourceNode].length;
        int[] isVisited = new int[numOfNodes];

        stack.push(sourceNode);
        isVisited[sourceNode] = 1;

        int element = 0;

        while(!stack.isEmpty()){
            element = stack.pop();
            System.out.println(element + " ");
            for(int i = 0; i < numOfNodes; i++){
                 if(adjMatrix[element][i] == 1 && isVisited[i] == 0){
                     stack.push(i);
                     isVisited[i] = 1;
                 }
            }
        }
    }

    public static void main(String[] args){

        int numOfNodes = 0;
        int sourceNode = 0;//设置哪一个点为起始点
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入图的节点总数:");
        numOfNodes = scanner.nextInt();

        int[][] adjMatrix = new int[numOfNodes][numOfNodes];
        System.out.println("请输入图的临接矩阵:");
        for(int i = 0; i < numOfNodes; i++){
            for(int j = 0; j < numOfNodes; j++){
                adjMatrix[i][j] = scanner.nextInt();
            }
        }

        System.out.println("请输入图开始遍历的起点Source(从0开始编号):");
        sourceNode = scanner.nextInt();

        System.out.println("图的广度优先遍历(BFS)结果为(从0开始编号):");
        bfsMethod(adjMatrix, sourceNode);

        System.out.println("\n图的深度优先遍历(DFS)结果为(从0开始编号):");
        dfsMethod(adjMatrix, sourceNode);

        scanner.close();

    }
}

  

posted on 2018-01-16 15:46  干饭君  阅读(432)  评论(0)    收藏  举报

导航