カスタム有向グラフの深さ優先トラバーサル (DFS)

グラフとは何ですか?

次の抽象構造のようになります。
描く

ちょっと抽象的ですね ̄□ ̄||。

別のものを見てみましょう。
抽象的な木

これは二分木で、前の図からいくつかの「接続」を引いて木になっています。

したがって、グラフはある程度まではツリーの延長として理解できます (グラフはツリーの「規則」をさらに破っています)。

プログラム内の「グラフ」

グラフストレージ

  • ノード要素を保存するにはコレクション (Set) が必要です。
  • ノードが訪問されたかどうかを保存するには、マップ (HashMap) が必要です。
  • HashMap<T, ArrayList> は、ノード間のパスを保存するために必要です。

以下に示すように:
グラフメモリモデル

コード

DFS 深さ優先トラバーサル アルゴリズムも含まれています。


import java.util.*;

/**
 * @ClassName ArrayGraph
 * @Description 自定义“有向图”class,不允许有重复的元素
 * @Author SkySong
 * @Date 2021-05-16 17:14
 */
public class ArrayGraph<T> {
    
    
    //存放节点元素
    private Set<T> vars;
    //标记节点是否被访问过
    private HashMap<T, Boolean> visit;
    //节点间的通路
    private HashMap<T, ArrayList<T>> accesses = new HashMap<>();

    /**
     * 清空访问
     */
    public void clearVisit() {
    
    
        try {
    
    
            vars.forEach((k) -> visit.put(k, false));
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    /**
     * 初始化节点集合
     *
     * @param vars 节点集合
     */
    public ArrayGraph(Set<T> vars) {
    
    
        this.vars = vars;
        visit  = new HashMap<>();
        clearVisit();
    }

    /**
     * 添加节点间通路
     *
     * @param from 出发节点
     * @param to   目的节点
     */
    public void addAccess(T from, T to) {
    
    
        if (!vars.contains(from) && !vars.contains(to)) {
    
    
            return;
        }
        ArrayList<T> ts = accesses.get(from);
        if (ts == null) {
    
    
            ts = new ArrayList<>();
        }
        ts.add(to);
        accesses.put(from, ts);
    }

    /**
     * DFS 深度优先遍历
     *
     * @param head 起始节点
     * @return 图 “变” 数组
     */
    public List<T> DFSOrder(T head) {
    
    
        if (!vars.contains(head)) {
    
    
            return null;
        }
         //创建一个list,用来存放最终的有序 序列
        ArrayList<T> list = new ArrayList<>();
        //毫无疑问,第一个遍历的节点元素一定是 我们传进去的 head
        list.add(head);
        //确定 head 的通路(head通向的节点)
        ArrayList<T> ts = accesses.get(head);
        if (ts == null || ts.isEmpty()){
    
    
            visit.put(head,true);
            return list;
        }
        //改变 head 的访问状态
        visit.put(head,true);
        Stack<T> stack = new Stack<>();
        stack.push(head);
        while (!stack.isEmpty()){
    
    
            int index = 0;
            for (T t : ts) {
    
    
                //如果此节点已经访问过了,我们就不做任何操作
                if (visit.get(t)){
    
    
                    continue;
                }
                //如果此节点没有访问过,访问之
                list.add(t);
                //改变节点访问状态
                visit.put(t,true);
                //探寻此节点的下一层“通路”
                ts = accesses.get(t);
                if (ts != null){
    
    
                    //如果此节点下一层有“通路”,便将此节点放入栈中
                    stack.push(t);
                    //并改变标志位,跳过出栈操作
                    index++;
                }
                break;
            }
            //如果此节点没有下一层可以访问,则触发出栈操作,去上一层寻找
            if (index == 0){
    
    
                T pop = stack.pop();
                ts = accesses.get(pop);
            }
        }
        return list;
    }
}

この DFS では、アイデアを大まかに詳しく説明します。

まず、グラフの走査では開始ノードを決定する必要があることは明らかです。(これは DFS メソッドのパラメータでもあります)。

  • まず第一に、「深さ優先」を完了するための主要なツールであるスタックが必要です。

    「深さ優先」とは、アクセス処理中に分岐に遭遇した場合、まず次の層の分岐を選択してアクセスします。

    スタックの「先入れ後出し」機能を使用して、現在のノードの「上位」を層ごとに保存します。レイヤーごとにそれらを見つけられるようにしてください。

  • 訪問を開始し、各層で 1 つのノードを訪問し、次の層に進みます。下に降りることができないことがわかると (現在のノードに接続されているノードは訪問済み)、1 層上に進み、その後、上記のプロセスを繰り返します。

テスト

public static void main(String[] args) {
    
    
        ArrayGraph<Integer> graph = new ArrayGraph<>(Sets.newHashSet(1,2,3,4,5));
        graph.addAccess(1,2);
        graph.addAccess(1,3);
        graph.addAccess(2,4);
        graph.addAccess(3,4);
        graph.addAccess(5,1);
        
        System.out.println(graph.DFSOrder(5).toString());
    }

結果:

[5、1、2、4、3]

写真を添付すると、誰でも自分で確認できます。
有向グラフ

拡大

この例は有向グラフに関するものですが、無向グラフとしても使用できます。

ちなみに「通路」を加える場合はその逆を加えれば実現できます。

この例では、要素の繰り返しは許可されていないと述べていますが、実際には、この問題を別の方向から考えることもできます。

グラフの実務では要素が参照オブジェクトとなることが多く、比較的複雑なデータ構造となっており、内容は同じでも参照アドレスが異なるなど、ある程度の「同一性」も実現できます。

それがすべてですありがとうございました !

おすすめ

転載: blog.csdn.net/weixin_43415201/article/details/117229439