It really depends on what algorithms you need to implement, there is no silver bullet (and that's shouldn't be a surprise... the general rule about programming is that there's no general rule ;-) ).
I often end up representing directed multigraphs using node/edge structures with pointers... more specifically:
struct Node
{
... payload ...
Link *first_in, *last_in, *first_out, *last_out;
};
struct Link
{
... payload ...
Node *from, *to;
Link *prev_same_from, *next_same_from,
*prev_same_to, *next_same_to;
};
In other words each node has a doubly-linked list of incoming links and a doubly-linked list of outgoing links. Each link knows from
and to
nodes and is at the same time in two different doubly-linked lists: the list of all links coming out from the same from
node and the list of all links arriving at the same to
node.
The pointers prev_same_from
and next_same_from
are used when following the chain of all the links coming out from the same node; the pointers prev_same_to
and next_same_to
are instead used when managing the chain of all the links pointing to the same node.
It's a lot of pointer twiddling (so unless you love pointers just forget about this) but query and update operations are efficient; for example adding a node or a link is O(1), removing a link is O(1) and removing a node x is O(deg(x)).
Of course depending on the problem, payload size, graph size, graph density this approach can be way overkilling or too much demanding for memory (in addition to payload you've 4 pointers per node and 6 pointers per link).
A similar structure full implementation can be found here.