数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历

一、图的基本概念

:是由结点集合及结点间的关系集合组成的一种数据结构。

结点和边:图中的顶点称作结点,图中的第i个结点记做vi。

有向图: 在有向图中,结点对<x ,y>是有序的,结点对<x,y>称为从结点x到结点y的一条有向边,因此,<x,y>与<y,x>是两条不同的边。有向图中的结点对<x,y>用一对尖括号括起来,x是有向边的始点,y是有向边的终点,有向图中的边也称作

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

无向图 :在无向图中,结点对(x,y)是无序的,结点对(x,y)称为与结点x和结点y相关联的一条边。(x,y)等价于<x,y>和

<y,x>。

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

完全图 :在有n个结点的无向图中,若有n(n-1)/2条边,即任意两个结点之间有且只有一条边,则称此图为无向完全图。在有n个结点的有向图中,若有n(n-1)条边,即任意两个结点之间有且只有方向相反的两条边,则称此图为有向完全图。

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

邻接结点 :在无向图G中,若(u,v)是E(G)中的一条边,则称u和v互为邻接结点,并称边(u,v)依附于结点u和v。在有向图G中,若<u,v>是E(G)中的一条边,则称结点u邻接到结点v,结点v邻接自结点u,并称边<u,v>和结点u和结点v相关联。

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

结点的度 :结点v的度是与它相关联的边的条数,记作TD(v)。

路径 :在图G=(V,E)中,若从结点vi出发有一组边使可到达结点vj,则称结点vi到结点vj的结点序列为从结点vi到结点vj的路径

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

:有些图的边附带有数据信息,这些附带的数据信息称为权。第i条边的权用符号wi表示。

路径长度 :对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一条路径的路径长度是指该路径上各个边权值的总和。

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

子图 :设有图G1={V1,E1}和图G2={V2,E2},若V2V1且E2E1,则称图G2是图G1的子图。

连通图和强连通图 :在无向图中,若从结点vi到结点vj有路径,则称结点vi和结点vj是连通的。如果图中任意一对结点都是连通的,则称该图是连通图。在有向图中,若对于任意一对结点vi和结点vj(vi≠vj)都存在路径,则称图G是强连通图。

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

最小生成树 :一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图联通的最少的边。(n-1)条边。

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

二、图的存储结构–图的临界矩阵存储结构

假设图G=(V,E)有n个结点,即V={v0,v1,…,vn-1},E可用如下形式的矩阵A描述,对于A中的每一个元素aij,满足:

 由于矩阵A中的元素aij表示了结点vi和结点vj之间边的关系,或者说,A中的元素aij表示了结点vi和结点vj(0≤j≤n-1)的邻接关系,所以矩阵A称作邻接矩阵。

1、无向图及其临界矩阵

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

2、有向图及其邻接矩阵

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

3、带权图及其临界矩阵

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

三、邻接矩阵类图的设计与图的深度优先遍历与广度优先遍历算法

图的遍历算法设计需要考虑三个问题:

 (1)图的特点是没有首尾之分,所以算法的参数要指定访问的第一个结点;

 (2)对图的遍历路径有可能构成一个回路,从而造成死循环,所以算法设计要考虑遍历路径可能出现的死循环问题;

 (3)一个结点可能和若干个结点都是邻接结点,要使一个结点的所有邻接结点按照某种次序被访问。

1、连通图的深度优先遍历算法

(1)访问结点v并标记结点v为已访问;

(2)查找结点v的第一个邻接结点w;

(3)若结点v的邻接结点w存在,则继续执行,否则算法结束;

(4)若结点w尚未被访问则深度优先搜索递归访问结点w;

(5)查找结点v的w邻接结点的下一个邻接结点w,

2、连通图的广度优先遍历算法

(1)访问初始结点v并标记结点v为已访问;

(2)结点v入队列;

(3)当队列非空时则继续执行,否则算法结束;

(4)出队列取得队头结点u;

(5)查找结点u的第一个邻接结点w;

(6)若结点u的邻接结点w不存在,则转到步骤(3),否则循环执行,

   (6.1)若结点w尚未被访问,则访问结点w,并标记结点w为已访问;

   (6.2)结点w入队列;

   (6.3)查找结点u的w邻接结点后的下一个邻接结点w,转到步骤(6)。

以下图有向图为例:

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * @Description: 邻接矩阵类(有向)
 * @Author: Ni Fujia
 * @CreateDate: 2018/7/14 14:09
 **/
public class MyAdjGraphic {
    //如果两个节点之间没有边,权值为-1
    static final int MAXWEIGHT = -1;
    //存放节点的集合
    List vertices = new ArrayList();
    //邻接矩阵的二维数组
    int[][] edegs;
    //边的数量
    int numOfEdges;

    public MyAdjGraphic(int numOfEdges) {
        init(numOfEdges);
    }

    /**
     * @param numOfEdges 边的数量
     * @Description: 初始化临街矩阵
     * @Return: void
     * @Author: Ni Fujia
     * @CreateDate: 2018/7/14 14:14
     */
    private void init(int numOfEdges) {
        this.edegs = new int[numOfEdges][numOfEdges];
        for (int i = 0; i < numOfEdges; i++) {
            for (int j = 0; j < numOfEdges; j++) {
                //对角线上的元素
                if (i == j) {
                    this.edegs[i][j] = 0;
                }else{
                    this.edegs[i][j] = MAXWEIGHT;
                }
            }
        }
        this.numOfEdges = 0;
    }

    //返回边的数量
    public int getNumOfEdges() {
        return this.numOfEdges;
    }

    //返回节点的数量
    public int getVertices() {
        return this.vertices.size();
    }

    //返回节点的值
    public Object getValueOfVertices(int index) {
        return this.vertices.get(index);
    }

    //获取某条边的权值
    public int getWeightOfVertices(int row, int col) {
        if ((row < 0 || row >= vertices.size()) || (col < 0 || col >= vertices.size())) {
            throw new IndexOutOfBoundsException("row或者col参数不合法");
        }
        return this.edegs[row][col];
    }

    //插入节点
    public void insertVertices(Object obj) {
        this.vertices.add(obj);
    }

    //插入带权值的边
    public void insertEdges(int row, int col, int weight) {
        if ((row < 0 || row >= vertices.size()) || (col < 0 || col >= vertices.size())) {
            throw new IndexOutOfBoundsException("row或者col参数不合法");
        }
        this.edegs[row][col] = weight;
        this.numOfEdges++;
    }

    //删除某条边
    public void deleteEdges(int row, int col) throws Exception {
        if ((row < 0 || row >= vertices.size()) || (col < 0 || col >= vertices.size())) {
            throw new IndexOutOfBoundsException("row或者col参数不合法");
        }
        //边不存在的情况
        if (row == col || this.edegs[row][col] == MAXWEIGHT) {
            throw new Exception("边不存在");
        }

        this.edegs[row][col] = MAXWEIGHT;
        this.numOfEdges--;
    }

    //打印邻接矩阵
    public void print() {
        for (int i = 0; i < edegs.length; i++) {
            for (int j = 0; j < edegs[i].length; j++) {
                System.out.print(edegs[i][j] + "\t");
            }
            System.out.println();
        }
    }

    //取第一个邻接节点
    public int getFirstNeighbor(int row) {
        if (row < 0 || row >= vertices.size()) {
            throw new IndexOutOfBoundsException("vertice参数不合法");
        }

        for (int col = 0; col < this.vertices.size(); col++) {
            if (this.edegs[row][col] > 0) {
                return col;
            }
        }
        return -1;
    }

    //取下一个邻接节点
    public int getNextNeighbor(int row,int col){
        if ((row < 0 || row >= vertices.size()) || (col < 0 || col >= vertices.size())) {
            throw new IndexOutOfBoundsException("row或者col参数不合法");
        }

        for (int result = col + 1; result < this.vertices.size(); result++) {
            if (this.edegs[row][result] > 0) {
                return result;
            }
        }

        return -1;
    }

    /**
     * @Description: 连通图的深度优先遍历的真正实现算法
     * @param vertice 以vertice为初始节点序号
     * @param visited visited数组标记节点是否被访问过,true表示访问过,false表示未访问
     * @Return: void
     * @Author: Ni Fujia
     * @CreateDate: 2018/7/15 10:55
     */
    private void depthFirstSearch(int vertice, boolean visited[]) {
        //访问该节点
        System.out.print(this.getValueOfVertices(vertice)+"  ");
        //将改节点标记为已访问
        visited[vertice] = true;
        //取第一个邻接节点
        int col = getFirstNeighbor(vertice);
        //当邻接节点存在时循环
        while (col != -1) {
            //如果没有访问过,以col为初始节点递归遍历
            if (!visited[col]) {
                depthFirstSearch(col, visited);
            }
            //取下一个邻接节点
            col = getNextNeighbor(vertice, col);
        }
    }

    //连通图的深度优先遍历算法
    public void depthFirstSearch(){
        boolean[] visited = new boolean[this.vertices.size()];
        for (int i = 0; i < visited.length; i++) {
            visited[i] = false;
        }
        for (int j = 0; j < visited.length; j++) {
            if (!visited[j]) {
                depthFirstSearch(j, visited);
            }
        }
    }

    /**
     * @Description: 广度优先遍历算法的真正实现
     * @param vertice 初始节点
     * @param visited  数组visited标记节点是否被访问过
     * @Return: void
     * @Author: Ni Fujia
     * @CreateDate: 2018/7/15 11:52
     */
    private void broadFirstSearch(int vertice, boolean visited[]) {
        int u,w;
        //创建顺序队列queue
        Queue queue = new LinkedList();
        System.out.print(this.getValueOfVertices(vertice) + "  ");
        visited[vertice] = true;
        //结点vertice入队列
        queue.add(new Integer(vertice));
        while (!queue.isEmpty()) {
            //出队列
            u = ((Integer) queue.remove()).intValue();
            w = getFirstNeighbor(u);
            while (w != -1) {
                if (!visited[w]) {
                    System.out.print(this.getValueOfVertices(w)+"  ");
                    visited[w] = true;
                    queue.add(new Integer(w));
                }
                //取结点u的邻接结点w的下一个邻接结点
                w=this.getNextNeighbor(u, w);
            }
        }
    }

    //连通图的深度优先遍历算法
    public void broadFirstSearch(){
        boolean[] visited = new boolean[this.vertices.size()];
        for (int i = 0; i < visited.length; i++) {
            visited[i] = false;
        }
        for (int j = 0; j < visited.length; j++) {
            if (!visited[j]) {
                broadFirstSearch(j, visited);
            }
        }
    }
}
/**
 * @Description: 描述边的权值类
 * @Author: Ni Fujia
 * @CreateDate: 2018/7/14 14:07
 **/
public class Weight {
    int row; //横坐标
    int col; //纵坐标
    int weight; //权值


    public Weight(int row, int col, int weight) {
        this.row = row;
        this.col = col;
        this.weight = weight;
    }

    /**
     * @param myAdjGraphic  临界矩阵对象类
     * @param vertices      节点对象数组
     * @param weights       权值数组
     * @param numOfVertices 节点的数量
     * @param numOfEdges    边的数量
     * @Description: 创建矩阵
     * @Return: void
     * @Author: Ni Fujia
     * @CreateDate: 2018/7/14 14:35
     */
    public static void createAdjGraphic(MyAdjGraphic myAdjGraphic, Object vertices[],
                                        Weight weights[], int numOfVertices, int numOfEdges) {

        //初始化节点
        for (int i = 0; i < numOfVertices; i++) {
            myAdjGraphic.insertVertices(vertices[i]);
        }

        //初始化所有的边
        for (int j = 0; j < numOfEdges; j++) {
            myAdjGraphic.insertEdges(weights[j].row, weights[j].col, weights[j].weight);
        }

    }
}

测试:

**
 * @Description: 测试图的邻接矩阵
 * @Author: Ni Fujia
 * @CreateDate: 2018/7/14 14:42
 **/
public class Test {
    public static void main(String[] args) {
        //节点的数量
        int numOfVertices = 5;
        //边的数量
        int numOfEdges = 5;
        MyAdjGraphic myAdjGraphic = new MyAdjGraphic(numOfEdges);
        //节点数组
        Object[] vertices = new Object[]{
                new Character('A'),
                new Character('B'),
                new Character('C'),
                new Character('D'),
                new Character('E')
        };

        //权值数组
        Weight[] weights = new Weight[]{
                new Weight(0,1,10),
                new Weight(0,4,20),
                new Weight(2,1,40),
                new Weight(1,3,30),
                new Weight(3,2,50)
        };
        Weight.createAdjGraphic(myAdjGraphic, vertices, weights, numOfVertices, numOfEdges);
        System.out.println("改邻接矩阵如下:");
        myAdjGraphic.print();
        System.out.println("邻接矩阵的节点数量为:" + myAdjGraphic.getVertices());
        System.out.println("邻接矩阵的边的数量为:" + myAdjGraphic.getNumOfEdges());

        try {
            /*myAdjGraphic.deleteEdges(0, 4);
            System.out.println("删除E之后的临界矩阵为:");
            myAdjGraphic.print();*/
            System.out.println("邻接矩阵的深度优先遍历算法结果是:");
            myAdjGraphic.depthFirstSearch();
            System.out.println();
            System.out.println("邻接矩阵的广度优先遍历算法结果是:");
            myAdjGraphic.broadFirstSearch();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

《数据结构与算法(Java描述)-20、图、图的邻接矩阵、有向图的广度优先遍历与深度优先遍历》

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/m0_37897502/article/details/81043803
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞