Thread: Problem with a program that detects if an array is sorted or not.

  1. #1
    Registered User
    Join Date
    Sep 2016
    Posts
    10

    Problem with a program that detects if an array is sorted or not.

    This program should detect if an array of values (here 5 values of the user's choice) is sorted and if it's not then it should sort it. The problem is my "detection" function sorts every array and I can't figure out why. The logic looks sound to me. Please help.

    Code:
    #include <iostream>
    
    using namespace std;
    
    int findSmallestValue (int array [], int size, int index)
    {
        int indexSmallest = index;
        for (int i = index + 1; i < size; i++)
        {
            if (array [i] < array [indexSmallest])
            {
                indexSmallest = i;
            }
        }
        return indexSmallest; // finds under what index is the smallest value
    }
    
    int swap (int array [], int first_index, int second_index)
    {
        int temp = array [first_index];
        array [first_index] = array [second_index];
        array [second_index] = temp;
    }
    
    void InsertionSort (int array [], int size)
    {
        for (int i = 0; i < size; i++)
        {
            int index = findSmallestValue(array, size, i);
            swap (array, i, index);
        } // This function does the sorting
    }
    
    void DisplayArray (int array [], int size)
    { // displays the array nicely
        cout << "{";
        for (int i = 0; i < size; i++)
        {
            if (i != 0)
            {
                cout << ",";
            }
            cout << array [i];
        }
        cout << "} \n";
    }
    
    bool IsSorted (int array [], int size)
    { // should detect is an array is sorted or not
        int index;
        for (int i = 0; i < size; i++)
        {
            index = findSmallestValue(array, size, i);
        }
        if (index != 0)
        {
            return false;
        }
        else if (index = 0)
        {
            for (int i = index + 1; i < size; i++)
            {
                index = findSmallestValue(array, size, i);
                if (index = i)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }
    
    int main()
    {
        int Values [5];
        cout << "Enter 5 values of your choice: \n";
        for (int i = 0; i < 5; i++)
        {
            cin >> Values [i];
        }
        cout << "Your array looks like this: ";
        DisplayArray(Values, 5);
        if (IsSorted(Values, 5))
        {
            cout << "Your array is already nicely sorted. \n";
        }
        else
        {
            cout << "Your array isn't sorted, let me help you: \n";
            InsertionSort(Values, 5);
            DisplayArray (Values, 5);
        }
    }

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    In IsSorted, this is a mistake:
    Code:
    else if (index = 0)
    You probably wanted to write:
    Code:
    else if (index == 0)
    but then it would have been even simpler to write:
    Code:
    else
    You make a similiar mistake in the loop, but more importantly, your loop will terminate after at most one iteration since the loop body either returns true or false without any chance of going to the next iteration.

    Personally, I would write just one loop and not call findSmallestValue. This loop will loop from the first element to the second last element, checking that the current element is not greater than the next element. Once you find this condition to be false, you know the array is not sorted, but if you end the loop without ever violating this condition, then the array is sorted.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Sep 2016
    Posts
    10
    Thank you for your answer, I took your advice and made one loop without calling the function findSmallest, just making sure the condition of the next element being smaller is respected. It works like a charm. I was overcomplicating, thanks a lot

  4. #4
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    I don't know if this is helpful or not but for future reference there's also the is_sorted function.

  5. #5
    Registered User
    Join Date
    Sep 2016
    Posts
    10
    Thank you for your answer but seeing as how I'm a beginner at this, I'm trying to work out the code by myself to better my skills and understanding

  6. #6
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    Fair enough. You know something is sorted if for ever iteration, the next element is equal to or greater than the current.

    Code:
    template <typename InputIterator>
    auto is_sorted(InputIterator const begin, InputIterator const end) -> bool
    {
      for (; begin != end; ++begin) {
        auto const tmp = begin;
        auto const next_val = *(++tmp);
    
        auto const curr_val = *begin;
        if (curr_val > next_val) {
          return false;
        }
      }
    
      return true;
    }
    This isn't tested but I think it should work. Note, it really just relies on duck typing so if you pass it the wrong thing, the error messages aren't that great.

  7. #7
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Don't post untested code! There are multiple problems with it. Your const fetish is causing some problems, but you're also accessing past the end of the array.

    This seems to work:
    Code:
    #include <iostream>
    
    template <typename InputIterator>
    bool is_sorted(InputIterator begin, InputIterator end) {
        for (; begin != end - 1; ++begin)
            if (*begin > *(begin + 1))
                return false;
        return true;
    }
    
    int main() {
        int a[] = {2, 4, 5, 7, 8, 8, 9};
        std::cout << is_sorted(a, a + sizeof a/sizeof*a) << '\n';
    }
    Last edited by algorism; 10-15-2016 at 09:42 AM.

  8. #8
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    I think your stuff violates InputIterator. As far as I know, any `+ n` operation on input iterator makes it random access iterator. This is written in terms of pure input iterator: C++ Shell

    Code:
    // Example program
    #include <iostream>
    #include <cassert>
    #include <list>
    
    
    template <typename InputIterator>
    auto is_sorted(InputIterator begin, InputIterator end) -> bool
    {
        for (auto curr = begin; curr != end; ++curr) {
            auto next = curr;
            ++next;
            
            if (next == end) {
                break;
            }
            
            if (*curr > *next) {
                return false;
            }
        }
        
        return  true;
    }
    
    
    using std::list;
    
    
    int main(void)
    {
        int data[3] = { 2, 0, 1 };
        int sorted_data[3] = { 0, 1, 2 };
        
        assert(is_sorted(data, data + 3) == false);
        assert(is_sorted(sorted_data, sorted_data + 3) == true);
        
        list<int> l;
        l.push_back(0);
        l.push_back(1);
        l.push_back(2);
        
        assert(is_sorted(l.begin(), l.end()) == true);
        
        std::cout << "Tests passed!\n";
        return 0;
    }
    Last edited by MutantJohn; 10-15-2016 at 12:51 PM.

  9. #9
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    If you really want it to work with input iterators, wouldn't it have to be more like this?
    Code:
    #include <iostream>
    #include <iterator>
    
    template <typename InputIterator>
    bool is_sorted(InputIterator begin, InputIterator end) {
        if (begin == end) return true;
        auto prev_val = *begin;
        for (++begin; begin != end; ++begin) {
            auto curr_val = *begin;
            if (prev_val > curr_val)
                return false;
            prev_val = curr_val;
        }
        return true;
    }
    
    int main() {
        std::istream_iterator<int> is_it(std::cin), is_end;
        std::cout<< is_sorted(is_it, is_end)<< '\n';
    }
    The point is that every time you dereference the iterator it reads another value from the associated input stream.

  10. #10
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    First, good catch on the 0-length case! I've actually never messed much with `istream_iterator` struff. I tried messing with it in the cpp.sh shell but for some reason it doesn't like me trying to send the EOF signal (kept bookmarking it in chrome lol). Was my code not working for your particular instance? To me they look largely logically equivalent.

    Also, how cool is this? We refactored software together and we wound up making it better!

  11. #11
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Quote Originally Posted by MutantJohn View Post
    Was my code not working for your particular instance? To me they look largely logically equivalent.
    The difference is that you read two values from the stream every iteration (*curr reads a value and *next reads a value). So when I run your program on the following file, it says it's in order since each pair of numbers is in order.
    Code:
    6
    7
    4
    5
    8
    9
    1
    2
    I don't know whether *curr is guaranteed to happen before *next in the expression *curr > *next (anybody?), but either way it's a problem.
    I've actually never messed much with `istream_iterator` stuff. ... Also, how cool is this? We refactored software together and we wound up making it better!
    I'm no expert either, that's for sure! I've definitely learned something with this back-and-forth coding exercise.

  12. #12
    Nasal Demon Xupicor's Avatar
    Join Date
    Sep 2010
    Location
    Poland
    Posts
    179
    I think the issue here is that InputIterator doesn't offer the multi-pass guarantee like a ForwardIterator would have, thus MutantJohn's function would work just fine with an array, a std::vector or a std::list, but fails with std::istream_iterator.
    That's because with InputIterator:
    Code:
    auto next = curr; // next points to the same stuff curr does
    ++next; // by now curr is not guaranteed to be valid!
    24.2.3 Input iterators [input.iterators]
    [...] Table 114 — Input iterator requirements (in addition to Iterator) [...]
    ++r [...] any copies of the previous value of r are no longer required either to be dereferenceable or to be in the domain of ==.
    [...]
    3 [ Note: For input iterators, a == b does not imply ++a == ++b. (Equality does not guarantee the substitution property or referential transparency.) Algorithms on input iterators should never attempt to pass through the same iterator twice. They should be single pass algorithms. Value type T is not required to be a CopyAssignable type (Table 23). These algorithms can be used with istreams as the source of the input data through the istream_iterator class template. — end note ]

    Still, it happens to work with std::is_sorted even though it's intended to work with ForwardIterator... Let's see how it's implemented in GCC: https://gcc.gnu.org/onlinedocs/gcc-6...ce.html#l03224
    Code:
     3224   template<typename _ForwardIterator, typename _Compare>
     3225     _ForwardIterator
     3226     __is_sorted_until(_ForwardIterator __first, _ForwardIterator __last,
     3227                       _Compare __comp)
     3228     {
     3229       if (__first == __last)
     3230         return __last;
     3231 
     3232       _ForwardIterator __next = __first;
     3233       for (++__next; __next != __last; __first = __next, (void)++__next)
     3234         if (__comp(__next, __first))
     3235           return __next;
     3236       return __next;
     3237     }
    It looks like __first is not guaranteed to be valid right after ++__next is executed (when we use InputIterator. It's of course valid with intended ForwardIterator.). Seems like it works by accident, on my box at least, but algorism's version would actually work by design. Of course - since std::is_sorted assumes ForwardIterator we shouldn't try to use it with InputIterator anyway - but it sure can be surprising to discover this.


    @algorism - the order of evaluation here is unspecified as far as I can tell, but it shouldn't matter anyway. (edit-- scratch the rest, I think I misread you. ; ) )
    Last edited by Xupicor; 10-16-2016 at 03:00 PM.

  13. #13
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    2,665
    I _knew_ there were iterator concepts!

    Also, my bad. I didn't even realize that the "real" is_sorted was written for ForwardIterator.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to check if an array of doubles is sorted?
    By Lina_inverse in forum C Programming
    Replies: 20
    Last Post: 10-06-2012, 03:12 PM
  2. How to detect if a array is sorted.
    By just_rookie in forum C++ Programming
    Replies: 14
    Last Post: 09-10-2012, 05:35 AM
  3. While loop that detects too many characters
    By breakboye in forum C Programming
    Replies: 3
    Last Post: 01-24-2012, 05:15 PM
  4. how delete 1th element of a sorted array
    By vicky_4040 in forum C Programming
    Replies: 4
    Last Post: 10-11-2009, 06:12 AM
  5. finding max/min in a sorted array
    By Crcullen3916 in forum C++ Programming
    Replies: 9
    Last Post: 09-23-2008, 02:18 AM

Tags for this Thread