概念
你的问题基本上包括以下3个步骤:
- 识别无向图
G
的连通分量
- 用最小 id 的顶点表示每个连通分量
- 将图的每条边映射到它所属的连通分量的代表。
该任务通过以下方式解决:
- 考虑基于
G
的顶点 id 的自然数序列的 '等效' 有向图 D_1
。
- 通过抽象掉由
D_1
表示的排序中的链,将 D_1
缩减为包含所有候选 id 的二分图 B
。
- 确定
B
中的最小代表 id。
- 建立从
G
的边到 B
的边及其弱连通分量 (WCCs) 的映射。
详细信息
G
的边集恰好由
unique_pairs
的记录给出的一组对
(id1, id2)
组成。
G
的顶点集与
unique_pairs
的两个
id
列的值的集合相匹配。由于
G
是无向的,因此无论使用记录
(id1,id2)
或
(id2,id1)
表示边都没有关系;因此我们假设对于
unique_pairs
中的每个记录
(id1,id2)
,都有
id1 < id2
成立。
我们通过构建一个基于有向图D_1
的辅助结构来完成任务,该有向图源自于根据其关联顶点的id的数值顺序定向每条无向图的边。让D_2
成为所有边方向相反的D_1
。识别原始图的连通分量相当于在D_1
或D_2
中识别WCCs。注意,按照构造,D_1
是无环的。
辅助辅助结构是将G中的边集映射到D_1中的极大链幂集(=不包含在另一条路径中的路径)上,使图像中每个边e的所有链都包含e并最大化其终端(=起点和终点顶点)的数字ID之间的差异。使用幂集是为了避免当两个极大链共享相同的端点但遵循“替代路线”时,映射可能不是明确定义的情况(通常,图像将只包含单个元素)。
接下来,我们粗略地映射:图像将是原始映射图像中任何极大链的终端ID对。
给定此映射,我们建立一个辅助二分图B,其边缘恰好表示终端ID的有序对。该图是二分的,因为最大链的任何结束顶点都不能是另一个最大链的起始顶点,反之亦然 - 如果发生这种情况,则这些链将不是最大的。
然而,根据构造,
B
是
D_1
的传递闭包的一部分。因此,
B
的WCC的顶点集是
D_1
的WCC的顶点集的子集。我们最终感兴趣的是每个后者WCC中具有最小id的顶点。由于不包含在最大链中的任何终端id都不能小于起始顶点id,所有这些顶点必须包含在
B
的顶点集中。因此,我们只考虑
B
的 WCC,并且对它们的每个最小终端ID 进行取值。通过将所有
G
边缘映射到
B
边缘,从而映射到
B
的 WCC,我们解决了原始问题。
SQL实现
在预处理步骤中,将 G
的边规范化为其关联顶点的自然排序。假设边缘的方向为 id1 -> id2
,test_unique_pairs
也表示 D_1
。
update test_unique_pairs
set id1 = id2
, id2 = id1
where id1 > id2
;
第一个查询将
D_1
的边缘映射到
B
的边缘,即在
D_1
中最大链的终端ID对应于
B
中的边缘。 这些链由关于
test_unique_pairs
的分层查询计算,并且将由其起始和结束顶点表示,这些对也表示
B
中的边缘。 如果它们共享两个终端,则此映射发生在替代链模数上 - 如果它们共享两个终端,则认为不同的最大路径等效。 Oracle中的分层查询允许轻松提取最大元素。 然而,最小元素是具有父子关系反转的层次结构的最大元素(考虑到这个计算是考虑到两个有向图
D_1
,
D_2
)。 为了简化第二个处理阶段,查询ID被包装到视图定义中:
create or replace view test_up_chains as
select *
from ( -- collect minimal/maximal reachable id for each edge in G
select distinct
inter.id1
, inter.id2
, min(minelt) chainstart
, max(maxelt) chainend
from ( -- collect minimum/maximum terminal id for chains starting/ending with any of G's edges
select distinct
inter_min.id1
, inter_min.id2
, inter_min.minelt
, inter_max.maxelt
from ( -- hierarchical query for maximal chains in D_1
select distinct
hup.ID1
, hup.ID2
, CONNECT_BY_ROOT hup.id2 maxelt
from test_unique_pairs hup
connect by nocycle prior hup.id1 = hup.id2
start with hup.id2 in (
select ref1.id2
from test_unique_pairs ref1
where not exists (
select 1
from test_unique_pairs ref2
where ref2.id1 = ref1.id2
)
)
) inter_max
join ( -- hierarchical query for maximal chains in D_2 ( = D_1 with reversed edge orientations )
select distinct
hup.ID1
, hup.ID2
, CONNECT_BY_ROOT hup.id1 minelt
from test_unique_pairs hup
connect by nocycle prior hup.id2 = hup.id1
start with hup.id1 in (
select ref1.id1
from test_unique_pairs ref1
where not exists (
select 1
from test_unique_pairs ref2
where ref2.id2 = ref1.id1
)
)
) inter_min
on ( inter_min.id1 = inter_max.id1 AND inter_min.id2 = inter_max.id2 )
) inter
group by grouping sets ( (inter.id1, inter.id2) )
) base
order by base.id1
, base.id2
;
接下来,将 B
的边缘合并成 WCC,并提取它们各自的最小 ID。下面的分层查询由于缺少 STARTS WITH
子句,基于边缘关系构建完整的可传递闭包,并为 B
中的每个边缘生成到同一 WCC 中所有其他边缘的根路径。因此,在所有层次结构的 'root' 中最小化给定 G
中的一条边的起始顶点,可以找到在 G
中达到的最小 ID。通过 NOCYCLE
指令压制循环。
select *
from (
select distinct
id1
, id2
, min(chainlim) chainrep
from (
select distinct
id1
, id2
, connect_by_root chainstart chainlim
from test_up_chains
connect by nocycle ( ( prior chainstart = chainstart and prior chainend
) base
group by grouping sets ( ( base.id1, base.id2 ) )
)
order by id1
, id2
;