3
\$\begingroup\$

I have this simple implementation for array sorting:

#include <iostream>
#include <stdlib.h>

using namespace std;

// some magic for the sorter
int compare(const void *i, const void *j) {
    int a = *(int *) i, b = *(int *) j;
    return a < b ? -1 : a > b ? 1 : 0;
}

int main() {
    int arr[100], temp = 0, count = 0;

    // reads values into the array (assumed integers as input)
    while (cin >> arr[count++]);
    count--;

    // what sorts the array
    qsort(arr, count, sizeof(int), compare);

    // prints everything but duplicate elements
    for (int i = 0; i < count; i++) {
        bool match = false;
        for (int j = 0; j < i && !match; j++)
            if (arr[i] == arr[j])
                match = true;
        if (!match)
            cout << arr[i];
    }
}

How can it be improved? I'd like to also remove duplicate elements rather than just silently ignoring them too.

\$\endgroup\$
  • 4
    \$\begingroup\$ std::sort(arr, arr+count); with std::sort from the header <algorithm>. It works with any container of any comparable value. \$\endgroup\$ – Morwenn Mar 25 '15 at 16:49
  • 1
    \$\begingroup\$ Then std::unique(arr, arr+count) to get rid of the duplicate elements. \$\endgroup\$ – Morwenn Mar 25 '15 at 16:51
  • 1
    \$\begingroup\$ Your test for uniqueness is very inefficient. Why not compare arr[i] with arr[i+1] and skip the sequence if equal? Keep trying to do things the hard way, you'll learn more this way. \$\endgroup\$ – chqrlie Mar 25 '15 at 21:08
  • \$\begingroup\$ The return statement in compare() can be simplified as: return (a > b) - (a < b); \$\endgroup\$ – chqrlie Mar 25 '15 at 21:11
  • 1
    \$\begingroup\$ Prefer the C++ std::sort much less chance to mess things up and the compiler can check all the type information so no need to use those nasty C cast operations. \$\endgroup\$ – Martin York Mar 25 '15 at 22:28
8
\$\begingroup\$

You should use your standard libraries when you can. Consider that a std::set already does all the hard work, unique, and sorted values:

#include <iostream>
#include <set>

int main() {
    std::set<int> data;

    // reads values into the set (assumed integers as input)
    int val;
    while (std::cin >> val) {
        data.insert(val);
    }

    for(std::set<int>::iterator iter = data.begin(); iter != data.end(); ++iter) {
        std::cout << (*iter) << "\n";
    }

}

Note though, that the above implementation has horrible handling of issues that may come from invalid input. Still, the entire implementation is significantly simpler than yours.

\$\endgroup\$
  • \$\begingroup\$ Yes, but as I've said in my code, I'm not expecting anything other integers. If I end up getting something else, there's something that is significantly wrong about where I'm reading from. Thanks a lot! (My new version based off of this is now in the original post) \$\endgroup\$ – T145 Mar 25 '15 at 20:07
  • 1
    \$\begingroup\$ Please see What you may and may not do after receiving answers \$\endgroup\$ – rolfl Mar 25 '15 at 20:09
  • \$\begingroup\$ You're really welcome. Pleased it made sense. \$\endgroup\$ – rolfl Mar 25 '15 at 20:09
6
\$\begingroup\$

This would just be a comment but it doesn't all fit...

  1. In compare, you could simply return a-b instead of doing 2 compares.
  2. The print can be done with one pass over the array (O(N)) instead of O(N^2):

...

int i = 0;
int value;
while (i < count) {
    value = arr[i];
    i++;
    while (i < count && value == arr[i]) {
        i++;
    }
    cout << value;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Agree. Also the input loop should test that count<100, and 100 should be a named constant. \$\endgroup\$ – AShelly Mar 25 '15 at 17:34
  • 3
    \$\begingroup\$ Absolutely not! return a-b does not do the job!!! INT_MIN < 1 yet INT_MIN-1 invokes undefined behaviour (at least in C it does) and on CPUs with 2s complement arithmetic, INT_MIN-1 is INT_MAX, a positive value. The trick can only be used with arithmetic types smaller than int. \$\endgroup\$ – chqrlie Mar 25 '15 at 21:24
5
\$\begingroup\$

100 is a magic number, it should be a constant, let's call it MAX

qsort is the old c way to sort a collection, the c++ way would be to use std::sort. You're using c++11, so you could even replace your compare function with a lambda expression, so your sorting would looks like that.

sort(arr, arr + MAX, [](int a, int b){ return a < b; });

In that case, you don't really need the lambda expression, because std::sort use the '<' operator by default, so your actual call is

sort(arr, arr + MAX);

You should prefer to use the standard containers such as std::set (which, like rolfl said, does exactly what you want) or std::vector, because it handle so much errors for you (If the use enters more than 100 values, your program wouldn't be valid) and is easier to use.

To find the unique values, you can use std::unique. It need the data to be sorted so you have to call std::sort first With an array, you will need to find the new size, so you can do it like that. If you have trouble understanding that line, please refer to this question

int size = unique(arr, arr + MAX) - arr;

The nice thing about the standard library is that it have a function for nearly everything, including generating values for a collection. So in your case, if you know that you will always wants 100 values, why don't use the std::generate_n function?

int temp;
generate_n(arr, MAX, [&](){cin >> temp; return temp; });
\$\endgroup\$
  • \$\begingroup\$ generate_n -> that's pretty neat. lambda syntax and everything... As someone who does C++ about once every couple years I wish there was better ways to discover all these massive libraries. \$\endgroup\$ – Bill Barry Mar 25 '15 at 21:01
  • \$\begingroup\$ Myself likewise :) \$\endgroup\$ – T145 Mar 26 '15 at 12:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.