4

I have a vector of pairs. Suppose it is like this:

vector<pair<int,int>> vec = { {1,12}, {1,5}, {1,6}, {1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };

The pairs are sorted by first element.

Given a pair, I need to find the index of the last pair in the vector whose first element is less than or equal to first element of the given pair. If for that last pair, other pairs lie to its left with the same value of the first element, I need the first of all those pairs:

<4,10> => 4 (vec[4] is <3,9>, the elements with the largest first value less than or equal to 4 are those with first element as 3, and there are 4 pairs with a 3 in the first element, at indices 4-7, so return the first of those pairs)

<0,10> => -1, since no element exists to its right.

<1,6> => 0 (vec[0] is <1,12>. There is no pair whose first element is less than 1, and there are 4 pairs, including <1,6> whose first element is 1. So we need the first of these 4 pairs.)

<23,81> => 12 (vec[12] is <20,8>)

Condition: I need to use only standard algorithms like upper_bound, binary_search and lower_bound. I tried this, but it is failing badly:

vector<pair<int,int>> vec = { {1,12}, {1,5}, {1,6},{1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };

auto i = std::lower_bound(vec.begin(), vec.end(), make_pair<int,int>(4,10),
    [](const pair<int,int>& f1, const pair<int,int>& f2) { return f1.first < f2.first; });

cout << i-vec.begin();
gsamaras
  • 71,951
  • 46
  • 188
  • 305
SexyBeast
  • 7,913
  • 28
  • 108
  • 196

2 Answers2

7

Since you want the first pair, you maybe want to combine lower and upper bound?

#include <algorithm>
#include <vector>
#include <utility>
#include <iostream>

using namespace std;

int main()
{
    vector<pair <int,int> > vec = { {1,12}, {1,5}, {1,6}, {1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };

    auto u_it = std::upper_bound(vec.begin(), vec.end(), make_pair<int,int>(4, 10),
        [](const pair<int,int>& f1, const pair<int,int>& f2) { return f1.first < f2.first; });

    if(u_it == vec.begin())
        cout << "-1\n";

    auto l_it = std::lower_bound(vec.begin(), u_it, *prev(u_it),
        [](const pair<int,int>& f1, const pair<int,int>& f2) { return f1.first < f2.first; });

    cout << l_it - vec.begin() << "\n";
    return 0;

}

Output:

Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall -std=c++0x main.cpp 
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out 
4

PS - answer updated after WhozCraig's comment:

you want to use it = std::upper_bound(beg,end) to find the first strictly-greater element, and if that answers non-begin, then use std::lower_bound(beg,it) to find the lowest-matching ordinal of the element whose value is pulled from (it-1).

The answer now satisfies all the test cases you have provided (I am not showing it here). Hope that helps! :)


Appendix:

Ref for std::lower_bound, std::upper_bound and std::prev. Please notice how the std::lower_bound call uses std::make_pair without an initializing list, so that it lets the compiler come into play and resolve deduce the type.

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • `(4 - 1,10)` ? What is that? – SexyBeast Nov 22 '16 at 17:23
  • @SexyBeast I updated the answer, hope now everything is clear! – gsamaras Nov 22 '16 at 18:19
  • 1
    This is a terrific answer! Thanks buddy! I will give it a bounty in some time! While you are at it, the vector initialization code has been cropped off with a dollar sign! – SexyBeast Nov 22 '16 at 18:23
  • @SexyBeast good it helped! Well to be honest I was about to drop off and feel good for answer [another question](http://stackoverflow.com/questions/40748090/generating-random-number-on-set-interval-in-c/40748148#40748148). It was only for you and WhozCraig's comments to get back into the game! Just accept the answer, no need to give away your rep, I already answered! :) You may needed for desperate times when a question that you die to find an answer is left unanswered after a while... ;) – gsamaras Nov 22 '16 at 18:27
  • Off topic question, instead of using `make_pair` in the comparator function, can't I just use an int, like 4 here to represent the first value only, since the second value is redundant, and use that in the function somehow? – SexyBeast Nov 22 '16 at 18:35
  • There must be a way and I was thinking of the very same thing to be honest! However, it's not as simple as just changing the comparator functions and the `val` of the bounds. We are passing a vector of pairs as input, which complicates things @SexyBeast. maybe it would be sane to post a new question, linking to this one and my answer, asking how to! – gsamaras Nov 22 '16 at 18:41
  • That's a reasonable halfway solution @SexyBeast, I agree! ;) – gsamaras Nov 22 '16 at 18:44
  • Yes, precisely! – SexyBeast Nov 22 '16 at 18:44
  • Umm, no, I just added a `{0,1}` at the beginning and searched for it, I am getting `0`. – SexyBeast Nov 22 '16 at 19:41
  • @SexyBeast well danged if it does! My mistake, I'll delete my comment. P.S. Why `make_pair(prev(u_it)->first, 10)` instead of just `*prev(u_it)`? – Mark Ransom Nov 22 '16 at 19:47
  • SexyBeast is right, the code works for that case too! Just checked! @MarkRansom I was having some compiler issues and overkilled it, updated, thanks! :) – gsamaras Nov 22 '16 at 19:59
1

std::lower_bound returns the first item greater or equal to the given value


you need to

  • +1 to the input of std::lower_bound
  • -1 to the result of std::lower_bound

to find the value (or you can use std::upper_bound)

and use std::lower_bound again to find the right pair


example

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

int my_find(const vector<pair<int,int>>& vec, int value){
   auto comparer = [](const pair<int,int>& f1, int value) { return f1.first < value; };

   auto i = std::lower_bound(vec.begin(), vec.end(), value+1, comparer);
   if(i==vec.begin()){return -1;}

   i = std::lower_bound(vec.begin(), vec.end(), (i-1)->first, comparer);
   return i-vec.begin();
}

int main(){

   vector<pair<int,int>> vec = { {1,12}, {1,5}, {1,6},{1,9}, {3,9}, {3,11}, {3,13}, {3,4}, {5,9}, {5,91}, {13,8}, {16,8}, {20,8}, {20,81} };

   cout << my_find(vec,-1) << '\n';
   cout << my_find(vec,3) << '\n';
   cout << my_find(vec,10) << '\n';
   cout << my_find(vec,100) << '\n';
}

BTW, you don't need to provide pair to lower_bound

and if you only use lower_bound, you need only one comparer

apple apple
  • 10,292
  • 2
  • 16
  • 36
  • And since we are looking for the *last* item, we should use `std::upper_bound()` and check if `it-1` is even valid. – Nico Schertler Nov 22 '16 at 17:08
  • Can you give me a working example, with the comparator function? I am having a bit of difficulty in visualizing this.. :( – SexyBeast Nov 22 '16 at 17:10
  • @SexyBeast I suppose you `make_pair(4,10)` because you need to find last element of `3` BTW – apple apple Nov 22 '16 at 17:12
  • 1
    Subtracting 1 from `i` does not work, gives me wrong results. I get `7`, which is the index of the last `3`, whereas I want `4`, the index of the first `3`. – SexyBeast Nov 22 '16 at 17:13
  • @SexyBeast it looks [pretty good](http://melpon.org/wandbox/permlink/jvtJbWupEHxgB0AJ) to me – apple apple Nov 22 '16 at 17:16
  • First, `lower_bound` returns the first *greater than or equal*, not greater than. Second, subtracting one from an iterator without first checking to see if it's equal to `begin()` is an error. – Mark Ransom Nov 22 '16 at 17:19
  • @MarkRansom so I `+1` to it's input, do you look at my [previous working example](http://melpon.org/wandbox/permlink/jvtJbWupEHxgB0AJ)? For the equal to `begin()` it will only equal to `begin()` if the set is empty. and is not the core of this solution. – apple apple Nov 22 '16 at 17:21
  • @appleapple, please find that in the next line I wrote **If for that last pair, other pairs lie to its left with the same value of the first element, I need the first of all those pairs**. – SexyBeast Nov 22 '16 at 17:24
  • Which doesn't apply to the test case for `<1,6>` you provided. – Simon Kraemer Nov 22 '16 at 17:27
  • @SimonKraemer it returns `3(<1,9>)` which is right, are you get different result? [wandbox](http://melpon.org/wandbox/permlink/XUpKuvBFur1eIr5Q) – apple apple Nov 22 '16 at 17:30
  • Edited my question, sorry for a mistake, for `<1,6>` the answer would be `0` and not `1`. – SexyBeast Nov 22 '16 at 17:33
  • @appleapple As far as I understand the question it shoudl return `2` for the elements `<1,12>` and `<1,5>` which both lie left to the first value greater or equal to `<1,6>` (which is `<1,6>`) and have the same value in the first part (which is `1`) – Simon Kraemer Nov 22 '16 at 17:35
  • @SimonKraemer Answer updated, is this what you want? – apple apple Nov 22 '16 at 17:39
  • @MarkRansom sorry, you are right, I was confused by the two, answer updated, thankyou. – apple apple Nov 22 '16 at 18:33
  • The answer is accurate now, whoever downvoted this, please upvote it now. This required lot of hard work. – SexyBeast Nov 22 '16 at 18:39