Javaデータ構造とアルゴリズム(12):図

1.図の基本的な紹介

1.1なぜ写真があるのですか?

  • 線形テーブルは、直接の先行と直接の後続の間の関係に限定されます。
  • ツリーには、親ノードである直接の先行ノードを1つだけ含めることができます。
  • 多対多の関係を表現する必要がある場合は、グラフを使用する必要があります。

1.2図の図

グラフは、ノードが0個以上の隣接する要素を持つことができるデータ構造です。2つのノード間の接続はエッジと呼ばれます。ノードは頂点と呼ばれることもあります。図に示すように:
ここに画像の説明を挿入します

1.3グラフの一般的な概念

  • バーテックス
  • 無向グラフ
    ここに画像の説明を挿入します
  • 有向グラフ
  • 加重グラフ
    ここに画像の説明を挿入します

2グラフの表現

グラフを表現するには、2次元配列表現(先行マトリックス)、リンクリスト表現(先行ジャンクションテーブル)の2つの方法があります。

2.1主要なマトリックス

リード行列は、グラフ内の頂点間の関係を表す行列です.n個の頂点を持つグラフの場合、行列は行と列で表される1 ... n個の点です。
ここに画像の説明を挿入します

2.2リーディングテーブル

  • 先行行列は、各頂点にn個のエッジにスペースを割り当てる必要があります。実際、存在しないエッジが多数あるため、スペースがある程度失われます。
  • リンクテーブルの実現は、存在しないエッジではなく、既存のエッジのみを考慮します。したがって、スペースの無駄はなく、先頭のテーブルは配列とリンクリストで構成されます。
    ここに画像の説明を挿入します

3.グラフをトラバースする方法

グラフのいわゆるトラバーサルは、ノードへの訪問です。一般に、2つのトラバーサル方法があります。

  • 深さ優先探索
  • 幅優先探索

3.1深さ優先探索の基本的な考え方

  • 深さ優先トラバーサルは、初期アクセスノードから開始して、複数の隣接ノードを持つ場合があります。深さ優先トラバーサルの戦略は、最初に最初の隣接ノードにアクセスし、次にこのアクセスした隣接ノードを初期Aノードとして使用することです。 、最初の隣接ノードにアクセスするには、次のように理解できます。現在のノードにアクセスするたびに、現在のノードの最初の隣接ノードに最初にアクセスします。
  • このようなアクセス戦略は、ノードの隣接するすべてのノードに水平方向にアクセスするのではなく、垂直方向の掘削を優先することであることがわかります。
  • 深さ優先探索は再帰的なプロセスです。

3.2深さ優先走査アルゴリズムのステップ

  • 最初のノードvにアクセスし、ノードvをアクセス済みとしてマークします。
  • ノードvの最初の隣接ノードwを見つけます。
  • wが存在する場合は実行を続行し、wが存在しない場合は手順1に戻り、vの次のノードから続行します。
  • wにアクセスしていない場合は、wに対して深さ優先走査再帰を実行します(つまり、wを別のvとして扱い、手順1、2、3に進みます)。
  • ノードvのw隣接ノードの次の隣接ノードを見つけて、ステップ3に進みます。
    ここに画像の説明を挿入します

3.3幅優先探索の基本的な考え方

  • 階層型検索プロセスと同様に、幅優先走査では、訪問したノードの順序を維持するためにキューを使用する必要があります。これにより、これらのノードの隣接ノードをこの順序で訪問できます。

3.4幅優先探索アルゴリズムの手順

  • 最初のノードvにアクセスし、ノードvをアクセス済みとしてマークします。
  • ノードvがキューに入ります。
  • キューが空でない場合は、実行を続行します。空でない場合、アルゴリズムは終了します。
  • キューから出て、ヘッドノードuを取得します。
  • ノードuの最初の隣接ノードwを見つけます。
  • ノードuの隣接ノードwが存在しない場合は、手順3に進みます。存在しない場合は、次の3つの手順をループします。
    • ノードwにまだアクセスしていない場合は、ノードwにアクセスして、訪問済みとしてマークします。
    • ノードwがキューに入ります。
    • wの隣接ノードに続くノードuの次の隣接ノードwを見つけ、ステップ6に進みます。

ここに画像の説明を挿入します
完全なコード

package com.lele.graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * author: hwl
 * date: 2020/12/5 18:52
 * version: 1.0.0
 * modified by:
 * description: 图
 */
public class Graph {
    
    

    private ArrayList<String> vertexList; // 存储顶点集合
    private int[][] edges; // 存储图对应的领接矩阵
    private int numOfEdges; // 表示边的数目
    // 定义给数组boolean[],记录某个节点是否被访问
    private boolean[] isVisited;

    public static void main(String[] args) {
    
    
        // 测试图是否创建ok
        int n = 8;// 结点的个数
//        String Vertexs[] = {"A","B","C","D","E"};
        String Vertexs[] = {
    
    "1","2","3","4","5","6","7","8"};
        // 创建图对象
        Graph graph = new Graph(n);
        // 循环的添加顶点
        for (String vertex : Vertexs) {
    
    
            graph.insertVertex(vertex);
        }

        // 添加边
        // A-B A-C  B-C B-D B-E
//        graph.insertEdge(0,1,1);
//        graph.insertEdge(0,2,1);
//        graph.insertEdge(1,2,1);
//        graph.insertEdge(1,3,1);
//        graph.insertEdge(1,4,1);


        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);
        graph.insertEdge(3,7,1);
        graph.insertEdge(4,7,1);
        graph.insertEdge(2,5,1);
        graph.insertEdge(2,6,1);
        graph.insertEdge(5,6,1);

        // 显示领接矩阵
        graph.showGraph();

        // 测试dfs
        System.out.println("深度遍历");
        graph.dfs();
        System.out.println();
        // 测试广度优先
        System.out.println("广度优先!");
        graph.bfs();
    }

    // 构造器
    public Graph(int n) {
    
    
        // 初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
    }

    /**
     * 得到第一个邻接结点的下标 w
     * @param index
     * @return  如果存在对应的下标,否则返回-1
     */
    public int getFirstNeighbor(int index) {
    
    
        for (int j = 0; j < vertexList.size(); j++) {
    
    
            if (edges[index][j] > 0) {
    
    
                return j;
            }
        }
        return -1;
    }

    /**
     * 根据前一个邻接结点的下标来获取下一个邻接结点
     * @param v1
     * @param v2
     * @return
     */
    public int getNextNeighbor(int v1, int v2) {
    
    
        for (int j = v2 + 1; j < vertexList.size(); j++) {
    
    
            if (edges[v1][j] > 0) {
    
    
                return j;
            }
        }
        return -1;
    }

    /**
     * 深度优先遍历算法
     * i 第一次就是0
     */
    public void dfs(boolean[] isVisited, int i) {
    
    
        // 首先我们访问该结点,输出
        System.out.print(getValueByIndex(i) + "->");
        // 将该结点设置为已经访问
        isVisited[i] = true;
        // 查找结点i的第一个邻接结点w
        int w = getFirstNeighbor(i);
        while(w != -1) {
    
    //说明有
            if (!isVisited[w]) {
    
    
                dfs(isVisited, w);
            }
            // 如果w结点已经被访问过
            w = getNextNeighbor(i, w);
        }
    }

    /**
     * 对dfs进行一个重载,遍历我们所有的结点,并进行dfs
     */
    public void dfs() {
    
    
        isVisited = new boolean[vertexList.size()];
        // 遍历所有的结点,进行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
    
    
            if (!isVisited[i]) {
    
    
                dfs(isVisited, i);
            }
        }
    }

    /**
     * 对一个结点进行广度优先遍历的方法
     * @param isVisited
     * @param i
     */
    private void bfs(boolean[] isVisited, int i) {
    
    
        int u; // 表示队列的头结点对应的下标
        int w; // 邻接结点
        // 队列,记录结点访问的顺序
        LinkedList queue = new LinkedList();
        // 访问结点,输出结点信息
        System.out.print(getValueByIndex(i) + "=>");
        // 标记为已访问
        isVisited[i] = true;
        // 将结点加入队列
        queue.addLast(i);

        while(!queue.isEmpty()) {
    
    
            // 取出队列的头结点下标
            u = (Integer)queue.removeFirst();
            // 获得第一个邻接结点的下标 w
            w = getFirstNeighbor(u);
            while(w != -1) {
    
    // 找到
                // 是否访问过
                if (!isVisited[w]) {
    
    
                    System.out.print(getValueByIndex(w) + "=>");
                    // 标记已经访问
                    isVisited[w] = true;
                    // 入队
                    queue.addLast(w);
                }
                // 以u 为前驱点,找w后面的下一个邻接结点
                w = getNextNeighbor(u, w);// 体现出 广度优先
            }
        }
    }

    /**
     * 遍历所有的结点,都进行广度优先搜索
     */
    public void bfs() {
    
    
        isVisited = new boolean[vertexList.size()];
        for (int i = 0; i < getNumOfVertex(); i++) {
    
    
            if (!isVisited[i]) {
    
    
                bfs(isVisited, i);
            }
        }
    }

    /** 图中 常用的方法 **/
    /**
     * 获取结点的个数
     * @return
     */
    public int getNumOfVertex() {
    
    
        return vertexList.size();
    }

    /**
     * 显示图对应的矩阵
     */
    public void showGraph() {
    
    
        for (int[] link : edges) {
    
    
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 获得边的数目
     * @return
     */
    public int getNumOfEdges() {
    
    
        return numOfEdges;
    }

    /**
     * 返回结点i(下标)对应的数据 0->"A"  1-> "B"  2->"C"
     * @param i
     * @return
     */
    public String getValueByIndex(int i) {
    
    
        return vertexList.get(i);
    }

    /**
     * 返回v1和v2的权值
     * @param v1
     * @param v2
     * @return
     */
    public int getWeight(int v1, int v2) {
    
    
        return edges[v1][v2];
    }

    /**
     * 插入结点
     * @param vertex
     */
    public void insertVertex(String vertex) {
    
    
        vertexList.add(vertex);
    }

    /**
     * 添加边
     * @param v1 表示点的下标 即第几个顶点 "A"-"B", "A"->0  "B"->1
     * @param v2 第二个顶点的下标
     * @param weight 表示
     */
    public void insertEdge(int v1, int v2, int weight) {
    
    
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }
}

おすすめ

転載: blog.csdn.net/houwanle/article/details/110707754
おすすめ