首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Kosaraju算法在C简单实现中的应用

Kosaraju算法在C简单实现中的应用
EN

Code Review用户
提问于 2022-05-24 07:33:10
回答 1查看 390关注 0票数 3

我想知道我能否得到一些关于我简单实现Kosaraju算法的反馈。更具体地说,我是如何使用malloc和方法名称去分配我分配的东西的。

代码语言:javascript
运行
复制
/**
 * Kosaraju's algorithm implementation which is a linear time algorithm
 * to find the strongly connected components of a directed graph.
 * https://en.wikipedia.org/wiki/kosaraju's_algorithm
 */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct vertex {
    int value;
    struct vertex* next;
};

typedef struct vertex Vertex;

typedef struct graph {
    int num_vertices;
    Vertex** neighbors;
};

typedef struct graph Graph;

struct stack {
    int value;
    struct stack* next;
};

typedef struct stack Stack;

/**
 * @brief Create stack using endogenous linked list
 * @param stack pointer to a stack pointer
 */
static void stack_create(Stack** stack) {
    *stack = NULL;
}

/**
 * @brief Push method of stack
 * @param stack pointer to a stack pointer
 * @param value value to push to stack
 */
static void stack_push(Stack** stack, int value) {
    Stack* item = malloc(sizeof(Stack));
    item->value = value;
    item->next = *stack;
    *stack = item;
}

/**
 * @brief Pop method of stack
 * @param stack pointer to a stack pointer
 * @param value that has just been popped from the stack
 * @return boolean indicating whether popping from the stack was successful or
 * not
 */
static bool stack_pop(Stack** stack, int* v) {
    Stack* old = *stack;
    if (!old)
        return false;

    *v = old->value;
    *stack = old->next;
    free(old);
    return true;
}

/**
 * @brief Destroy method of stack
 * @param stack pointer to a stack pointer
 */
static void stack_destroy(Stack** stack) {
    int temp;
    while (stack_pop(stack, &temp))
        ;
}

/**
 * @brief Checks whether stack is empty or not
 * @param stack pointer to a stack pointer
 * @return boolean indicating whether stack is empty or not
 */
static bool stack_is_empty(Stack** stack) {
    return *stack == NULL;
}

/**
 * @brief Create graph given number of vertices implemented using adjacency
 * @return pointer to allocated graph
 */
static Graph* graph_create(int num_vertices) {
    Graph* graph = malloc(sizeof(Graph));
    graph->num_vertices = num_vertices;
    size_t vertices_size = num_vertices * sizeof(Vertex*);
    graph->neighbors = (Vertex**)malloc(vertices_size);
    memset(graph->neighbors, NULL, vertices_size);
    return graph;
}

/**
 * @brief Add edge method of graph
 * @param graph pointer to graph
 * @param source index of source
 * @param sink index of sink
 */
static void graph_add_edge(Graph* graph, int source, int sink) {
    Vertex* item = (Vertex*)malloc(sizeof(Vertex));
    item->value = sink;
    item->next = graph->neighbors[source];
    graph->neighbors[source] = item;
}

/**
 * @brief Deallocate graph vertex
 * @param vertex vertex linked list node
 */
static void graph_destroy_vertex(Vertex* vertex) {
    if (vertex == NULL)
        return;

    if (vertex->next != NULL)
        graph_destroy_vertex(vertex->next);

    free(vertex);
}

/**
 * @brief Deallocate graph
 * @param graph pointer to graph
 */
static void graph_destroy(Graph* graph) {
    int i;
    for (i = 0; i < graph->num_vertices; i++) {
        graph_destroy_vertex(graph->neighbors[i]);
    }

    free(graph);
}

/**
 * @brief DFS traversal of graph
 * @param graph pointer to graph
 * @param stack pointer to stack
 * @param visited visited boolean array
 * @param v vertex
 */
static void dfs(Graph* graph, Stack** stack, bool* visited, int v) {
    visited[v] = true;
    Vertex* neighbors = graph->neighbors[v];
    while (neighbors != NULL) {
        if (!visited[neighbors->value]) {
            dfs(graph, stack, visited, neighbors->value);
        }
        neighbors = neighbors->next;
    }
    stack_push(stack, v);
}

/**
 * @brief Builds reverse of graph
 * @param graph pointer to graph
 * @return reversed graph
 */
static Graph* reverse(Graph* graph) {
    Graph* reversed_graph = graph_create(graph->num_vertices);

    int i;
    Vertex* neighbors;
    for (i = 0; i < graph->num_vertices; i++) {
        neighbors = graph->neighbors[i];
        while (neighbors != NULL) {
            graph_add_edge(reversed_graph, neighbors->value, i);
            neighbors = neighbors->next;
        }
    }
    return reversed_graph;
}

/**
 * @brief Use dfs to list a set of vertices dfs_and_print from a vertex v in
 * reversed grap
 * @param graph pointer to graph
 * @param visited boolean array indicating whether index has been visited or not
 * @param deleted boolean array indicating whether index has been popped or not
 * @param v vertex
 */
void dfs_and_print(Graph* graph, bool* visited, bool* deleted, int v) {
    printf("%d,", v);
    visited[v] = true;
    deleted[v] = true;
    Vertex* arcs = graph->neighbors[v];  // the adjacent list of vertex v
    while (arcs != NULL) {
        int u = arcs->value;
        if (!visited[u] && !deleted[u]) {
            dfs_and_print(graph, visited, deleted, u);
        }
        arcs = arcs->next;
    }
}

/**
 * @brief Collect SCC from the graph
 * @param graph pointer to graph
 * @param stack pointer to a stack pointer
 * @param visited boolean array indicating whether index has been visited or not
 */
void collect_scc(Graph* graph, Stack** stack, bool* visited) {
    bool* deleted = (bool*)alloca(graph->num_vertices * sizeof(bool));
    memset(deleted, false, graph->num_vertices * sizeof(bool));
    int c = 1;
    while (!stack_is_empty(stack)) {
        int v;
        bool any = stack_pop(stack, &v);
        if (any && !deleted[v]) {
            memset(visited, false,
                   graph->num_vertices *
                   sizeof(bool));  // mark all vertices of reverse as not visited
            printf("Strongly Connected Component #%d: ", c);
            dfs_and_print(graph, visited, deleted, v);
            printf("\n");
            c++;
        }
    }
}

/**
 * @brief Kosaraju logic
 * @param graph pointer to graph
 */
static void kosaraju(Graph* graph) {
    Stack* stack;
    stack_create(&stack);

    size_t visited_size = graph->num_vertices * sizeof(bool);
    bool* visited = (bool*)alloca(visited_size);
    memset(visited, false, visited_size);
    int i = 0;
    for (i = 0; i < graph->num_vertices; i++) {
        if (!visited[i]) {
            dfs(graph, &stack, visited, i);
        }
    }

    Graph* reversed_graph = reverse(graph);
    collect_scc(reversed_graph, &stack, visited);

    graph_destroy(graph);
    graph_destroy(reversed_graph);
    stack_destroy(&stack);
}

int main() {
    Graph* graph = graph_create(10);
    graph_add_edge(graph, 1, 2);
    graph_add_edge(graph, 2, 1);

    graph_add_edge(graph, 3, 4);
    graph_add_edge(graph, 4, 5);
    graph_add_edge(graph, 5, 6);
    graph_add_edge(graph, 6, 7);
    graph_add_edge(graph, 7, 3);

    kosaraju(graph);

    return 0;
}
EN

回答 1

Code Review用户

发布于 2022-05-24 13:43:34

按引用类型

分配

考虑查看下面的代码。这种类型对吗?要确定,需要在其他地方搜索(较早的70行)才能看到Vertex*

代码语言:javascript
运行
复制
size_t vertices_size = num_vertices * sizeof(Vertex*);
graph->neighbors = (Vertex**)malloc(vertices_size);

现在尝试无类型代码。如果没有搜索,这是正确的。它更容易编写正确的代码,检查和维护。

代码语言:javascript
运行
复制
size_t vertices_size = num_vertices * sizeof graph->neighbors[0];
graph->neighbors = malloc(vertices_size);

代码假定*alloc()从未失败

malloc()和朋友们可能会失败。健壮的代码检查NULL返回。

num_vertices <= 0

num_vertices <= 0时,代码在没有检测到的情况下失败。考虑size_t num_vertices或检测和处理超出范围的输入.

memset()表示字节(

)

严格地说,NULL不一定是0及以下,然后只对指针的每个字节使用LSByte。相反,使用一个循环并对每个指针进行迭代或研究calloc()以零填充一个分配。

代码语言:javascript
运行
复制
memset(graph->neighbors, NULL, vertices_size);  // Iffy code.

memset(graph->neighbors, 0, vertices_size);  // better

memset(deleted, false, graph->num_vertices * sizeof(bool));在概念上也有问题。虽然false是0-所以我们在这里功能上还可以,但bool可能超过1字节。

static

我更希望在一个单独的*.h,*.c文件中看到图函数,然后将static用于需要它的函数。

格式

不要手动格式化,考虑使用自动格式化程序。它更有效率。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/276797

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档