0

I'm using C++ to create Hopcroft's algorithm for DFA Minimization.

Part of Hopcroft's algorithm is to - initially - divide two sets (P with accept and non-accept states and Q with non-accept states only). I already have group P, and from P I'm trying to extract Q. I'm using the following code to do it:

for(int i=0; i<groupP.size(); i++)
    if(groupP[i]->final)
        groupQ.push_back(groupP[i]);

in which groupP and groupQ are:

vector<node*> groupQ;
vector<node*> groupP;

and node is a structure that I've created to represent a node of my automata. It's guaranteed that the boolean attribute "final" is already correctly set (false for non-final states, true for final states).

Finally, my question is: is it correct to copy one element from a vector to another by doing what I've done? If I modify the content of a copied element from groupP, will this same element be modified in groupQ as well?

woz
  • 544
  • 6
  • 22

2 Answers2

3

Right now, you have vectors of pointers. When you copy from one vector to another, you're copying the pointers, not the elements themselves.

Since you have two pointers referring to the same node, any modification made to a node will be visible in the other group--i.e., if yo make a change to groupP[i]->foo, then the same change will be visible in groupQ[j]->foo (provided that groupP[i] is one of the elements you copied from groupP to groupQ.

If you don't want that, you have a couple of choices. One would be to leave groupP and groupQ in the same vector, but partition the vector based on the state of an element's final member:

auto P_end = std::partition(groupP.begin(), groupQ.end(), 
                              [](node *n) { return n->final;});

Then [groupP.begin(), P_begin) is groupP (i.e., final==true) and [P_begin, groupP.end()) is groupQ (i.e., final==false).

This moves the pointers around (and gives you an iterator so you know the dividing line between the two) so you have exactly one pointer to each element, but they're separated into the two relevant groups.

As a final possibility, you might want to actually copy elements from groupP to groupQ, and in the process create a new element, so after you copy items from groupP to groupQ, each item you copied now exists in two place--i.e., there's one element in groupP and one element in groupQ. Either one can be modified, but they're separate from each other, so either can be modified, but a modification to one has no effect on the other.

The most obvious way to achieve that would be be to just use vectors of nodes:

vector<node> groupQ;
vector<node> groupP;

This way, when you copy from one group to the other, you're copying the nodes themselves rather than pointers to nodes, so each copy creates a new, independent node with the same value as an existing node.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Best answer! What if I do something like this: ` for(int j=0; jfinal) groupF.push_back(new node(automata[j]->state_name, automata[j]->transitions, automata[j]->final, automata[j]->inicial)); //...etc ` EDIT: Sorry I'm not managing to post it in a code-formatted style – woz May 29 '15 at 22:05
  • @woz: you certainly *can* dynamically allocate each element individually--but it's error prone and generally quite slow, so I'd tend to avoid it as a rule. – Jerry Coffin May 29 '15 at 22:08
2

You could use std::copy_if which does the same thing:

std::copy_if(groupP.cbegin(), groupP.cend(),
             std::back_inserter(groupQ),
             [](node* n){ return n->final; });

Since you are manipulating pointers, the elements themselves are shared, so modifying a node in one of the container can be seen from the other.

Note that manipulating raw pointers like you are doing is very error prone, and you may want to use shared pointers for instance.

Edit: Adding missing std::back_inserter.

Émilien Tlapale
  • 903
  • 5
  • 11