来自无向图的最小ID

时间:2015-08-05 05:50:08

标签: oracle graph bidirectional

使用Oracle 11g,我试图在ID对的无向图中找到最小的ID。

使用以下对:

create table unique_pairs ( ID1 INT, ID2 INT );
insert all 
    into unique_pairs (ID1,ID2) VALUES ( 1, 2 )  
    into unique_pairs (ID1,ID2) VALUES ( 1, 3 )
    into unique_pairs (ID1,ID2) VALUES ( 4, 2 )
    into unique_pairs (ID1,ID2) VALUES ( 4, 5 )
    into unique_pairs (ID1,ID2) VALUES ( 7, 6 )
    into unique_pairs (ID1,ID2) VALUES ( 8, 10 )
    into unique_pairs (ID1,ID2) VALUES ( 10, 7 )
select * from dual;
commit;

我正在使用两个通过查询连接的联合来尝试在地图的两个方向上旅行:

select distinct a.ID1, a.ID2, min(a.ultimate_parent_id ) as ultimate_parent_id
from
(SELECT distinct ID1, ID2,
    connect_by_root(ID2) ultimate_parent_id
  FROM unique_pairs
CONNECT BY ID2 = prior ID1 AND ID2 != PRIOR ID2 
union
SELECT distinct ID1, ID2,
    connect_by_root(ID1) ultimate_parent_id
  FROM unique_pairs
CONNECT BY ID1 = prior ID2 AND ID1 != PRIOR ID1) a
group by a.ID1, a.ID2
order by 3;

我明白了:

   ID1        ID2 ULTIMATE_PARENT_ID
     1          2                  1
     1          3                  1
     4          2                  2
     4          5                  4
    10          7                  6
     7          6                  6
     8         10                  6

应该是这样的:

   ID1        ID2 ULTIMATE_PARENT_ID
     1          2                  1
     1          3                  1
     4          2                  1
     4          5                  1
    10          7                  6
     7          6                  6
     8         10                  6 

我认为我的问题涉及当我的数据是非定向时,按功能连接意味着层次结构。任何帮助都会很棒,谢谢!

2 个答案:

答案 0 :(得分:2)

Collapsar有一个非常冗长的描述,但这是一个非常简短的解决方案,符合您的要求。我离图形理论的日子太过分了,不像他那样解释它,但简单的解释是,由于你的unique_pairs代表无向图中的边,它可以在有向图中表示为ID1的并集, ID2与ID2,ID1因此是我的第一个公用表表达式。使用该有向图,您可以像我的第二个公用表表达式一样计算所有连接的节点对。最后,您可以将原始unique_pairs表连接到ID1上的已连接对CTE,并将最小根节点作为ULTIMATE_PARENT_ID:

with dg1 as (
  select id1, id2 from unique_pairs
  union all
  select id2, id1 from unique_pairs
), cp as (
select id1
     , CONNECT_BY_ROOT id1 root
  from dg1
connect by nocycle prior id1 = id2 
)
select dg1.*
     , min(root) ULTIMATE_PARENT_ID
  from unique_pairs dg1
  join cp
    on cp.id1 = dg1.id1
  group by dg1.id1, dg1.id2
order by 1,2

请参阅SQL Fiddle

| ID1 | ID2 | ULTIMATE_PARENT_ID |
|-----|-----|--------------------|
|   1 |   2 |                  1 |
|   1 |   3 |                  1 |
|   4 |   2 |                  1 |
|   4 |   5 |                  1 |
|   7 |   6 |                  6 |
|   8 |  10 |                  6 |
|  10 |   7 |                  6 |

答案 1 :(得分:1)

<强>概念

您的问题基本上由3个步骤组成:

  • 识别无向图G
  • 的连接组件
  • 用顶点用最小id
  • 表示每个连通分量
  • 将图表的每个边缘映射到它所属的连接组件的代表。

任务通过以下方式解决:

  • 考虑等同的&#39;基于D_1顶点id的自然数字排序的有向图G
  • 通过抽象D_1所代表的排序中的链来将B缩减为包含所有候选ID的二分图D_1
  • 确定B中的最小代表ID。
  • 建立从G的边缘到B边缘的映射,从而建立其弱连接组件(WCC)。

<强>详情

G的边集恰好是(id1, id2)记录给出的对unique_pairs的集合。 G的顶点集与id的{​​{1}}列unique_pairs列的值集匹配。由于G是无向的,因此是否通过记录(id1, id2)(id2, id1)表示边缘并不重要;从此我们假设id1 < id2(id1, id2)中的每条记录unique_pairs保留了wlog。

我们通过根据从D_1导出的有向图G构建辅助结构,通过根据其入射顶点的id的数字顺序定向无向图的每个边来解决任务。让D_2D_1,使所有边缘方向反转。识别原始图表的连接组件相当于在D_1D_2中识别WCC。通过构造观察D_1是非循环的。

辅助辅助结构是G中的一组边缘映射到D_1中最大链的幂集(=未包含在另一个路径中的路径),以便所有链中的边e的图像包含e并最大化其终端的数字id(=开始和结束顶点)之间的差异。 powersets的使用是技术性的,否则当两个最大链共享相同的终端但遵循“替代路由”时,映射可能不是很明确。 (通常图像将包含单个元素)。

在下一步中,我们粗化映射:代替最大链组,图像将是原始映射图像中任何最大链的终端ID对。

鉴于此映射,我们构建了辅助二分图B,其边缘精确地表示有序的终端ID对。该图是二分的,因为最大链的末端顶点不能是另一个最大链的起始顶点,反之亦然 - 如果发生这种情况,则所述链不会是最大的。

但是,通过构造,B构成了D_1传递闭包的一部分。因此,B的WCC的顶点集是D_1的WCC的顶点集的子集。我们最终对后面每个WCC中具有最小id的顶点感兴趣。由于最大链中包含的终端id不能小于起始顶点id,因此所有这些顶点必须包含在B的顶点集中。因此,我们必须考虑B的WCC,只考虑每个WCC的最小终端ID。连同所有G边缘到B边缘的辅助映射,以及B的WCC,我们解决了原始问题。

SQL实施

在预处理步骤中,G的边缘被归一化为其入射顶点的自然排序。假设边的方向为id1 -> id2test_unique_pairs也代表D_1

update test_unique_pairs
   set id1 = id2
     , id2 = id1
 where id1 > id2
     ;

第一个查询将D_1的边缘映射到B的边缘,即。 D_1中最大链的终端id对。这些链是非构造的非循环,将通过test_unique_pairs上的分层查询计算,它们将由它们的起点和终点顶点表示,这些对也表示B中的边。这种映射以共模两个终端的模数替代链发生 - 如果它们共享两个终端,则认为不同的最大路径是等效的。 oracle中的分层查询允许轻松提取最大元素。然而,最小元素是层次结构的最大元素,其中父子关系被反转(为了满足这种计算需要考虑两个有向图D_1D_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中所有其他边的根路径。因此,在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 != chainend ) OR ( prior chainstart != chainstart and prior chainend = chainend ) )
                                 ) base
                          group by grouping sets ( ( base.id1, base.id2 ) )
                        )
               order by id1
                      , id2
     ;