3

Let's assume I have a vector<node> containing 10000 objects:

vect[0] to vect[9999]

struct node
{
    int data;
};

And let's say I want to find the vector id that contain this data ("444"), which happens to be in node 99.

Do I really have to do a for-loop to loop through all the elements then use

if (data == c[i].data)

Or is there a quicker way? Consider that my data is distinct and won't repeat in other nodes.

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
user2079226
  • 31
  • 1
  • 4
  • 1
    Are the elements sorted? – johnsyweb Feb 16 '13 at 20:55
  • Unless you know exactly where the element you're looking for is located you have to traverse the array in order to find it. If you're sure that its index is 99, for example, you can simply say `vect[99]` and you know it contains the value `444`. – Tomislav Dyulgerov Feb 16 '13 at 20:55
  • 1
    If you can choose a different container then a std::map would be faster. – amdn Feb 16 '13 at 20:55
  • What do you mean by "the vector id"? Do you need the *index* (as you say in the title) or do you really just need the *element*? As others hav alluded, it is important to choose the correct data structure for the operations you are likely to perform. – johnsyweb Feb 16 '13 at 21:08

5 Answers5

4

For this answer I am assuming that you've made an informed decision to use a std::vector over the other containers available.

Do I really have to do a for-loop to loop through all the elements?

No, you do not have to roll a for-loop to find an element. The idiomatic way of finding an element in a container is to use an algorithm from the standard library. Whether you should roll your own really depends on the situation.

To help you decide...

Alternative 1:

std::find() requires a that there is a suitable equality comparator for your node data type, which may be as simple as this:

bool operator ==(node const& l, node const& r)
{
    return l.data == r.data;
}

Then, given a required node, you can search for the element. This returns an iterator (or a pointer if you're using a plain old array). If you need the index, this requires a little calculation:

auto i = std::find(v.begin(), v.end(), required);
if (i != v.end())
{
    std::cout << i->data << " found at index " << i - v.begin() << std::endl;
}
else
{
    std::cout << "Item not found" << std::endl;
}

Alternative 2:

If creating a node is too expensive or you don't have an equality operator, a better approach would be to use std::find_if(), which takes a predicate (here I use a lambda because it's succinct, but you could use a functor like in this answer):

// Alternative linear search, using a predicate...
auto i = std::find_if(v.begin(), v.end(), [](node const& n){return n.data == 444;});
if (i != v.end())
{
    std::cout << i->data << " found at index " << i - v.begin() << std::endl;
}
else
{
    std::cout << "Item not found" << std::endl;
}

Or is there a quicker way?

Again, it depends. std::find() and std::find_if() run in linear time (O(n)), the same as your for-loop.

That said, using std::find() or std::find_if() won't involve random access or indexing into the container (they use iterators) but they may require a little bit of extra code compared with your for-loop.

Alternative 3:

If running time is critical and your array is sorted (say with std::sort()), you could perform a binary-search, which runs in logarithmic time (O(log n)). std::lower_bound() implements a binary search for the first element that is not less than the given value. It does not take a predicate unfortunately but requires a suitable less-than comparator for your node data type, such as:

bool operator <(node const& l, node const& r)
{
    return l.data < r.data;
}

The invocation is similar to std::find() and returns an iterator, but requires an extra check:

auto i = std::lower_bound(v.begin(), v.end(), required);
if (i != v.end() && i->data == required.data)
{
    std::cout << i->data << " found at index " << i - v.begin() << std::endl;
}
else
{
    std::cout << "Item not found" << std::endl;
}

These functions from the Algorithms Library work with any container supplying an iterator, so switching to another container from std::vector would be quick and easy to test and to maintain.

The decision is yours!

[See a demonstration here.]

Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
0

You should use std::find. You can't get faster than linear complexity (O(n)) if you know nothing about the vector beforehand (like it being sorted).

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • What advantage would be gained from using `std::find()` over a simple `for`-loop in this case? – johnsyweb Feb 16 '13 at 20:56
  • 1
    @Johnsyweb are you asking why a built-in solution is better than re-inventing the wheel? – Luchian Grigore Feb 16 '13 at 20:58
  • 2
    I'm not in favour of wheel reinvention, but I think "you should" warrants qualification in the answer, yes. `std::find()` doesn't return the *index* of the element (which it looked like was required before the question was edited). – johnsyweb Feb 16 '13 at 21:00
  • For the record, I don't, however, think that this answer warrants a down-vote. – johnsyweb Feb 16 '13 at 21:29
0

If you want to find elements in the container then vector is not the right data-structure. You should use an ordered container such as std::set or std::map. Since elements in these containers are kept ordered (sorted), we can find elements in O(log (n)) time as opposed to linear time for unordered containers.

eladidan
  • 2,634
  • 2
  • 26
  • 39
0

Use std::find :

vector<int>::Iterator it = find (vect.begin(), vect.end(), 444);

Note that If you have sorted vector, you can make it faster.

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
Yousf
  • 3,957
  • 3
  • 27
  • 37
0

A neat solution would be to add an extra int index member to the node struct to provide data-to-index mapping when you have an instance of the struct. In such a case, you should probably wrap std::vector in a NodeVector class which will handle the updating of indices when, say, you remove an item (it's enough to subtract 1 from elements' indices which preceed the element being removed in such a case) etc. If the vector doesn't change the number of elements, that's not even an issue. Other than that, if you can't have an instance of the struct grow in size, use std::map. Iterating over the containter to find one item is not very smart, unless you need to do it very rarely and making anything complicated isn't worth the trouble.

neuviemeporte
  • 6,310
  • 10
  • 49
  • 78