1

I've created a generic data structures and algorithm library written in C. I've tried to implement Minimum Spanning tree using the Prim's Algorithm

When I run the code for a bi directional graph, I see that the reverse edge also gets added as part of the MST() creating a duplicate edge and increasing the overall MST tree cost.

The Code implementation is as given below,

graph.h

/*! @file graph.h
    @brief 
    Contains declations of graph types, operations and structure
*/
#pragma once
#include "common.h"
#include "link_list.h"

/// graph Vertex
typedef struct gnode {
    t_gen id;           ///< Pointer to store Data
    int idx;            ///< Index of vertex in adaceny list
    t_linklist *neigh;      ///< Link List to neighbor vertices(nodes)
} t_gnode;

/// graph neighbor edges represented in neigh list
typedef struct gedge {
    t_gnode *node;          ///< Pointer to neighbor vertex
    int weight;         ///< Cost of the edge
} t_gedge;

// fn ptr for adding weighted edge
typedef t_gen (*f_wedge)(t_gen, t_gen, t_gen, int); 

/// graph struct defn
typedef struct graph {
    // graph info params
    char *name;         ///< Graph Instance Name
    int count;          ///< Vertex Count of graph
    int max_size;           ///< Max Vertex count of graph
    int total_edges;        ///< Edge count of graph

    // graph nodes
    t_gnode *nodes;         ///< Adaceny List Representation of graph vertices

    // graph routines
    f_gen2 add_vertex;      ///< routine to add a vertex in graph
    f_gen2 del_vertex;      ///< routine to del a vertex in graph
    f_gen3 add_edge;        ///< routine to add an edge in graph
    f_gen3 del_edge;        ///< routine to del an edge in graph
    f_gen3 add_edge_sym;        ///< routine to add a symmetric edge in graph
    f_gen3 del_edge_sym;        ///< routine to del a symmetric edge in graph
    f_wedge add_wedge;      ///< routine to add a weighted edge in graph
    f_wedge add_wedge_sym;      ///< routine to add a weighted symmetric 
    f_find find;            ///< routine to find a vertex in graph
    f_len len;          ///< routine to get vertex count in graph
    
} t_graph;


/// Dist info
typedef struct dist_info {
    t_gedge edge;           ///< Pointer to edge
    t_gnode *parent;        ///< Pointer to Vertex vertex
} t_distinfo;

graph.c


/*! @brief  
 *  Find the Minimum Spanning for weighted undirected graph
 *  Using Prim's Algorithm
 *  @see https://en.wikipedia.org/wiki/Prim%27s_algorithm
 *  @param d     - Pointer instance of graph
 *  @return      - Pointer to dist array to all nodes in graph
 */
t_gen prims_mst(t_gen d)
{
    t_graph *g = (t_graph*)d;
    t_gedge *v, *u;
    t_llnode *cur, *end;
    t_linklist *neigh_list;
    t_distinfo *dist, *tmp;
    t_heap *h;
    t_dparams dp;
    t_gen *pq;

    // Data specific operation for generic min heap
    init_data_params(&dp, eUSER);
    dp.free     = dummy_free;
    dp.cmpr     = graph_wedge_cmpr; 
    dp.cmpr_idx = graph_wedge_cmpr_idx;
    dp.swap_idx = gen_swp_idx;
    dp.copy_idx = gen_cpy_idx;
    dp.get_idx  = gen_get_idx;

    // Creating a generic min heap to store edges
    pq   = get_mem(g->count, sizeof(t_gen));    
    h    = create_heap("Dijkstra's Heap", pq, g->count, eMIN_HEAP, &dp);
    
    // Initalize all dist to all nodes as infinite 
    dist = get_mem(g->count, sizeof(t_distinfo));   
    for (int i = 0; i < g->count; i++) {
        dist[i].edge.node   = &g->nodes[i];
        dist[i].edge.weight = INT_MAX; 
        dist[i].parent      = NULL;
    }
    
    
    // Set 0 as start vertex
    h->insert(h, &dist[0].edge);
    while (h->empty(h) != true) {
        // Of the edges that connect the tree to vertices not yet in the tree,
        // Find the minimum-weight edge, and transfer it to the tree
        u = h->extract(h);

        neigh_list = (t_linklist*)u->node->neigh;
        cur = neigh_list->head_node(neigh_list);
        end = neigh_list->end_node(neigh_list);
        while (cur) {
            v = (t_gedge*)cur->data;
            
            // If cur edge weight is greater than new edge
            // and the end vertex is not visited
            if (dist[v->node->idx].edge.weight > v->weight) {
                // Set new edge as part of MST, update parent
                // and add edge to heap
                dist[v->node->idx].edge.weight = v->weight;
                dist[v->node->idx].parent = u->node;
                h->insert(h, &dist[v->node->idx].edge);
            }
            
            // Exit after neigh list traversal complete
            cur = neigh_list->next_node(neigh_list, cur);
            if (cur == end) {
                break;
            }
        }
    }
    
    // Destroy heap and destroy the array used for storing heap
    h->destroy(h);
    free_mem(pq);

    return dist;
}

Can somebody help me figure out how to remove the reverse(duplicate) edge from the MST.

Heap implementationhere.

Complete Graph implementation here.

Link list implementation that forms the adjacency list here.

Link to Github repo here

Dominique Fortin
  • 2,212
  • 15
  • 20
  • Perhaps the problem is related to conceiving your graph as "bidirectional" instead of *undirected*. The former would be an implementation detail that should be invisible to clients. The latter is a semantic description of a graph to which Prim's could apply. – John Bollinger Aug 09 '21 at 14:57
  • 2
    Overall, though, you have presented far too much code. Unless you reduce the problem to a [mre], we are unlikely to be able to help you. – John Bollinger Aug 09 '21 at 14:59
  • @Mohammed Meraj - If you read the Wikipedia article referred to in the code comment, you'll realize that the `Graph 2` you use for the test cannot have a spanning tree, as of its ten vertices only seven are connected. – Armali Aug 10 '21 at 19:36

0 Answers0