二分探索(全4回)

二分探索とは

二分探索 とは、解の存在範囲を狭めていくことにより最適解を求める手法(アルゴリズム)です。

二分探索で検索

二分探索 で解けるもっとも基本的な問題は、数列から 特定の数字を検索 する問題です。

数列は昇順にソートされている必要があります。

問題

昇順にソートされた数列から、ある数字k以上となるような最小のインデックス(数字の位置)を求めてください。

ソースコード

処理としては、まず中央の値に着目します。

中央の値がk以上であれば 解が中央の位置よりも下 にあることがわかるので、上限の位置を中央に ずらします。

中央の値がk以下であれば 解が中央の位置よりも上 にあることがわかります、下限の位置を中央に ずらします。

こうすることにより、1回の比較で解の存在の範囲を 半分に絞る ことができます。

これ以降、句点の中点での比較を繰り返すことで解の存在の範囲を絞っていき解を求めます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#----- 入力例1 -----
lst = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
k = 5
#----- 入力例2 -----
# lst = [-10,4,6,100,200,400,500]
# k = 10
#----- 入力例3 -----
# lst = range(99999999999)
# k = 30000
#--------------------
n = len(lst)

# 解の存在範囲を初期化
lb = -1 # 下限の範囲
ub = n # 上限の範囲

while ub - lb > 1:
mid = (lb + ub) // 2
if lst[mid] >= k:
ub = mid
else:
lb = mid
print('解', ub)

[実行結果](入力例1)

解 4

インデックス4 の位置の数値は5であり、kと一致します。

[実行結果](入力例2)

解 3

数列に10は存在しませんが、インデックス3 の位置の数字100はk(=10)よりも大きくなります。

[実行結果](入力例3)

解 30000

非常に大きな数列 に対しても、二分探索であれば効率的に検索することができます。

次回からは 二分探索 を応用していろいろな問題を解いていきます。

NetworkX⑥(最短経路問題)

最短経路問題

複数の経路の中から、最も効率の良い経路 を求める問題を 最短経路問題 といいます。

「効率のよい」という意味としては、「時間が短い、費用が安い、距離が短い」などさまざまな基準があります。

NetworkX では、このような基準を weight として数値化します。

まず、最短経路を求めるグラフを定義し、図示してみます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import networkx as nx

G = nx.DiGraph()

# 始点ノード、終了ノード、重さ(weight)を定義
G.add_weighted_edges_from(
[('A', 'B', 4), ('A', 'C', 3), ('B', 'C', 1), ('B', 'D', 1), ('B', 'E', 5),
('C', 'F', 2), ('D', 'E', 3), ('F', 'E', 1), ('E', 'G', 2), ('F', 'G', 4), ]
)

# ノードの位置(グラフ描画用) => ノード名:(横位置, 縦位置)
pos = {'A': (0, 1), 'B': (1, 2), 'C': (1, 0), 'D': (2, 1), 'E': (3, 2),
'F': (3, 0), 'G': (4, 1)}

edge_labels = {}
for (i, j) in G.edges():
edge_labels[i, j] = f"{ G[i][j]['weight'] }"

nx.draw(G, pos=pos, with_labels=True, node_size=500)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)

[実行結果]

ダイクストラ法

最短経路問題 を解くためには、NetworkXdijkstra_path_length関数 を使います。

[Google Colaboratory]

1
2
3
4
5
6
7
8
# ダイクストラ法で最短経路とその重み(最小コスト)を求める
start, end = "A", "G"
shortest_path = nx.dijkstra_path(G, start, end)
shortest_path_weight = nx.dijkstra_path_length(G, start, end)

# 出力
print("Shortest Path:", shortest_path)
print("Weight:", shortest_path_weight)

[実行結果]

Shortest Path: ['A', 'C', 'F', 'E', 'G']
Weight: 8

最短経路は ‘A’ ➡ ‘C’ ➡ ‘F’ ➡ ‘E’ ➡ ‘G’ となり、最小コスト(トータルの重さ)は 8 であることを導き出すことができました。

NetworkX⑤(最小カット問題)

NetworkXを、使って 最小カット問題 を解いてみます。

最小カット問題

グラフに対してsからtへのパスが存在しなくなるために除去しなければいけない辺の 容量の和の最小値 を求める問題を 最小カット問題 といいます。

前回記事で使ったグラフをもう一度作成します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import networkx as nx

G = nx.DiGraph()

#(始点,終点,重み)でエッジを設定
G.add_edges_from([('s', '1', dict(capacity=10)),
('s', '2', dict(capacity=2)),
('1', '2', dict(capacity=6)),
('1', '3', dict(capacity=6)),
('3', '2', dict(capacity=3)),
('2', 't', dict(capacity=5)),
('3', 't', dict(capacity=8)),
])

#エッジラベルの描画時に'capacity'の表示を無くすための工夫
edge_labels = {(i, j): w['capacity'] for i, j, w in G.edges(data=True)}
pos = nx.spring_layout(G, k=10)
nx.draw_networkx_edge_labels(G,pos, edge_labels=edge_labels)

# グラフの描画
nx.draw_networkx(G, pos)

[実行結果]

解法

最小カット問題 を解くためには、minimum_cut関数 を使います。

引数には、グラフオブジェクト始点ノード終了ノード を設定します。

[Google Colaboratory]

1
2
3
4
5
cut_value, partition = nx.minimum_cut(G, 's', 't')
print(f'cut value: {cut_value}')

S, T = partition
print(f' (S, T): ({S}, {T})')

[実行結果]

cut value: 11

  (S, T): ({'1', '2', 's'}, {'3', 't'})

始点側のノードの集合は {‘1’, ‘2’, ‘s’} で、終点側の集合は {‘3’, ‘t’} となりました。

この集合2つをつなぐエッジ(辺)の容量の和は 11 であり、これが最小となります。

最小カット問題 の解は 最大流問題 と解は同じとなり、最大流最小カット定理 と呼ばれます。

NetworkX④(最大流問題)

NetworkXを、使って 最大流問題 を解いてみます。

最大流問題

グラフ間に流れる数値を最大化させる問題を 最大流問題 といいます。

[最大流問題の例]

ネットワーク上の2台のコンピュータs、tがありsからtにデータを送ります。

このネットワークには全部でN台のコンピュータがあり、いくつかのコンピュータ間は一方向性の

通信ケーブルで接続されていて、それぞれ1秒間に通信できる最大のデータ量が決まってきます。

sからtへ1秒間で最大どれだけのデータを送信することができるでしょうか。

まず、ネットワーク上のコンピュータとデータ量をグラフに表示してみます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import networkx as nx

G = nx.DiGraph()

#(始点,終点,重み)でエッジを設定
G.add_edges_from([('s', '1', dict(capacity=10)),
('s', '2', dict(capacity=2)),
('1', '2', dict(capacity=6)),
('1', '3', dict(capacity=6)),
('3', '2', dict(capacity=3)),
('2', 't', dict(capacity=5)),
('3', 't', dict(capacity=8)),
])

#エッジラベルの描画時に'capacity'の表示を無くすための工夫
edge_labels = {(i, j): w['capacity'] for i, j, w in G.edges(data=True)}
pos = nx.spring_layout(G, k=10)
nx.draw_networkx_edge_labels(G,pos, edge_labels=edge_labels)

# グラフの描画
nx.draw_networkx(G, pos)

[実行結果]

解法

最大流問題 を解くためには、maximum_flow関数 を使います。

引数には、グラフオブジェクト、始点ノード、終了ノードを設定します。

[Google Colaboratory]

1
2
3
4
5
6
7
flow_value, flows = nx.maximum_flow(G, 's', 't')
print(f'maximum flow: {flow_value}')

caps = nx.get_edge_attributes(G, 'capacity')
for u in nx.topological_sort(G):
for v, flow in sorted(flows[u].items()):
print(f' ({u}, {v}): {flow}/{caps[(u, v)]}')

[実行結果]

maximum flow: 11

  (s, 1): 9/10

  (s, 2): 2/2

  (1, 2): 3/6

  (1, 3): 6/6

  (3, 2): 0/3

  (3, t): 6/8

  (2, t): 5/5

sからtへの最大流は 11 と導き出すことができました。

また、最大流が流れる際に各ノード間に流すデータ通信も表示しています。

NetworkX③(パス/閉路/放射状の描画)

NetworkX を使っていろいろなタイプの描画を行ってみます。

パス

まずは パス を描画します。

add_path関数 に複数のノード番号を設定します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
import networkx as nx

G = nx.DiGraph() # 有向グラフ (Directed Graph)

# 指定したパス上の頂点と辺を追加
nx.add_path(G, [1, 2, 3, 4, 5]) # 1 → 2 → 3 → 4 → 5

nx.draw_networkx(G)

[実行結果]

順番に ノードを辿ったグラフ が描画されました。

閉路

次に 閉路 を描画します。

add_cycle関数 に複数のノード番号を設定します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
import networkx as nx

G = nx.DiGraph() # 有向グラフ (Directed Graph)

# 指定した閉路上の頂点と辺を追加
nx.add_cycle(G, [1, 2, 3, 4, 5]) # 1 → 2 → 3 → 4 → 5 → 1

nx.draw_networkx(G)

[実行結果]

順番にノード番号を辿り、最後のノードから最初の番号に戻る 閉路グラフ を描画することができました。

放射状

最後に 放射線状 のグラフを描画します。

add_star関数 に複数のノード番号を設定します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
import networkx as nx

G = nx.DiGraph() # 有向グラフ (Directed Graph)

# 放射状に頂点と辺を追加
nx.add_star(G, [1, 2, 3, 4, 5]) # 1 → 2, 1 → 3, 1 → 4, 1 → 5

nx.draw_networkx(G)

[実行結果]

最初のノードから、2番目以降のノード全てにエッジのある 放射上のグラフ を描画することができました。

NetworkX②(ノード削除、エッジ削除)

ノード削除

今回はグラフからノードを削除してみます。

まずは、前回記事と同様のグラフを描画します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
import networkx as nx

G = nx.DiGraph() # 有向グラフ (Directed Graph)

# 頂点の追加
G.add_node(1)
G.add_nodes_from([3, 4, 5])

# 辺の追加 (頂点も必要に応じて追加されます)
G.add_edge(1, 2)
G.add_edges_from([(1, 3), (2, 5), (3, 4), (4, 5)])

nx.draw_networkx(G)

[実行結果]

上記のグラフから、ノード(頂点)を削除してみます。

[Google Colaboratory]

1
2
3
# 頂点の削除 (削除された頂点に接続されている辺も削除されます)
G.remove_node(5) # 頂点を1つ削除
G.remove_nodes_from([3, 4]) # 頂点を複数削除

[実行結果]

ノードが削除されました。

また削除されたモードに合わせてエッジも削除されていることが確認できます。

エッジ削除

今度はグラフからエッジを削除してみます。

まずグラフ図を描画します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
import networkx as nx

G = nx.DiGraph() # 有向グラフ (Directed Graph)

# 頂点の追加
G.add_node(1)
G.add_nodes_from([3, 4, 5])

# 辺の追加 (頂点も必要に応じて追加されます)
G.add_edge(1, 2)
G.add_edges_from([(1, 3), (2, 5), (3, 4), (4, 5)])

nx.draw_networkx(G)

上記のグラフから、エッジ(辺)を削除してみます。

[実行結果]

[Google Colaboratory]

1
2
3
# # 辺の削除
G.remove_edge(3, 4) # 辺を1つ削除
G.remove_edges_from([(1, 3), (2, 5)]) # 辺を複数削除

[実行結果]

エッジ(辺)が削除されました。

NetworkX ①(グラフ描画)

NetworkX

NetworkX は、グラフ理論ネットワーク理論系の計算 を行うためのPythonパッケージです。

簡単にグラフが定義できたり,図を作ったりできます。

グラフは 頂点(ノード)辺(エッジ) で成り立っています。。

無向グラフ

辺に向きがないグラフを 無向グラフ といいます。

NetworkX を使って 無向グラフ を描いてみます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
import networkx as nx

G = nx.Graph() # 無向グラフ

# 頂点の追加
G.add_node(1)
G.add_nodes_from([3, 4, 5])

# 辺の追加 (頂点も必要に応じて追加されます)
G.add_edge(1, 2)
G.add_edges_from([(1, 3), (2, 5), (3, 4), (4, 5)])

nx.draw_networkx(G)

[実行結果]

有向グラフ

辺に向きがあるグラフを 有効グラフ といいます。

NetworkX を使って 有効グラフ を描いてみます。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
import networkx as nx

G = nx.DiGraph() # 有向グラフ (Directed Graph)

# 頂点の追加
G.add_node(1)
G.add_nodes_from([3, 4, 5])

# 辺の追加 (頂点も必要に応じて追加されます)
G.add_edge(1, 2)
G.add_edges_from([(1, 3), (2, 5), (3, 4), (4, 5)])

nx.draw_networkx(G)

[実行結果]

Union Find木を使って問題を解く②

Union Find木を使って問題を解く②

前前回記事で実装した Union Find木 を使って、下記の問題を解いてみます。

[問題]

N匹の動物がいて、1,2、・・・・Nと番号がつけられています。

動物はすべて3つの種類A,B,Cのいずれかです。

AはBと食べ、BはCを食べ、CはAを食べます。

そして次の2種類の情報が順番に与えられます。

 ・タイプ1:xとyは同じ種類です。

 ・タイプ2:xはyを食べます。

これらの情報はすべて 正しいとは限りません。

以前に与えられた情報と 矛盾する情報 や、x、yが正しい番号でない ような間違った情報が与えられる可能性があります。

与えられた情報のうち、間違った情報の個数 を出力してください。

また間違った情報は捨てると考えます。

[条件]

・動物数Nは、1以上 50000以下

・与えられる情報の数は、0以上 100000以下

[解き方]
Union Find木 は、同じグループを管理するデータ構造ですが、この問題では同じ情報だけではなく 食べる という関係があります。

各動物iについて3つの要素 i-A、i-B、i-C をつくり、3×Nこの要素で Union Find木 を作成し、次のように考えます。

  • i-x は iが種類xである場合 を表す。
  • 各グループは、それらがすべて同時に起こることが分かっている。

例えば、i-Aj-B が同じグループである場合、iが種類Aならばjは必ず種類Bであり、jが種類Bならばiはかならず種類Aであることを表します。

そうするとそれぞれの情報について、以下を行えばよいことになります。

  • タイプ1:xとyが同じ種類の場合
    x-Aとy-Ax-Bとy-Bx-Cとy-Cの3つのペアをグループ化する。
  • タイプ2:xがyを食べる場合
    x-Aとy-Bx-Bとy-Cx-Cとy-Aの3つのペアをグループ化する。

また、それぞれグループ化する前に矛盾がおきているかどうかのチェックを行います。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#-------------入力---------------
n = 100 # 動物の数
# 2種類のタイプ情報、動物1、動物2
info_lst = [{'type':1, 'x':101, 'y':1},
{'type':2, 'x':1, 'y':2},
{'type':2, 'x':2, 'y':3},
{'type':2, 'x':3, 'y':3},
{'type':1, 'x':1, 'y':3},
{'type':2, 'x':3, 'y':1},
{'type':1, 'x':5, 'y':5}]
#--------------------------------
uf = UnionFind(n * 3)
ans = 0

for info in info_lst:
tp = info['type']
x, y = info['x'] - 1, info['y'] - 1 # [1 ~ n ]の範囲を、[0 ~ n - 1 ]の範囲にする

# 動物の番号の範囲をチェック
if x < 0 or x >= n or y < 0 or y >= n:
print('動物番号の範囲チェックエラー', info)
ans += 1
else:
if tp == 1: # 同じ種類の場合
if uf.same(x, y + n) or uf.same(x, y + n * 2):
print('矛盾', info)
ans += 1
else:
print('同じ種類としてグループ化', info)
uf.union(x, y)
uf.union(x + n, y + n)
uf.union(x + n * 2, y + n * 2)
else: # xはyを食べる場合
if uf.same(x, y) or uf.same(x, y + n * 2):
print('矛盾', info)
ans += 1
else:
print('食べられる種類としてグループ化', info)
uf.union(x, y + n)
uf.union(x + n, y + n * 2)
uf.union(x + n * 2, y)

# print(uf.all_group_members())
# for group in uf.all_group_members():
# members = uf.members(group)
# if len(members) > 1:
# print(members)
print()
print('答え', ans)

[実行結果]

動物番号の範囲チェックエラー {'type': 1, 'x': 101, 'y': 1}
食べられる種類としてグループ化 {'type': 2, 'x': 1, 'y': 2}
食べられる種類としてグループ化 {'type': 2, 'x': 2, 'y': 3}
矛盾 {'type': 2, 'x': 3, 'y': 3}
矛盾 {'type': 1, 'x': 1, 'y': 3}
食べられる種類としてグループ化 {'type': 2, 'x': 3, 'y': 1}
同じ種類としてグループ化 {'type': 1, 'x': 5, 'y': 5}

答え 3

1つめの情報は番号として間違っていて、4つめと5つめの情報は矛盾しているので、正解は3ということになります。

Union Find木を使って問題を解く

Union Find木を使って問題を解く

前回記事で実装した Union Find木 を使って、下記の問題を解いてみます。

[問題]

人 1 から 人 N までのN 人の人がいます。

「人Ai と人 Bi は友達である」という情報が M 個与えられます。

同じ情報が複数回与えられることもあります。

X と Y が友達、かつ、Y と Z が友達ならば、X と Z も友達です。

また、M 個の情報から導くことができない友達関係は存在しません。

いじわるな高橋君は、このN人をいくつかのグループに分け、全ての人について「同じグループの中に友達がいない」という状況を作ろうとしています。

最小でいくつのグループに分ければ良いでしょうか?

[条件]

・人数Nは、2以上 200000以下です。

・友達関係の数Mは、0以上 200000以下です。

・AiとBiは、1以上 N以下です。

・AiとBiは、異なる数字です。

[解き方]
Union Find木 を使って友達同士のグループを作ります。

同じグループの中に友達がいない 状況を作るためには、友達の数が一番多いグループの人数分グループ数に分ければよいことになります。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
n = 5
lst = [(1, 2), (3, 4), (5, 1)]
#-----------入力2---------------
# n = 4
# lst = [(1, 2), (2, 1), (1, 2), (2, 1), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
#-----------入力3---------------
#n = 10
#lst = [(3, 1), (4, 1), (5, 9), (2, 6)]
#--------------------------------
uf = UnionFind(n) # 前回記事で実装した「Union Find木」を使う

for l in lst: # 友達関係から友達グループを作成する
uf.union(l[0]-1, l[1]-1)

ans = 0 # 友達がいない状況をつくるためのグループ数
for g in uf.all_group_members().values(): # 友達グループごとにループ
print(f'友達グループ', g) # 友達グループのメンバー
g_size = len(g) # 友達の数
# 友達の数が「友達がいない状況をつくるためのグループ数」より大きかったら
if g_size > ans:
ans = g_size # グループ数に友達数を設定

print('答え =>', ans)

[実行結果(入力1の場合)]

友達グループ [0, 1, 4]
友達グループ [2, 3]
答え => 3

1つ目の友達グループが3人ともっとも人数が多いので、3グループに分ければよいことになります。

例えば{0, 2}{1, 3}{4}と分ければ、だれも友達がいないグループとなります。

[実行結果(入力2の場合)]

友達グループ [0, 1, 2, 3]
答え => 4

4人全員が友達なので、4グループに分けて全員をばらばらにする必要があります。

[実行結果(入力3の場合)]

友達グループ [0, 2, 3]
友達グループ [1, 5]
友達グループ [4, 8]
友達グループ [6]
友達グループ [7]
友達グループ [9]
答え 3

1つめの友達グループが3人ともっとも人数が多いので、3グループに分けます。

友達が2人の場合は、3グループのうちいずれかの2グループに振り分ければよいですし、友達がいない人は、3グループのどこに分けても友達関係はできません。

Union Find木

Union Find木

Union Find木 は、グループ分けを管理することができるデータ構造です。

次のようなことを効率的に行うことができます。

  • 要素aと要素bが同じグループに属しているかどうかをチェックする。
  • 要素aと要素bのグループを一つにまとめる。

グループ同士をまとめることはできますが、要素をグループから除いたり、グループを分けたりすることはできません。

Union Find木 を実現するためのクラスは以下のようになります。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from collections import defaultdict

# n個の要素を0 ~ n - 1のインデックスで管理する
class UnionFind():
def __init__(self, n):
self.n = n # 要素の数
self.parents = [-1] * n # 各要素の親要素の番号を格納するリスト

def find(self, x): # 要素[x]が属するグループの親を返す
if self.parents[x] < 0:
return x
else:
self.parents[x] = self.find(self.parents[x])
return self.parents[x]

def union(self, x, y): # 要素[x]が属するグループと要素yが属するグループとを併合
x = self.find(x)
y = self.find(y)
if x == y:
return
if self.parents[x] > self.parents[y]:
x, y = y, x
self.parents[x] += self.parents[y]
self.parents[y] = x

def size(self, x): # 要素[x]が属するグループのサイズ(要素数)を返す
return -self.parents[self.find(x)]

def same(self, x, y): # 要素[x],要素[y]が同じグループに属するかどうかを返す
return self.find(x) == self.find(y)

def members(self, x): # 要素[x]が属するグループに属する要素をリストで返す
root = self.find(x)
return [i for i in range(self.n) if self.find(i) == root]

def roots(self): # すべての親の要素をリストで返す
return [i for i, x in enumerate(self.parents) if x < 0]

def group_count(self): # グループの数を返す
return len(self.roots())

def all_group_members(self): # 全グループの全要素を返す
group_members = defaultdict(list)
for member in range(self.n):
group_members[self.find(member)].append(member)
return group_members

def __str__(self): # printでの表示用。ルート要素: [そのグループに含まれる要素のリスト]を文字列で返す
return '\n'.join(f'{r}: {m}' for r, m in self.all_group_members().items())

動作確認

実装した Union Find木 の動作を確認してみます。

要素数5個(0,1,2,3,4)のUnion Find木を定義し、[0,1]のグループと[2,3,4]のグループに分けます。

[Google Colaboratory]

1
2
3
4
5
6
uf = UnionFind(5)
uf.union(0, 1)
uf.union(2, 3)
uf.union(3, 4)

print(uf)

[実行結果]

0: [0, 1]

2: [2, 3, 4]

2つのグループに分けることができました。

親の要素(一番もとになる要素)がグループのキーとなっていることが確認できます。

次に、要素1と要素3が所属するグループを調べてみます。

[Google Colaboratory]

1
2
print(uf.find(1))
print(uf.find(3))

[実行結果]

0

2

要素1がグループ0に所属し、要素3がグループ2に所属していることが分かりました。

今度は、要素0,要素2が同じグループに属するかどうかと、要素3,要素4が同じグループに属するかどうかを調べます。

[Google Colaboratory]

1
2
print(uf.same(0, 2))
print(uf.same(3, 4))

[実行結果]

False

True

要素0,要素2は違うグループであり、要素3,要素4は同じグループに属することを確認できました。

最後に、要素3が所属するグループの全要素を表示します。

[Google Colaboratory]

1
print(uf.members(3))

[実行結果]

[2, 3, 4]

要素3が所属するグループの全要素は、2,3,4であることが分かりました。

次回は、Union Find木 を使って問題を解いてみます。

© 2026 Playing with Python All Rights Reserved.
Theme by hipaper