0

I have the following task: Given a set of N positive numbers. Write a program which finds the middle element of the set. Input: on the first line there are number of examples, the second line is for the length of the set. The next line is for the numbers of the set separated with space. Something line this:
2 (number of examples)
5 (length)
12 4 22 31 32 (set)
8 (length)
22 33 44 11 55 66 88 99 (set)

Then you have to sort the set and print the middle element. Output:
22 (mid elem of first set)
44 (mid elem of second set)

Constraints: N < 10^11 and each number of set should be < 10^20
So i choose to work directly with strings because of the big numbers. Here is my code:

#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <cstring>
using namespace std;

bool comparer (const string s1, const string s2)
{
   return atoi(s1.c_str()) < atoi(s2.c_str());
}


int main()
{
    int numberOfExamples;
    cin >> numberOfExamples;

    for (int i = 0; i < numberOfExamples; i++)
    {
        long length;
        string input;
        cin >> length;
        string buffer;
        while(getline(cin, input))
        {
            vector<string> v;
            istringstream is(input);
            while (is >> buffer) v.push_back(buffer);

            sort (v.begin(), v.end(), comparer);

            string middle;
            if (v.size() % 2 == 0)
            {
                middle = v[v.size()/2 -1];
            }
            else
            {
                middle = v[v.size()/2];
            }
            cout << middle;
        }

    }
    return 0;
}

And after compiling it stop on the second row for inputing the length of the first example and i got the error of type 0xC00000005. Any help would be appreciated.

Kristian Kamenov
  • 307
  • 1
  • 3
  • 11
  • `while(getline(cin, input))` - fyi that's going to read the rest of your input stream, not just the single line of values for the current iteration; that `while` should be `if`. – WhozCraig Nov 29 '16 at 16:40
  • This seems like homework so you may not be able to, but if you can use the standard library, [`std::nth_element`](http://www.cplusplus.com/reference/algorithm/nth_element/) is what you want. – lcs Nov 29 '16 at 16:40

3 Answers3

1

The algorithm you're trying to implement is:

  • Read number of tests; ignore the rest of the line.
  • For each test
    • read number of test values, ignore the rest of the line
    • read a single line containing test values
    • split line of tests into value vector
    • sort vector
    • find middle element

You're either failing, or just plain not doing, the bold portions above. For example, your code:

cin >> length; 
string buffer;
while(getline(cin, input))

will read in the formatted length as an integer, but the remainder of the current line (which is likely just a newline) is left in the input stream. Therefore the getline consume that, not the line of test values. Worse, that means all the logic inside the for-loop will be working with an empty input line, which means there will be no values stored in the vector. That means when you get here:

if (v.size() % 2 == 0)
{
    middle = v[v.size()/2 -1];
}

v.size() is zero, which means v.size() % 2 is zero, which means your "middle" is now set to v[-1], which is clearly out of bounds.

So the biggest problem is you're not properly consuming remaining line data after formatted input, a common problem for beginning C++ programmers. See this question and related answers.

The second problem, that while (getline(...)) is wrong. that will consume all data until the end of your file. you only want ONE line of data; not all the remaining lines of data. Once you fix the formatted input problem mentioned prior, that while should be if instead.

Code

#include <iostream>
#include <sstream>
#include <algorithm>
#include <vector>
#include <string>
#include <limits>
#include <iterator>

int main()
{
    unsigned int numberOfExamples;
    if (!(std::cin >> numberOfExamples) || numberOfExamples == 0)
        return EXIT_FAILURE;

    while (numberOfExamples-- > 0)
    {
        unsigned int n_tests = 0;
        if (std::cin >> n_tests)
        {
            if (n_tests == 0)
                continue;

            // consume remainder of test lines
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

            // read set of values
            std::string line;
            if (std::getline(std::cin, line))
            {
                std::istringstream iss(line);
                std::vector<int> v {
                    std::istream_iterator<int>(iss),
                    std::istream_iterator<int>() };

                if (v.size() > 0)
                {
                    std::sort(v.begin(), v.end());

                    if (v.size() % 2 == 0)
                        std::cout << v[v.size()/2-1] << '\n';

                    else
                        std::cout << v[v.size()/2] << '\n';
                }
            }
        }
        else
        {
            std::cerr << "Failed to read number of test values\n";
        }
    }
}

Input

2
5
12 4 22 31 32
8
22 33 44 11 55 66 88 99

Output

22
44

See it live

WhozCraig
  • 59,130
  • 9
  • 69
  • 128
0

The biggest issue with your code that I can see is that using atoi in the comparator is not going to work. If the input data is bigger than what and int can hold then atoi will not work as it tries to convert the string to an int.

What you can do though is compare the strings to each other. You have to take the sized into account otherwise 100 would come before 5 but it is not hard to implement. Something like the following should work.

bool comparer (const string& s1, const string& s2)
{
    if (s1.size() != s2.size())
        return s1.size() < s2.size();
    return s1 < s2;
}

Note that I changed s1 and s2 to be references as passing by value will create a lot of copies and kill the performance.

NathanOliver
  • 150,499
  • 26
  • 240
  • 331
0

Thank all of you! I've fixed my code so now its working correctly. Here is the fix

#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <cstring>  
using namespace std;

bool comparer (const string& s1, const string& s2)
{
    if (s1.size() != s2.size())
        return s1.size() < s2.size();
    return s1 < s2;
}


int main()
{
    int numberOfExamples;
    cin >> numberOfExamples;
    cin.ignore();
    vector<string> outputs;

    for (int i = 0; i < numberOfExamples; i++)
    {
        long long length;
        string input;
        cin >> length;
        cin.ignore();
        string buffer;
        if(getline(cin, input))
        {
            vector<string> v;
            istringstream is(input);
            while (is >> buffer) v.push_back(buffer);
            if (v.size() != length)
            {
                cout << "Incorrect length!";
                return 1;
            }

            sort (v.begin(), v.end(), comparer);

            string middle;
            if (v.size() % 2 == 0)
            {
                middle = v[v.size()/2 -1];
            }
            else
            {
                middle = v[v.size()/2];
            }
            outputs.push_back(middle);
        }


    }

    for (vector<string>::iterator it = outputs.begin(); it != outputs.end(); it++)
    {
        cout << *it << endl;
    }
    return 0;
}

Also im pushing the answers in a new vector and when the for loop is over im displaying all of them. And one last question. Should i use cin.ignore() every time i read from the user?

Kristian Kamenov
  • 307
  • 1
  • 3
  • 11