二部グラフの概念
二部グラフの頂点セットは、互いに素な 2 つのサブセットに分割できます。グラフの各エッジに接続されている 2 つの頂点は、これら 2 つのサブセットに属し、2 つのサブセット内の頂点は隣接していません。
定義を見るだけではよく分からないかもしれませんが、簡単に言えば、二部グラフは次の特性を満たします: グラフの各辺で結ばれた 2 つの点は同じ集合に属さない (グラフの各点を 1 色で塗った場合) 、画像内の 2 つの色: 両側の 2 つの点の色は異なる必要があります)
まず、2 部グラフは特殊なグラフ モデルとして、多くの高度なグラフ アルゴリズム (最大フロー アルゴリズムなど) で使用されていますが、これらの高度なアルゴリズムを特に習得する必要はありません。彼ら自身。
シンプルかつ実用的な観点から見ると、2 部グラフ構造は特定のシナリオでより効率的にデータを保存できます。
たとえば、映画と俳優の関係を保存するにはデータ構造が必要です。特定の映画には複数の俳優が出演する必要があり、特定の俳優は複数の映画に出演する可能性があります。この関係を保存するためにどのようなデータ構造を使用しますか?
これはストレージ マッピング関係であるため、最も単純なことはハッシュ テーブルを使用することです。ハッシュ テーブルを使用して、
HashMap<String, List<String>>
映画から俳優リストへのマッピングを保存できます。映画の名前を指定すると、その映画に主演した俳優をすぐに取得できます。映画。しかし、俳優の名前を指定して、その俳優が出演しているすべての映画をすぐに入手したい場合はどうすればよいでしょうか? これには、「逆インデックス作成」が必要で、前のハッシュ テーブルに対していくつかの操作を実行し、俳優をキー、映画リストを値として持つ別のハッシュ テーブルを作成します。
明らかに、ストレージにハッシュ テーブルが使用される場合、「各俳優から映画リストへ」のマッピングと「各映画から俳優リストへ」のマッピングを格納するために 2 つのハッシュ テーブルが必要になります。しかし、「グラフ」構造を使用してそれを保存し、映画とそれに参加している俳優を結び付けると、自然に 2 部グラフになります。
二部図の問題テンプレート
二部グラフ問題の解決テンプレートは次のとおりです。
/* 图遍历框架 */
void traverse(Graph graph, boolean[] visited, int v) {
visited[v] = true;
// 遍历节点 v 的所有相邻节点 neighbor
for (int neighbor : graph.neighbors(v)) {
if (!visited[neighbor]) {
// 相邻节点 neighbor 没有被访问过
// 那么应该给节点 neighbor 涂上和节点 v 不同的颜色
traverse(graph, visited, neighbor);
} else {
// 相邻节点 neighbor 已经被访问过
// 那么应该比较节点 neighbor 和节点 v 的颜色
// 若相同,则此图不是二分图
}
}
}
トピック
質問の説明
リートコードhttps://leetcode.cn/problems/vEAB3K/
グラフ内に n 個のノードを持つ無向グラフがあります。各ノードには 0 から n - 1 までの一意の番号があります。
グラフを表す 2 次元配列グラフが与えられたとします。ここで、graph[u] はノード u の隣接ノードで構成されるノード配列です。形式的には、graph[u] 内のすべての v について、ノード u とノード v の間に無向エッジが存在します。この無向グラフには次の特性もあります。
自己ループはありません (graph[u] には u が含まれません)。
平行なエッジはありません (graph[u] には重複する値が含まれません)。
v がグラフ[u] にある場合、u もグラフ[v] に存在する必要があります (グラフは無向グラフです)。このグラフは
接続されたグラフではない可能性があります。つまり、グラフ間に接続がない可能性があります。 2 つのノード u と v。互いのパス。
二部グラフの定義: グラフのノード セットが 2 つの独立したサブセット A と B に分割でき、グラフの各エッジの 2 つのノードのうち 1 つがセット A に由来し、もう 1 つがセット B に由来する場合、グラフはは二部グラフと呼ばれます。グラフが 2 部グラフの場合は true を返し、それ以外の場合は false を返します。
例 1:
入力: chart = [[1,2,3],[0,2],[0,1,3],[0,2]]
出力: false
説明: ノードを 2 つの独立したサブセットに分割することはできません。エッジは、あるサブセット内のノードを別のサブセット内のノードに接続します。
例 2:入力: chart = [[1,3],[0,2],[1,3],[0,2]]
出力: true
説明: ノードは 2 つのグループに分けることができます: {0, 2} と { 1、3}。
ヒント:
chart.length == n
1 <= n <= 100
0 <=graph[u].length < n
0 <=graph[u][i] <= n - 1graph
[u]にはuは含まれません
graph[u] ] のすべての値は互いに異なります。graph
[u] に v が含まれる場合、graph[v] にも u が含まれます。
問題解決のアイデア
実際、この質問は、グラフ構造のトラバースに基づいていくつかの変更を加えることです。グラフのトラバース中に必要なのはトラバースのみですが、この質問では、トラバース中に対応するノードをグループ化する (対応する色でノードをペイントする) 必要があるため、次のことが保証されます。通過した各ノードは対応する色でペイントされ、以前に通過したノードを通過し、現在のノードの色が接続されているノードの色と同じであることが判明するまで再帰が続きます。すると、競合が発生します。今回は、このグラフが 2 部グラフではないと判断します (まだ走査されていない場合は、走査されて色でマークされ、再帰の次のステップが実行されます。ノードが以前に走査されている場合は、ノードとそのノードを比較する必要があります。接続されているノードの色が競合するかどうかを確認します。競合がない場合は、単にバックトレースします。競合がある場合は、前に設定した識別子キーを使用し、キーを false に設定して、直接戻ります)
コード例
class Solution {
//定义结果
boolean ok=true;
//定义数组判断保存颜色
boolean[]color;
//定义数组判断是否添加过
boolean[]visited;
public boolean isBipartite(int[][] graph) {
int size=graph.length;
//初始化
color=new boolean[size];
visited=new boolean[size];
//避免存在多个图
for(int i=0;i<size;++i){
if(!visited[i])
reverse(graph,i);
}
return ok;
}
//递归函数
public void reverse(int[][]graph,int index){
//出口条件
if(!ok){
return;
}
//将结果设置为参观
visited[index]=true;
//处理邻接节点
for(int element:graph[index]){
if(!visited[element]){
color[element]=!color[index];
//递归
reverse(graph,element);
}else{
if(color[element]==color[index]){
ok=false;
return;
}
}
}
}
}