igraph 参考手册

用于使用 igraph C 库

搜索手册

第 28 章 图运算符

1. 并集和交集

1.1. igraph_disjoint_union — 创建两个不相交图的并集。

igraph_error_t igraph_disjoint_union(igraph_t *res,
                                     const igraph_t *left,
                                     const igraph_t *right);

首先,第二个图的顶点将使用新的顶点 ID 重新标记,以形成两个不相交的顶点 ID 集合,然后形成两个图的并集。如果两个图分别具有 |V1| 和 |V2| 个顶点以及 |E1| 和 |E2| 条边,则新图将具有 |V1|+|V2| 个顶点和 |E1|+|E2| 条边。

图的顶点和边的顺序将被保留。换句话说,第一个图的顶点和边的 ID 映射到新图中的相同值,而第二个图的顶点和边的 ID 映射到递增第一个图的顶点和边的计数后的 ID。

两个图需要具有相同的方向性,即都是有向图或都是无向图。

此函数的当前版本无法处理图、顶点和边的属性,它们将丢失。

参数: 

res:

指向未初始化的图对象的指针,结果将存储在此处。

:

第一个图。

:

第二个图。

返回值: 

错误代码。

另请参阅: 

igraph_disjoint_union_many() 用于创建多个图的不相交并集,igraph_union() 用于非不相交并集。

时间复杂度:O(|V1|+|V2|+|E1|+|E2|)。

示例 28.1.  文件 examples/simple/igraph_disjoint_union.c

#include <igraph.h>
#include <stdio.h>

int main(void) {
    igraph_t left, right, uni;
    igraph_vector_ptr_t glist;
    igraph_integer_t i, n;

    igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,3, -1);
    igraph_small(&right, 5, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,4, -1);

    igraph_disjoint_union(&uni, &left, &right);
    igraph_write_graph_edgelist(&uni, stdout);
    printf("\n");

    igraph_destroy(&left);
    igraph_destroy(&right);
    igraph_destroy(&uni);

    /* Empty graph list; the result is the directed null graph. */
    igraph_vector_ptr_init(&glist, 0);
    igraph_disjoint_union_many(&uni, &glist);
    if (!igraph_is_directed(&uni) || igraph_vcount(&uni) != 0) {
        return 1;
    }
    igraph_vector_ptr_destroy(&glist);
    igraph_destroy(&uni);

    /* Non-empty graph list. */
    igraph_vector_ptr_init(&glist, 10);
    n = igraph_vector_ptr_size(&glist);
    for (i = 0; i < n; i++) {
        VECTOR(glist)[i] = calloc(1, sizeof(igraph_t));
        igraph_small(VECTOR(glist)[i], 2, IGRAPH_DIRECTED, 0,1, 1,0, -1);
    }
    if (!igraph_is_directed(&uni)) {
        return 2;
    }

    igraph_disjoint_union_many(&uni, &glist);
    igraph_write_graph_edgelist(&uni, stdout);
    printf("\n");

    /* Destroy and free the graph list. */
    n = igraph_vector_ptr_size(&glist);
    for (i = 0; i < n; i++) {
        igraph_destroy(VECTOR(glist)[i]);
        free(VECTOR(glist)[i]);
    }
    igraph_vector_ptr_destroy(&glist);
    igraph_destroy(&uni);

    return 0;
}


1.2. igraph_disjoint_union_many — 创建多个图的不相交并集。

igraph_error_t igraph_disjoint_union_many(igraph_t *res,
                                          const igraph_vector_ptr_t *graphs);

首先,图中的顶点将使用新的顶点 ID 重新标记,以具有成对不相交的顶点 ID 集合,然后形成图的并集。结果中的顶点和边的数量是图中顶点和边的总数。

输入图的顶点和边的顺序在输出图中保留。

所有图需要具有相同的方向性,即全部有向或全部无向。如果图列表的长度为零,则结果将是一个没有顶点的有向图。

此函数的当前版本无法处理图、顶点和边的属性,它们将丢失。

参数: 

res:

指向未初始化的图对象的指针,操作的结果将存储在此处。

graphs:

指针向量,包含指向已初始化的图对象的指针。

返回值: 

错误代码。

另请参阅: 

igraph_disjoint_union() 如果您只有两个图,则可以使用更简单的语法,igraph_union_many() 用于非不相交并集。

时间复杂度:O(|V|+|E|),结果中顶点数加上边数。

1.3. igraph_join — 创建两个不相交图的连接。

igraph_error_t igraph_join(igraph_t *res,
                           const igraph_t *left,
                           const igraph_t *right);

警告

此函数是实验性的,其签名尚未被视为最终签名。我们保留更改函数签名的权利,而无需更改 igraph 的主要版本。使用它需要您自担风险。

首先,第二个图的顶点将使用新的顶点 ID 重新标记,以具有两个不相交的顶点 ID 集合,然后形成两个图的并集。最后,第一个图的顶点将添加与第二个图的每个顶点的边。如果两个图分别具有 |V1| 和 |V2| 个顶点以及 |E1| 和 |E2| 条边,则新图将具有 |V1|+|V2| 个顶点和 |E1|+|E2|+|V1|*|V2| 条边。

图的顶点顺序将被保留。换句话说,第一个图的顶点 ID 映射到新图中的相同值,而第二个图的顶点 ID 映射到递增第一个图的顶点计数后的 ID。新边将与共享来自顶点的其他边分组。

两个图需要具有相同的方向性,即都是有向图或都是无向图。如果两个图都是有向图,则对于图 G1、G2 中的每个顶点 v、u,我们添加边 (v, u), (u, v) 以保持完整性。

此函数的当前版本无法处理图、顶点和边的属性,它们将丢失。

参数: 

res:

指向未初始化的图对象的指针,结果将存储在此处。

:

第一个图。

:

第二个图。

返回值: 

错误代码。

时间复杂度:O(|V1|*|V2|+|E1|+|E2|)。

1.4. igraph_union — 计算两个图的并集。

igraph_error_t igraph_union(
        igraph_t *res,
        const igraph_t *left, const igraph_t *right,
        igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2);

结果中的顶点数是两个参数图中较大图的顶点数。结果图包含至少在一个操作数图中存在的边。

操作数图的方向性必须相同。

边的多重性通过取输入图中两个多重性中的较大值来处理。换句话说,如果第一个图在顶点对 (u, v) 之间有 N 条边,第二个图有 M 条边,则结果图将在它们之间具有 max(N, M) 条边。

参数: 

res:

指向未初始化的图对象的指针,结果将存储在此处。

:

第一个图。

:

第二个图。

edge_map1:

指向已初始化的向量或空指针的指针。如果不是空指针,它将包含从第一个参数图(left)的边到结果图的边的映射。

edge_map2:

edge_map1 相同,但用于第二个图 right

返回值: 

错误代码。

另请参阅: 

igraph_union_many() 用于多个图的并集,igraph_intersection()igraph_difference() 用于其他运算符。

时间复杂度:O(|V|+|E|),|V| 是顶点数,|E| 是结果图中的边数。

示例 28.2.  文件 examples/simple/igraph_union.c

#include <igraph.h>
#include <stdio.h>

int main(void) {
    igraph_t left, right, uni;
    igraph_vector_int_t edge_map1, edge_map2;

    igraph_vector_int_init(&edge_map1, 0);
    igraph_vector_int_init(&edge_map2, 0);

    igraph_small(&left, 4, IGRAPH_DIRECTED,
                 0,1, 1,2, 2,2, 2,3, 2,3, 3,2,
                 -1);

    igraph_small(&right, 6, IGRAPH_DIRECTED,
                 0,1, 1,2, 2,2, 2,3, 5,2,
                 -1);

    igraph_union(&uni, &left, &right, &edge_map1, &edge_map2);
    igraph_write_graph_edgelist(&uni, stdout);
    igraph_vector_int_print(&edge_map1);
    igraph_vector_int_print(&edge_map2);

    igraph_destroy(&uni);
    igraph_destroy(&left);
    igraph_destroy(&right);

    igraph_vector_int_destroy(&edge_map2);
    igraph_vector_int_destroy(&edge_map1);

    return 0;
}


1.5. igraph_union_many — 创建多个图的并集。

igraph_error_t igraph_union_many(
    igraph_t *res, const igraph_vector_ptr_t *graphs,
    igraph_vector_int_list_t *edgemaps
);

结果图将包含与参数中最大的图一样多的顶点,如果一条边至少是一个操作数图的一部分,则它将包含在结果图中。

结果图中的顶点数将是参数图中最大顶点数。

参数图的方向性必须相同。如果图列表的长度为零,则结果将是一个没有顶点的有向图。

边的多重性通过取输入图中同一顶点对 (u, v) 的所有多重性的最大值来处理;这将是结果图中 (u, v) 的多重性。

参数: 

res:

指向未初始化的图对象的指针,这将包含结果。

graphs:

指针向量,包含指向并集运算符的操作数的指针,当然是图对象。

edgemaps:

如果不是空指针,则它必须是已初始化的整数向量列表,并且从图到结果图的边的映射将存储在此处,顺序与 graphs 相同。每个映射都存储在一个单独的 igraph_vector_int_t 对象中。

返回值: 

错误代码。

另请参阅: 

igraph_union() 用于两个图的并集,igraph_intersection_many()igraph_intersection()igraph_difference 用于其他运算符。

时间复杂度:O(|V|+|E|),|V| 是最大图中的顶点数,|E| 是结果图中的边数。

1.6. igraph_intersection — 收集两个图的公共边。

igraph_error_t igraph_intersection(
        igraph_t *res,
        const igraph_t *left, const igraph_t *right,
        igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2);

结果图仅包含第一个图和第二个图中都存在的边。结果图中的顶点数与两个参数中较大图的顶点数相同。

操作数图的方向性必须相同。

边的多重性通过取输入图中两个多重性中的较小值来处理。换句话说,如果第一个图在顶点对 (u, v) 之间有 N 条边,第二个图有 M 条边,则结果图将在它们之间具有 min(N, M) 条边。

参数: 

res:

指向未初始化的图对象的指针。这将包含操作的结果。

:

第一个操作数,一个图对象。

:

第二个操作数,一个图对象。

edge_map1:

空指针,或已初始化的向量。如果是后者,则从结果图的边到 left 输入图的边的映射存储在此处。对于不在交集中的边,存储 -1。

edge_map2:

空指针,或已初始化的向量。与 edge_map1 相同,但用于 right 输入图。对于不在交集中的边,存储 -1。

返回值: 

错误代码。

另请参阅: 

igraph_intersection_many() 一次计算多个图的交集,igraph_union()igraph_difference() 用于其他运算符。

时间复杂度:O(|V|+|E|),|V| 是节点数,|E| 是两个图中较小图中的边数。(包含较少顶点的图被认为是较小的。)

示例 28.3.  文件 examples/simple/igraph_intersection.c

#include <igraph.h>

void print_vector(igraph_vector_t *v) {
    igraph_integer_t i, l = igraph_vector_size(v);
    for (i = 0; i < l; i++) {
        printf(" %" IGRAPH_PRId "", (igraph_integer_t) VECTOR(*v)[i]);
    }
    printf("\n");
}

int main(void) {

    igraph_t left, right, isec;
    igraph_vector_int_t v;
    igraph_vector_ptr_t glist;
    igraph_t g1, g2, g3;
    igraph_vector_int_t edge_map1, edge_map2;

    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1);
    igraph_create(&left, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 1, 0, 5, 4, 1, 2, 3, 2, -1);
    igraph_create(&right, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init(&edge_map1, 0);
    igraph_vector_int_init(&edge_map2, 0);

    igraph_intersection(&isec, &left, &right, &edge_map1, &edge_map2);
    igraph_vector_int_init(&v, 0);
    igraph_get_edgelist(&isec, &v, 0);
    printf("---\n");
    igraph_vector_int_print(&v);
    igraph_vector_int_print(&edge_map1);
    igraph_vector_int_print(&edge_map2);
    printf("---\n");
    igraph_vector_int_destroy(&v);
    igraph_destroy(&left);
    igraph_destroy(&right);
    igraph_destroy(&isec);
    igraph_vector_int_destroy(&edge_map1);
    igraph_vector_int_destroy(&edge_map2);

    /* empty graph list */
    igraph_vector_ptr_init(&glist, 0);
    igraph_intersection_many(&isec, &glist, 0);
    if (igraph_vcount(&isec) != 0 || !igraph_is_directed(&isec)) {
        return 1;
    }
    igraph_destroy(&isec);
    igraph_vector_ptr_destroy(&glist);

    /* graph list with an empty graph */
    igraph_vector_ptr_init(&glist, 3);
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1);
    igraph_create(&g1, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1);
    igraph_create(&g2, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);
    igraph_empty(&g3, 10, IGRAPH_DIRECTED);

    VECTOR(glist)[0] = &g1;
    VECTOR(glist)[1] = &g2;
    VECTOR(glist)[2] = &g3;
    igraph_intersection_many(&isec, &glist, 0);
    if (igraph_ecount(&isec) != 0 || igraph_vcount(&isec) != 10) {
        return 2;
    }
    igraph_destroy(&g1);
    igraph_destroy(&g2);
    igraph_destroy(&g3);
    igraph_destroy(&isec);
    igraph_vector_ptr_destroy(&glist);

    /* "proper" graph list */
    igraph_vector_ptr_init(&glist, 3);
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, -1);
    igraph_create(&g1, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, 3, 2, 4, 5, 6, 5, -1);
    igraph_create(&g2, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);
    igraph_vector_int_init_int_end(&v, -1, 2, 3, 1, 0, 1, 2, 3, 2, 4, 5, 6, 5, 2, 3, -1);
    igraph_create(&g3, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    VECTOR(glist)[0] = &g1;
    VECTOR(glist)[1] = &g2;
    VECTOR(glist)[2] = &g3;
    igraph_intersection_many(&isec, &glist, 0);
    igraph_write_graph_edgelist(&isec, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);
    igraph_destroy(&g3);
    igraph_destroy(&isec);
    igraph_vector_ptr_destroy(&glist);

    return 0;
}


1.7. igraph_intersection_many — 计算多个图的交集。

igraph_error_t igraph_intersection_many(
    igraph_t *res, const igraph_vector_ptr_t *graphs,
    igraph_vector_int_list_t *edgemaps
);

此函数计算存储在 graphs 参数中的图的交集。只有 graphs 中每个图的一部分的边将包含在结果图中。

结果图中的顶点数将是参数图中最大顶点数。

参数图的方向性必须相同。如果图列表的长度为零,则结果将是一个没有顶点的有向图。

边的多重性通过取输入图中同一顶点对 (u, v) 的所有多重性的最小值来处理;这将是结果图中 (u, v) 的多重性。

参数: 

res:

指向未初始化的图对象的指针,操作的结果将存储在此处。

graphs:

指针向量,包含指向图对象的指针,交集运算符的操作数。

edgemaps:

如果不是空指针,则它必须是已初始化的整数向量列表,并且从图到结果图的边的映射将存储在此处,顺序与 graphs 相同。每个映射都存储在一个单独的 igraph_vector_int_t 对象中。对于不在交集中的边,存储 -1。

返回值: 

错误代码。

另请参阅: 

igraph_intersection() 用于两个图的交集,igraph_union_many()igraph_union()igraph_difference() 用于其他运算符。

时间复杂度:O(|V|+|E|),|V| 是顶点数,|E| 是最小图(即具有较少顶点的图)中的边数。

2. 其他集合类运算符

2.1. igraph_difference — 计算两个图的差集。

igraph_error_t igraph_difference(igraph_t *res,
                      const igraph_t *orig, const igraph_t *sub);

结果中的顶点数是原始图,即左侧第一个操作数中的顶点数。在结果图中,只有 orig 中的边将包含在 sub 中不存在。

参数: 

res:

指向未初始化的图对象的指针,结果将存储在此处。

orig:

运算符的左操作数,一个图对象。

sub:

运算符的右操作数,一个图对象。

返回值: 

错误代码。

另请参阅: 

igraph_intersection()igraph_union() 用于其他运算符。

时间复杂度:O(|V|+|E|),|V| 是较小图中的顶点数,|E| 是结果图中的边数。

示例 28.4.  文件 examples/simple/igraph_difference.c

#include <igraph.h>

int main(void) {

    igraph_t orig, sub, diff;
    igraph_vector_int_t v;

    /* Subtract from itself */
    printf("subtract itself\n");
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, -1);
    igraph_create(&orig, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_difference(&diff, &orig, &orig);
    igraph_write_graph_edgelist(&diff, stdout);
    if (igraph_ecount(&diff) != 0 ||
        igraph_vcount(&diff) != igraph_vcount(&orig)) {
        return 1;
    }

    igraph_destroy(&orig);
    igraph_destroy(&diff);

    /* Same for undirected graph */
    printf("subtract itself, undirected\n");
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, -1);
    igraph_create(&orig, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 1, 0, 1, 2, 2, 1, 4, 5, -1);
    igraph_create(&sub, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_difference(&diff, &orig, &sub);
    igraph_write_graph_edgelist(&diff, stdout);
    if (igraph_ecount(&diff) != 0 ||
        igraph_vcount(&diff) != igraph_vcount(&orig)) {
        return 2;
    }

    igraph_destroy(&orig);
    igraph_destroy(&sub);
    igraph_destroy(&diff);

    /* Subtract the empty graph */
    printf("subtract empty\n");
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, -1);
    igraph_create(&orig, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_empty(&sub, 3, IGRAPH_DIRECTED);
    igraph_difference(&diff, &orig, &sub);
    igraph_write_graph_edgelist(&diff, stdout);
    if (igraph_ecount(&diff) != igraph_ecount(&orig) ||
        igraph_vcount(&diff) != igraph_vcount(&orig)) {
        return 3;
    }

    igraph_destroy(&orig);
    igraph_destroy(&sub);
    igraph_destroy(&diff);

    /* A `real' example */
    printf("real example\n");
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, 8, 9, -1);
    igraph_create(&orig, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 0, 1, 5, 4, 2, 1, 6, 7, -1);
    igraph_create(&sub, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_difference(&diff, &orig, &sub);
    igraph_write_graph_edgelist(&diff, stdout);

    igraph_destroy(&diff);
    igraph_destroy(&orig);
    igraph_destroy(&sub);

    /* undirected version */
    printf("real example, undirected\n");
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 1, 4, 5, 8, 9, 8, 10, 8, 13, 8, 11, 8, 12, -1);
    igraph_create(&orig, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 0, 1, 5, 4, 2, 1, 6, 7, 8, 10, 8, 13, -1);
    igraph_create(&sub, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_difference(&diff, &orig, &sub);
    igraph_write_graph_edgelist(&diff, stdout);

    igraph_destroy(&diff);
    igraph_destroy(&orig);
    igraph_destroy(&sub);

    /* undirected version with loop edge, tests Github issue #597 */
    printf("Github issue #597, undirected\n");
    igraph_vector_int_init_int_end(&v, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 0, -1);
    igraph_create(&orig, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 0, -1);
    igraph_create(&sub, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_difference(&diff, &orig, &sub);
    igraph_write_graph_edgelist(&diff, stdout);

    igraph_destroy(&diff);
    igraph_destroy(&orig);
    igraph_destroy(&sub);

    return 0;
}


2.2. igraph_complementer — 创建图的补图。

igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph,
                        igraph_bool_t loops);

补图意味着所有不属于原始图的边都将包含在结果中。

参数: 

res:

指向未初始化的图对象的指针。

:

原始图。

循环:

是否将环边添加到补图中。

返回值: 

错误代码。

另请参阅: 

时间复杂度:O(|V|+|E1|+|E2|),|V| 是图中的顶点数,|E1| 是原始图中的边数,|E2| 是补图中的边数。

示例 28.5.  文件 examples/simple/igraph_complementer.c

#include <igraph.h>

int main(void) {

    igraph_t g1, g2;

    /* complementer of the empty graph */
    igraph_empty(&g1, 5, IGRAPH_DIRECTED);
    igraph_complementer(&g2, &g1, IGRAPH_LOOPS);
    igraph_write_graph_edgelist(&g2, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /* the same without loops */
    igraph_empty(&g1, 5, IGRAPH_DIRECTED);
    igraph_complementer(&g2, &g1, IGRAPH_NO_LOOPS);
    igraph_write_graph_edgelist(&g2, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /* complementer of the full graph */
    igraph_full(&g1, 5, IGRAPH_DIRECTED, IGRAPH_LOOPS);
    igraph_complementer(&g2, &g1, IGRAPH_LOOPS);
    if (igraph_ecount(&g2) != 0) {
        return 1;
    }
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /* complementer of the full graph, results loops only */
    igraph_full(&g1, 5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS);
    igraph_complementer(&g2, &g1, IGRAPH_LOOPS);
    igraph_write_graph_edgelist(&g2, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /**************
     * undirected *
     *************/

    /* complementer of the empty graph */
    igraph_empty(&g1, 5, IGRAPH_UNDIRECTED);
    igraph_complementer(&g2, &g1, IGRAPH_LOOPS);
    igraph_write_graph_edgelist(&g2, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /* the same without loops */
    igraph_empty(&g1, 5, IGRAPH_UNDIRECTED);
    igraph_complementer(&g2, &g1, IGRAPH_NO_LOOPS);
    igraph_write_graph_edgelist(&g2, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /* complementer of the full graph */
    igraph_full(&g1, 5, IGRAPH_UNDIRECTED, IGRAPH_LOOPS);
    igraph_complementer(&g2, &g1, IGRAPH_LOOPS);
    if (igraph_ecount(&g2) != 0) {
        return 1;
    }
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    printf("---\n");

    /* complementer of the full graph, results loops only */
    igraph_full(&g1, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS);
    igraph_complementer(&g2, &g1, IGRAPH_LOOPS);
    igraph_write_graph_edgelist(&g2, stdout);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    return 0;
}


2.3. igraph_compose — 计算两个图的组合。

igraph_error_t igraph_compose(igraph_t *res,
                              const igraph_t *g1,
                              const igraph_t *g2,
                              igraph_vector_int_t *edge_map1,
                              igraph_vector_int_t *edge_map2);

图的组合包含与两个操作数中较大的图相同数量的顶点。当且仅当存在一个顶点 k,使得第一个图包含一条 (i,k) 边,第二个图包含一条 (k,j) 边时,它才包含一条 (i,j) 边。

当然,这正是两个二元关系的组合。

两个图必须具有相同的方向性,否则该函数将返回错误。请注意,对于无向图,根据定义,两个关系是对称的。

参数: 

res:

指向未初始化的图对象的指针,结果将存储在此处。

g1:

第一个操作数,一个图对象。

g2:

第二个操作数,另一个图对象。

edge_map1:

如果不是空指针,则它必须是指向已初始化向量的指针,并且从结果图的边到第一个图的边的映射存储在此处。

edge_map1:

如果不是空指针,则它必须是指向已初始化向量的指针,并且从结果图的边到第二个图的边的映射存储在此处。

返回值: 

错误代码。

时间复杂度:O(|V|*d1*d2),|V| 是第一个图中的顶点数,d1 和 d2 是第一个图和第二个图中的平均度。

示例 28.6.  文件 examples/simple/igraph_compose.c

#include <igraph.h>

int main(void) {

    igraph_t g1, g2, res;
    igraph_vector_int_t v;
    igraph_vector_int_t map1, map2;

    igraph_vector_int_init(&map1, 0);
    igraph_vector_int_init(&map2, 0);

    /* composition with the empty graph */
    igraph_empty(&g1, 5, IGRAPH_DIRECTED);
    igraph_full(&g2, 5, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS);
    igraph_compose(&res, &g1, &g2, &map1, &map2);
    if (igraph_ecount(&res) != 0) {
        return 1;
    }
    if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) {
        return 11;
    }
    igraph_destroy(&res);
    igraph_compose(&res, &g2, &g1, &map1, &map2);
    if (igraph_ecount(&res) != 0) {
        return 2;
    }
    if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) {
        return 12;
    }
    igraph_destroy(&res);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    /* same but undirected */
    igraph_empty(&g1, 5, IGRAPH_UNDIRECTED);
    igraph_full(&g2, 5, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS);
    igraph_compose(&res, &g1, &g2, &map1, &map2);
    if (igraph_ecount(&res) != 0) {
        return 1;
    }
    if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) {
        return 11;
    }
    igraph_destroy(&res);
    igraph_compose(&res, &g2, &g1, &map1, &map2);
    if (igraph_ecount(&res) != 0) {
        return 2;
    }
    if (igraph_vector_int_size(&map1) != 0 || igraph_vector_int_size(&map2) != 0) {
        return 12;
    }
    igraph_destroy(&res);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    /* proper directed graph */
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 5, 6, -1);
    igraph_create(&g1, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 0, 1, 2, 4, 5, 6, -1);
    igraph_create(&g2, &v, 0, IGRAPH_DIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_compose(&res, &g1, &g2, &map1, &map2);
    igraph_write_graph_edgelist(&res, stdout);
    igraph_vector_int_print(&map1);
    igraph_vector_int_print(&map2);
    igraph_destroy(&res);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    /* undirected graph */
    igraph_vector_int_init_int_end(&v, -1, 0, 1, 1, 2, 5, 6, -1);
    igraph_create(&g1, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_vector_int_init_int_end(&v, -1, 0, 1, 0, 4, 5, 6, -1);
    igraph_create(&g2, &v, 0, IGRAPH_UNDIRECTED);
    igraph_vector_int_destroy(&v);

    igraph_compose(&res, &g1, &g2, &map1, &map2);
    igraph_write_graph_edgelist(&res, stdout);
    igraph_vector_int_print(&map1);
    igraph_vector_int_print(&map2);
    igraph_destroy(&res);
    igraph_destroy(&g1);
    igraph_destroy(&g2);

    igraph_vector_int_destroy(&map2);
    igraph_vector_int_destroy(&map1);

    return 0;
}


3. 其他运算符

3.1. igraph_connect_neighborhood — 将每个顶点连接到其邻域。

igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order,
                                igraph_neimode_t mode);

此函数将新边添加到输入图中。每个顶点都连接到从它最多 order 步可到达的所有顶点(除非已经存在连接)。

请注意,输入图已就地修改,不会创建新图。如果您也想保留原始图,请调用 igraph_copy()

对于无向图,可达性始终是对称的:如果最多 order 步可以从顶点 B 到达顶点 A,那么反之亦然。在这种情况下,只会添加一个无向 (A,B) 边。

参数: 

:

输入图。它将就地修改。

order:

整数常量,它给出了顶点将连接到源顶点的距离。

模式:

常量,它指定如何对有向图执行邻域搜索。如果 IGRAPH_OUT,则可从源顶点到达的顶点将被连接,IGRAPH_IN 是相反的。如果 IGRAPH_ALL,则有向图被视为无向图。

返回值: 

错误代码。

另请参阅: 

igraph_graph_power() 用于计算图的 k 次幂;igraph_square_lattice() 使用此函数来连接顶点的邻域。

时间复杂度:O(|V|*d^k),|V| 是图中的顶点数,d 是平均度,k 是 order 参数。

3.2. igraph_contract_vertices — 用一个顶点替换多个顶点。

igraph_error_t igraph_contract_vertices(igraph_t *graph,
                             const igraph_vector_int_t *mapping,
                             const igraph_attribute_combination_t *vertex_comb);

此函数通过将多个顶点合并为一个来修改图。修改后的图中的顶点对应于输入图中的顶点组。不会删除任何边,因此修改后的图通常会具有自环(对应于组内边)和多重边(对应于两个组之间的多个连接)。使用 igraph_simplify() 删除自环并合并多重边。

参数: 

:

输入图。它将就地修改。

mapping:

一个向量,给出映射。对于原始图中的每个顶点,它应包含其在结果图中的所需 ID。为了不创建“孤立顶点”,即原始图中没有对应顶点的顶点,请确保 ID 是从零开始的连续整数。

vertex_comb:

如何处理顶点属性。NULL 意味着在收缩后不保留顶点属性(甚至对于未受影响的顶点)。有关详细信息,请参阅 igraph 手册中关于属性的部分。

返回值: 

错误代码。

时间复杂度:O(|V|+|E|),在顶点数加上边数中是线性的。

示例 28.7.  文件 examples/simple/igraph_contract_vertices.c

#include <igraph.h>

/* Create the condensation of a directed graph.
 * See https://en.wikipedia.org/wiki/Strongly_connected_component#Definitions
 * This example demonstrates how to write a basic igraph function, complete
 * with error handling. */
igraph_error_t condensation(const igraph_t *graph, igraph_t *cond) {
    igraph_vector_int_t membership;

    /* Data structures such as vector must be initialized in igraph before use. */
    IGRAPH_CHECK(igraph_vector_int_init(&membership, 0));

    /* Adding the initialized vector to the "finally" stack ensures that it will
     * be automatically destroyed if an error occurs. */
    IGRAPH_FINALLY(igraph_vector_int_destroy, &membership);

    /* Functions that return an error code can be wrapped in IGRAPH_CHECK to pass that error
     * up to the caller. */
    IGRAPH_CHECK(igraph_connected_components(graph, &membership, /* csize */ NULL, /* no */ NULL, IGRAPH_STRONG));

    /* To compute the condensation, we simply contract strongly connected components.
     * Since igraph_contract_vertices() modifies graphs in-place, we make a copy first. */
    IGRAPH_CHECK(igraph_copy(cond, graph));

    /* Since we are not done creating the condensation yet, we add 'cond' to the
     * "finally" stack, so that it will be destroyed if an error occurs. */
    IGRAPH_FINALLY(igraph_destroy, cond);

    /* Contract strongly connected components. */
    IGRAPH_CHECK(igraph_contract_vertices(cond, &membership, NULL));

    /* igraph_contract_vertices() preserves all edges, some of which become
     * parallel edges or self-loops after the contraction. We simplify these. */
    IGRAPH_CHECK(igraph_simplify(cond, /* remove_multiple */ true, /* remove_loops */ true, NULL));

    /* Data structures that are no longer needed must be explicitly destroyed.
     * If they were added to the "finally" stack, they must be removed explicitly,
     * in the opposite order to how they were added. IGRAPH_FINALLY_CLEAN removes
     * the indicated number of entries from the "finally" stack. We remove
     * 'membership' because it was destroyed, and 'cond' because the responsibility
     * to destroy it is now with the caller. */
    igraph_vector_int_destroy(&membership);
    IGRAPH_FINALLY_CLEAN(2);

    return IGRAPH_SUCCESS; /* return with no error */
}

int main(void) {
    igraph_t graph, cond;

    /* Create a random directed graph with mean degree 2 and compute its condensation. */
    igraph_erdos_renyi_game_gnm(&graph, 100, 200, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS);
    condensation(&graph, &cond);

    printf("Number of vertices in the condensation: %" IGRAPH_PRId "\n", igraph_vcount(&cond));
    igraph_write_graph_edgelist(&cond, stdout);

    /* Destroy data structures that are no longer needed. */
    igraph_destroy(&graph);
    igraph_destroy(&cond);

    return 0;
}


3.3. igraph_graph_power — 图的 k 次幂。

igraph_error_t igraph_graph_power(const igraph_t *graph, igraph_t *res,
                                  igraph_integer_t order, igraph_bool_t directed);

警告

此函数是实验性的,其签名尚未被视为最终签名。我们保留更改函数签名的权利,而无需更改 igraph 的主要版本。使用它需要您自担风险。

图 G 的 k 次幂是一个简单图,其中顶点 u 通过一条边连接到 v,如果 v 在 G 中最多 k 步可从 u 到达。按照惯例,图的零次幂没有边。一次幂与原始图相同,只是删除了多重边和自环。

图幂通常仅为无向图定义。igraph 将此概念扩展到有向图。要在输入中忽略边的方向,请将 directed 参数设置为 false。在这种情况下,结果将是一个无向图。

图和顶点属性被保留,但边属性被丢弃。

参数: 

:

输入图。

res:

给定 order 的图的幂。

order:

非负整数,将图提升到的幂。换句话说,距离在 order 以内的顶点将被连接。

有向:

是否考虑边的方向。

返回值: 

错误代码。

另请参阅: 

igraph_connect_neighborhood() 用于将每个顶点连接到其邻域,从而就地修改图。

时间复杂度:O(|V|*d^k),|V| 是图中的顶点数,d 是平均度,k 是 order 参数。

3.4. igraph_product — 两个图的图积,根据选择的积类型。

igraph_error_t igraph_product(igraph_t *res,
                              const igraph_t *g1,
                              const igraph_t *g2,
                              igraph_product_t type);

警告

此函数是实验性的,其签名尚未被视为最终签名。我们保留更改函数签名的权利,而无需更改 igraph 的主要版本。使用它需要您自担风险。

此函数使用由 type 参数选择的图积概念来计算两个图的积。这两个图必须是相同的类型,有向图或无向图。如果需要无向图和有向图的积,请使用 igraph_to_directed()igraph_to_undirected() 将其中一个转换为适当的类型。

乘积图的每个顶点对应于一对 (u, v),其中 u 是来自第一个图的顶点,v 是来自第二个图的顶点。因此,乘积图中的顶点数为 |V1| |V2|,其中 |V1||V2| 是操作数的顶点集的大小。使用 index = u |V2| + v 将对 (u, v) 映射到乘积图中的唯一顶点索引。

下面详细介绍了支持的图积类型。符号 u ~ v 用于指示顶点 uv 相邻,即存在从 uv 的连接。

IGRAPH_PRODUCT_CARTESIAN

计算两个图的笛卡尔积。在乘积图中,当且仅当 u1 = u2v1 ~ v2u1 ~ u2v1 = v2 时,才存在从 (u1, v1)(u2, v2) 的连接。因此,乘积中的边数为 |V1| |E2| + |V2| |E1|

时间复杂度:O(|V1| |V2| + |V1| |E2| + |V2| |E1|),其中 |V1| 和 |V2| 是顶点数,|E1| 和 |E2| 是操作数的边数。

IGRAPH_PRODUCT_TENSOR

计算两个图的张量(分类)积。在乘积图中,当且仅当 u1 ~ u2v1 ~ v2 时,才存在从 (u1, v1)(u2, v2) 的连接。因此,在有向情况下,乘积中的边数为 |E1| |E2|,在无向情况下,边数为 2 |E1| |E2|

时间复杂度:O(|V1| |V2| + |E1| |E2|),其中 |V1| 和 |V2| 是顶点数,|E1| 和 |E2| 是操作数的边数。

参数: 

res:

指向未初始化的图对象的指针。乘积图将存储在此处。

g1:

第一个操作数图。

g2:

第二个操作数图。

type:

要计算的图积的类型。

返回值: 

错误代码:如果指定的 type 不受支持或输入图 g1g2 与请求的产品不兼容,则为 IGRAPH_EINVAL

3.5. igraph_induced_subgraph — 创建由指定顶点导出的子图。

igraph_error_t igraph_induced_subgraph(const igraph_t *graph, igraph_t *res,
                                       const igraph_vs_t vids,
                                       igraph_subgraph_implementation_t impl);

此函数收集指定的顶点和它们之间的所有边到新图。由于顶点 ID 始终是从零开始的连续整数,因此创建的子图中的 ID 将与原始图中的 ID 不同。要获取它们之间的映射,请使用 igraph_induced_subgraph_map()

参数: 

:

图对象。

res:

子图,另一个图对象将存储在此处,请不要在此函数调用之前初始化此对象,如果您不再需要它,请调用 igraph_destroy()

vids:

一个顶点选择器,描述要保留哪些顶点。顶点可以在选择器中出现多次,但它只会被考虑一次(即,不可能通过将其 ID 多次添加到选择器来复制顶点)。顶点在顶点选择器中出现的顺序被忽略;返回的子图将始终包含原始图的顶点,并按顶点 ID 的递增顺序排列。

impl:

此参数选择我们在构造新图时应使用的实现。基本上有两种可能性:IGRAPH_SUBGRAPH_COPY_AND_DELETE 复制现有图并删除新图中不需要的顶点,而 IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH 从头开始构造新图,而不复制旧图。如果您要提取非常大的图的相对较小的子部分,则后者更有效,而如果您要提取大小与整个图的大小相当的子图,则前者更好。还有第三种可能性:IGRAPH_SUBGRAPH_AUTO 将根据新图和旧图中的顶点数之比自动选择两种方法之一。

返回值: 

错误代码:IGRAPH_ENOMEM,没有足够的内存用于临时数据。IGRAPH_EINVVIDvids 中的顶点 ID 无效。

时间复杂度:O(|V|+|E|),|V| 和 |E| 是原始图中顶点和边的数量。

另请参阅: 

请同时参考igraph_induced_subgraph_map() 来获取图和提取的子图之间的顶点 ID 映射; 以及 igraph_delete_vertices() 来从图中删除指定的顶点集合,这是此函数的反向操作。

3.6. igraph_induced_subgraph_map — 创建一个诱导子图并返回原始图的映射。

igraph_error_t igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res,
                                           const igraph_vs_t vids,
                                           igraph_subgraph_implementation_t impl,
                                           igraph_vector_int_t *map,
                                           igraph_vector_int_t *invmap);

此函数将指定的顶点以及它们之间的所有边收集到一个新图中。由于顶点 ID 始终是从零开始的连续整数,因此创建的子图中的 ID 将与原始图中的 ID 不同。图和提取的子图中的顶点 ID 之间的映射在 mapinvmap 中返回。

参数: 

:

图对象。

res:

子图,另一个图对象将存储在此处,请不要在此函数调用之前初始化此对象,如果您不再需要它,请调用 igraph_destroy()

vids:

一个顶点选择器,用于描述要保留哪些顶点。

impl:

此参数选择构造新图时应使用的实现方式。基本上有两种可能性:IGRAPH_SUBGRAPH_COPY_AND_DELETE 复制现有图并删除新图中不需要的顶点,而 IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH 从头开始构造新图,而不复制旧图。如果您要提取一个非常大的图的一个相对较小的子部分,则后者效率更高,而如果您要提取的子图的大小与整个图的大小相当,则前者更好。还有第三种可能性:IGRAPH_SUBGRAPH_AUTO 将根据新图和旧图中顶点数量的比率自动选择两种方法之一。

map:

返回 graph 中顶点到 res 中顶点的映射。 0 表示顶点未映射。 位置 j 处的 i + 1 表示 graph 中的顶点 j 映射到 res 中的顶点 i。

invmap:

返回 res 中顶点到 graph 中顶点的映射。 位置 j 处的 i 表示 graph 中的顶点 i 映射到 res 中的顶点 j。

返回值: 

错误代码:IGRAPH_ENOMEM,没有足够的内存用于临时数据。IGRAPH_EINVVIDvids 中的顶点 ID 无效。

时间复杂度:O(|V|+|E|),|V| 和 |E| 是原始图中顶点和边的数量。

另请参阅: 

请同时参考 igraph_delete_vertices() 来从图中删除指定的顶点集合,这是此函数的反向操作。

3.7. igraph_induced_subgraph_edges — 诱导子图中包含的边。

igraph_error_t igraph_induced_subgraph_edges(const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges);

此函数查找那些连接给定列表中顶点的边的 ID,列表在 vids 参数中传递。

参数: 

:

图。

vids:

一个顶点选择器,指定构成子图的顶点。

:

整数向量。 vids 诱导的子图中的边的 ID 将存储在此处。

返回值: 

错误代码。

时间复杂度:O(mv log(nv)),其中 nv 是 vids 中的顶点数,mv 是 vids 中顶点的度数之和。

3.8. igraph_linegraph — 创建图的线图。

igraph_error_t igraph_linegraph(const igraph_t *graph, igraph_t *linegraph);

G 无向图的线图 L(G) 定义如下。 L(G) 对于 G 中的每条边都有一个顶点,并且 L(G) 中的两个不同顶点通过一条边连接,如果它们对应的边共享一个端点。 在一个多重图中,如果共享两个端点,则创建两条边。 无向自环的单个顶点被计为两个端点。

G 有向图的线图 L(G) 略有不同:L(G) 对于 G 中的每条边都有一个顶点,并且 L(G) 中的两个顶点通过有向边连接,如果第一个顶点对应边的目标与第二个顶点对应边的源相同。

自环被认为是自相邻的,因此它们在线图中的对应顶点也将具有一个自环,在无向图和有向图中都是如此。

原始图中的边 i 将对应于线图中的顶点 i

此函数的第一个版本由 Vincent Matossian 贡献,谢谢。

参数: 

:

输入图,可以是有向图或无向图。

linegraph:

指向未初始化的图对象的指针,结果将存储在此处。

返回值: 

错误代码。

时间复杂度:O(|V|+|E|),边数加上顶点数。

3.9. igraph_simplify — 从图中移除环和/或多重边。

igraph_error_t igraph_simplify(igraph_t *graph,
                               igraph_bool_t remove_multiple, igraph_bool_t remove_loops,
                               const igraph_attribute_combination_t *edge_comb);

此函数根据 multipleloops 参数合并平行边并移除自环。 请注意,即使输入已经是简单图,此函数也可能会更改边的顺序。

参数: 

:

图对象。

remove_multiple:

如果为 true,则将移除多重边。

remove_loops:

如果为 true,则将移除环(自环)。

edge_comb:

如何处理边的属性。 NULL 表示在操作后丢弃边的属性,即使对于未受影响的边也是如此。 有关详细信息,请参阅 igraph 手册中关于属性的部分。

返回值: 

错误代码:如果内存不足,则为 IGRAPH_ENOMEM

时间复杂度:O(|V|+|E|)。

示例 28.8.  文件 examples/simple/igraph_simplify.c

#include <igraph.h>

int main(void) {

    igraph_t g;

    /* Multiple edges */

    igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, -1);
    igraph_simplify(&g, true, true, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_UNDIRECTED, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, -1);
    igraph_simplify(&g, true, true, /*edge_comb=*/ NULL);
    if (igraph_ecount(&g) != 1) {
        return 1;
    }
    igraph_destroy(&g);

    /* Loop edges*/

    igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 0, 1, 1, 2, 2, 1, 2, -1);
    igraph_simplify(&g, true, true, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_UNDIRECTED, 0, 0, 1, 1, 2, 2, 1, 2, -1);
    igraph_simplify(&g, true, true, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    /* Loop & multiple edges */

    igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, -1);
    igraph_simplify(&g, /* remove_multiple */ true, /* remove_loops */ false, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_UNDIRECTED, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, -1);
    igraph_simplify(&g, /* remove_multiple */ true, /* remove_loops */ false, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_DIRECTED, 2, 2, 2, 2, 2, 2, 3, 2, -1);
    igraph_simplify(&g, /* remove_multiple */ false, /* remove_loops */ true, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_UNDIRECTED, 3, 3, 3, 3, 3, 4, -1);
    igraph_simplify(&g, /* remove_multiple */ false, /* remove_loops */ true, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_DIRECTED, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, -1);
    igraph_simplify(&g, true, true, /*edge_comb=*/ NULL);
    igraph_write_graph_edgelist(&g, stdout);
    igraph_destroy(&g);

    igraph_small(&g, 0, IGRAPH_UNDIRECTED,
                 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 2, 3, 2, -1);
    igraph_simplify(&g, true, true, /*edge_comb=*/ NULL);
    if (igraph_ecount(&g) != 1) {
        return 2;
    }
    igraph_destroy(&g);

    return 0;
}


3.10. igraph_subgraph_from_edges — 创建一个包含指定边及其端点的子图。

igraph_error_t igraph_subgraph_from_edges(
    const igraph_t *graph, igraph_t *res, const igraph_es_t eids,
    igraph_bool_t delete_vertices
);

此函数将指定的边及其端点收集到一个新图中。 由于图中的边 ID 始终是从零开始的连续整数,因此提取的子图中的边 ID 将与原始图中的边 ID 不同。 如果 delete_vertices 设置为 true,则还将重新分配顶点 ID。 属性将被保留。

参数: 

:

图对象。

res:

子图,另一个图对象将存储在此处,请不要在此函数调用之前初始化此对象,如果您不再需要它,请调用 igraph_destroy()

eids:

一个边选择器,用于描述要保留哪些边。

delete_vertices:

是否也要删除未与任何指定边关联的顶点。 如果为 false,则结果图中的顶点数将始终等于输入图中的顶点数。

返回值: 

错误代码:IGRAPH_ENOMEM,临时数据内存不足。 IGRAPH_EINVEIDeids 中的无效边 ID。

时间复杂度:O(|V|+|E|),|V| 和 |E| 是原始图中顶点和边的数量。

另请参阅: 

请同时参考 igraph_delete_edges() 来从图中删除指定的边集合,这是此函数的反向操作。

3.11. igraph_reverse_edges — 反转有向图的某些边。

igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids);

此函数反转有向图的某些边。 修改是就地完成的。 所有属性以及边和顶点的顺序都将被保留。

请注意,很少需要反转所有边,因为几乎所有处理有向图的函数都采用 mode 参数,该参数可以设置为 IGRAPH_IN 以有效地将边视为已反转。

参数: 

:

将反转其边的图。

es:

要反转的边。 传递 igraph_ess_all(IGRAPH_EDGEORDER_ID) 以反转所有边。

返回值: 

错误代码。

时间复杂度:如果反转所有边,则为 O(1),否则为 O(|E|),其中 |E| 是图中的边数。

4. 已弃用的函数

4.1. igraph_subgraph_edges — 创建一个包含指定边及其端点的子图(已弃用的别名)。

igraph_error_t igraph_subgraph_edges(
    const igraph_t *graph, igraph_t *res, const igraph_es_t eids,
    igraph_bool_t delete_vertices
);

警告

自 0.10.3 版本起已弃用。 请不要在新代码中使用此函数; 请改用 igraph_subgraph_from_edges()