2

I am trying to check if a vector is ordered using the divide and conquer algorithm, here's the code I wrote so far:

#include <iostream>
#include <vector>

using namespace std;

bool isOrdered(std::vector <int> v, int left, int right)
{
int mid = (left + right)/2;

if (left == right){
    if (left == 0)
        return true;
    else
        return v[left-1] <= v[left];
}
else if (left + 1 == right)
{
    if (v[left] <= v[right])
        return true;
    else
        return false;
}
else if (left > right)
{
    if (v[left] > v[right])
        return true;
    else
        return false;
}
else
{
    return isOrdered(v, left, mid) && isOrdered(v, mid+1, right);
}
}

int main()
{
std::vector <int> v = {2, 2, 3, 2, 2};
cout << isOrdered(v, 0, v.size() - 1);
return 0;
}

It can't seem to work for certain cases and while debugging I kept adding specific base cases to make it work for one input but that would not make it work for another input, and I kept doing that until I realized I had an algorithmic error. I basically thought of it like this: Divide the vector up into sub-vectors and if all sub-vectors are ordered then the whole vector is ordered. However this approach very quickly breaks down. If the input length is not a power of 2 then it will eventually break it down into certain sub-vectors of length 1 which will always be ordered. For example what if the input is 2 2 3 2 2? The sub-vectors are {2, 2}, {3} and {2, 2} and all of them are ordered but the whole vector is not.

So how should I think this problem instead? I tried to make it work for sub-vectors of length 1 by adding that return v[left-1] <= v[left]; line but it still breaks down.

Lastrevio2
  • 51
  • 6
  • You need to stop once you have groups of 2 so all groups overlap. That would be `{2,2,3,2,2}` should recurse down into the groups `{2,2}, {2,3}, {3,2}, {2,2}`. – NathanOliver Dec 02 '19 at 14:30
  • 1
    BTW, pass your vector by const reference instead of by copy. – Jarod42 Dec 02 '19 at 14:32
  • @NathanOliver-ReinstateMonica I thought of that as well.. Not sure if it counts as divide and conquer, but either way how would I even go about doing that recursively? – Lastrevio2 Dec 02 '19 at 14:49
  • @Lastrevio2 Just stop the recursion when `right - left == 1`. Also, `(left + right)/2` has the potential to overflow. To fix that see this: https://stackoverflow.com/questions/24317360/midpoint-formula-overflow-error – NathanOliver Dec 02 '19 at 15:04
  • @NathanOliver-ReinstateMonica: Potential issue only when we use `signed` index ;-) (to feed war between signed/unsigned indexes ^_^ ) – Jarod42 Dec 02 '19 at 15:12
  • @Jarod42 It's still an issue with unsigned indexes, it's just a defined issue then ;-) – NathanOliver Dec 02 '19 at 15:15

3 Answers3

2

Beginning with recursion:

range is ordered if both subranges is ordered and if last item of "low" range is lower than first item of "high" range:

return isOrdered(v, left, mid - 1) && isOrdered(v, mid, right) && v[mid - 1] <= v[mid];

Remains the stop condition: when range is empty (cannot happen from argument) or has only one element. Those are ordered ranges.

So we got:

bool isOrdered(const std::vector<int>& v, std::size_t left, std::size_t right)
{
    if (left == right) { // Only one element
        return true;
    } else {
        const auto mid = (left + right + 1) / 2;
        return v[mid - 1] <= v[mid]
            && isOrdered(v, left, mid - 1)
            && isOrdered(v, mid, right);
    }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • "range is ordered if both subranges is ordered and if last item of "low" range is lower than "high" range:" Ahhh, I see! If both are ordered then the highest element is well the last, and the lowest is the first, so if last item of low range is lower than first item of high range then the whole thing is ordered. It starts to make sense now. – Lastrevio2 Dec 02 '19 at 15:11
  • Also, what I find interesting is that you wrote std::size_t. I know it's unique to C++ and doesn't come in C but I didn't know that you had to write std:: for it (I'm using C:B which excuses you out of writing std:: sometimes even if you did not say ````using namespace std;```, like I can just say string instead of std::string on code blocks without using namespace std), so I didn't know if that's the case for size_t as well, so I assume that in a more normal compiler/editor you'd have to put std:: in front of size_t for it to work? (or use namespace std) – Lastrevio2 Dec 02 '19 at 15:12
  • @Lastrevio2 Since C++ has namespace C++ puts `size_t` inside of the `std` namespace. To be 100% portable you need `std::size_t` or at least `using std::size_t;`. Often times you can get away without it since the C definition is pulled in as well, but why tempt fate? – NathanOliver Dec 02 '19 at 15:20
  • @Lastrevio2: `size_t` (and some other type/typedef) can also come from C-header (which don't have `namespace std`). `std::string` is not one of them though. (ADL allows to omit `std::` for function call too in some circumstance). – Jarod42 Dec 02 '19 at 15:21
1

Why do you even use this "difficult" algorithm? In order to check if a vector is ordened, every member (v[i]) can't be larger than the next (v[i+1]).

The "divide-and-conquer" algorithm is more useful, for instance, to find something in an already ordered vector, but for checking whether or not a vector is ordered, a simple linear algorithm is much better (because of readability).

Dominique
  • 16,450
  • 15
  • 56
  • 112
  • 2
    This is not an answer. – acraig5075 Dec 02 '19 at 14:47
  • 1
    It's a homework exercise... Of course I'm not using this for an actual project but I'm trying to learn divide and conquer in general, how else am I going to learn if not by putting it into practice? – Lastrevio2 Dec 02 '19 at 14:48
  • @Lastrevio2: as a homework exercise or as a learning experience, it's ok. I just meant don't use this in a real-life situation (e.g. at work, in a programming job). – Dominique Dec 02 '19 at 15:26
0
#include <iostream>
#include <vector>

using namespace std;

bool isOrdered(const vector <int> &v, int left, int right) {
    int mid = (left + right)/2;
    if (left == right)
        return true;
    else
        return v[mid]<=v[mid+1] && isOrdered(v, left, mid) && isOrdered(v, mid+1, right);
}

int main()
{
    vector <int> v = {2, 2, 3, 2, 2};
    cout << isOrdered(v, 0, v.size() - 1);
    return 0;
}
alan23273850
  • 232
  • 2
  • 6
  • While this code may solve the problem the answer would be a lot better with an explanation on how/why it does. Remember that your answer is not just for the user that asked the question but also for all the other people that find it. Also note that `int mid = (left + right)/2;` is unsafe as it can overflow. – NathanOliver Dec 02 '19 at 15:05
  • this is an implementation derived from @Jarod42 – alan23273850 Dec 02 '19 at 15:05
  • You should still document how/why it works. Comments are "temporary" and can be deleted at any time. Your answer should stand on it's own feet and not rely on any outside information. – NathanOliver Dec 02 '19 at 15:16