views:

328

answers:

3

If a std::set or std::list contains a sequence of natural numbers (1, 2, 3..). would there be a function in standard library to find the missing number?

+3  A: 

None specifically for that purpose (the standard algorithms try to be a bit more generalized). You could, however, use std::accumulate for one fairly large piece of the work.

Hint: the sum of a set of numbers from 1..N is (N+1)*(N/2).

Edit: my idea of an answer is to subtract the sum of you the numbers you have from the sum you'd get if all the numbers were present. That difference will be the missing number.

#include <numeric>
#include <iostream>

#define elements(x) (sizeof(x)/sizeof(x[0]))

int main() { 
    int x[] = {8, 4, 3, 5, 1, 2, 7}; 


    int N = elements(x) +1;

    int projected_sum = (N+1)*(N/2);
    int actual_sum = std::accumulate(x, x+elements(x), 0);

    std::cout << "The missing number is: " << projected_sum - actual_sum << "\n";
    return 0;
}

For the moment, this ignores a few minor details like what happens if the set has an odd number of elements, but should show the general idea. I've also used an array instead of a list, simply for ease of initialization. I haven't used anything like random access that a list can't (reasonably) support. This has linear complexity, and as you can see from the sample input, doesn't require the input to be sorted.

Edit2: if the intent is that the input is in sorted order, I'd probably do the job a bit differently: just walk through and find an item that isn't exactly one greater than its predecessor.

Jerry Coffin
How does that help you find the missing number? That property also holds for a list with N-1 zeros and the last element as (N+1)*(N/2).
jon hanson
@jon: At least as I understand it, he's saying the input *will* be a list of numbers in sequence with exactly one of them missing.
Jerry Coffin
@Jerry Coffin +1 for math, although it will do extra work if the mismatch is early in the sequence.. but it will work if the sequence is unordered.
Cubbi
@Cubbi: yes, for data stored in a `set` (which is ordered by nature) you (at least theoretically) get better results with something like `std::mismatch`, which will traverse half the items on average, where this always traverses all items.
Jerry Coffin
+6  A: 

You could std::mismatch() with a sequence of natural numbers. To save space, I think boost::counting_iterator works wonders here:

#include <iostream>
#include <list>
#include <boost/iterator/counting_iterator.hpp>
int main()
{
    std::list<int> l = {1,2,3,4,6,7,8,9};
    auto i = mismatch(l.begin(), l.end(), boost::counting_iterator<int>(1));
    if(i.first==l.end())
            std::cout << "no missing numbers\n";
    else
            std::cout << "the first missing number is " << *i.second << '\n';

}

test run:

~ $ g++ -std=c++0x -pedantic -Wall -Wextra -o test test.cc
~ $ ./test
the first missing number is 5
Cubbi
Beat me to it. :) You can construct the counting_iterator with `l.front()` to generalize. The same idea works for `std::set` too. You can use `counting_iterator<int>(*s.begin())`.
Matthew Flaschen
Excellent answer!
Pukku
@Matthew Flaschen I was going to write *l.begin(), but then I thought: what if 1 is the missing number?
Cubbi
@Cubbi, if there's no 1, it's a sequence of consecutive natural numbers (possibly with missing element) starting with 2.
Matthew Flaschen
For `std::mismatch` to work, the numbers need to be sorted though. That's fine for `std::set`, but neither specified nor necessarily true for `std::list`.
Jerry Coffin
@Jerry Coffin I took "std::list contains a sequence of natural numbers" to mean "ordered sequence". So many ways to find ambiguities in such as small problem statement!
Cubbi
what's an 'auto' in this case?
Dave18
@Cubbi: Yup -- getting a really clear problem statement is almost never trivial.
Jerry Coffin
@Dave18: in this case, `auto` would work out to `int`, because `std::mismatch` returns `InIt::value_type`, which (in this case) will work out to `std::list<int>::value_type`, which is `int`.
Jerry Coffin
I had an error 'C2440: 'initializing' : cannot convert from 'std::pair<_Ty1,_Ty2>' to 'int'
Dave18
@Dave18: `auto` is the C++0x keyword for automatic type deduction, which saved me from having to type `std::pair<std::list<int>::const_iterator, boost::counting_iterator<int> > i = ...`
Cubbi
Seeing as it printed out the first missing number, would it be able to find the second missing if there was any?
Dave18
@Dave18 You can `mismatch(i.first, l.end(), ++i.second);` after that, although if what you *really* wanted was a list of all mismatches, then `std::set_difference()` would create it -- see Stephen Chu's answer.
Cubbi
+4  A: 

You can find all missing numbers using set_difference and a custom iterator:

class seqIter : public std::iterator<std::input_iterator_tag, int> {
public:
    seqIter(int n) : num(n) {}
    seqIter(const seqIter & n) : num(n.num) {}
    int & operator *() {return num;}
    seqIter & operator ++() { ++num; return *this; }
    bool operator !=(const seqIter & n) { return n.num != num; }
private:
    int num;
};

int main(int argc, char *argv[])
{
    int n[] = { 1, 3, 4, 7, 10 };
    std::set<int>   numbers(n, n + sizeof(n)/sizeof(n[0]));
    std::set<int>   missing;
    std::set_difference(    seqIter(*numbers.begin()+1), seqIter(*numbers.rbegin()), 
                            numbers.begin(), numbers.end(),
                            std::insert_iterator<std::set<int> >(missing, missing.begin())
                        );
}

It's probably not any faster than going thru the numbers with a for loop though.

Stephen Chu
+1 for being a real man and sticking to C++98!
Cubbi
Well. That's what happens when you got stuck with ancient IDE and tools. :)
Stephen Chu