简体   繁体   中英

std::map lower_bound is not returning the correct value

Good afternoon, I am trying to use the std::map lower_found member function. However , it keeps returning the wrong answer. Here is an excerpt of my test code. Please explain to me how to make std::map lower bound function properly. Thank you.

class Interval { 
     public:   
         explicit Interval(int item){ 
            mLow = item;
            mHigh = item;
            mStamp = 0;
         }
         Interval(int low, int high, int stamp = 0){ 
            mLow = low;
            mHigh = high;
            mStamp = stamp;

         }
         Interval(void){  
            mLow = 0;
            mHigh = 0;
            mStamp = 0;

         }

         Interval(const Interval& r):
            mLow(r.mLow),
            mHigh(r.mHigh),
            mStamp(r.mStamp)
         {

         }

         bool operator<(const Interval& rhs) const{     
             if (mLow < rhs.mLow){             
                 return true;     
             }     
             return false;   
         } // operator<    
         int low() const { return mLow; }   
         int high() const { return mHigh; }
         int getStamp() const { return mStamp; }
         void setLow(int lower) { mLow = lower; }   
         void setHigh(int higher) { mHigh = higher; }
         void setStamp(int stamp) { mStamp = stamp; }
     private:   
         int mLow;   
         int mHigh; 
         int mStamp;
}; // class Interval 


 int main(int Argc_,char *Argv_[]) {
    int n;
    Interval r;

    std::map<Interval, Interval> Intervals_type;
    r.setLow(0);
    r.setHigh(10);
    r.setStamp(1);
    std::pair< Interval, Interval > tmp(r,r);
    Intervals_type.insert(tmp); 

    r.setLow(10);
    r.setHigh(20);
    r.setStamp(2);
    std::pair< Interval, Interval > tmp2(r,r);
    Intervals_type.insert(tmp2);    


    r.setLow(20);
    r.setHigh(30);
    r.setStamp(3);
    std::pair< Interval, Interval > tmp3(r,r);
    Intervals_type.insert(tmp3);    

    r.setLow(30);
    r.setHigh(40);
    r.setStamp(4);
    std::pair< Interval, Interval > tmp4(r,r);
    Intervals_type.insert(tmp4);    



    n = 36;
    std::map<Interval, Interval>::const_iterator it = 
               Intervals_type.lower_bound(Interval(n));
    if (it == Intervals_type.end()){
        printf(" n = %d not found\n",n);
    }

    return 1;
}

std::map compares with operator < only, so it knows only about Interval::mLow , effectively treating all intervals as [mLow, ∞). You're using the wrong container. It's possible to do it with map but it's harder. Use Boost.Icl instead.

Edit: The best thing you have in STL for this purpose is std::multi_set. Order your intervals by the right end-point:

     bool operator<(const Interval& rhs) const{     
         return mHigh < rhs.mHigh;
     }

Now you can do it this way:

std::multi_set<Interval> cont;
cont.insert(Interval(0,10,1));
cont.insert(Interval(10,20,2));
cont.insert(Interval(20,30,3));
cont.insert(Interval(30,40,4));

std::multi_set<Interval>::const_iterator iter = cont.lower_bound(Interval(36));
if(iter == cont.end() || iter->low() > 36)
    // not found
else
    // found

IIUC, you're dealing with ranges, and you have an invariant on the map that ranges don't overlap. If that's the case, you have to define your operator< so that it deals with ranges, and does something drastic (assertion failure or an exception) in the case of an overlap, to prevent such ranges from being inserted. Assuming a half open range of [low,high) and an assertion that high >= low in the constructor of Interval, something like the following should work:

struct CmpInterval
{
    //  For insertion...
    bool operator<( Interval const& lhs, Interval const& rhs) const
    {
        assert( lhs.low >= rhs.high
                || lhs.high <= rhs.low
                || (lhs.low == rhs.low && lhs.high == rhs.high) );
        return lhs.low < rhs.low;
    }
    //  For find, lower_bound, etc.
    bool operator<( Interval const& lhs, int target ) const
    {
        return lhs.low < target;
    }

    bool operator<( int target, Interval const& rhs ) const
    {
        return target <= rhs.high;
    }
};

The last two are used for lower_bound, find, etc., when you pass a simple integer as key (and not an Interval); together, they define a strict ordering relationship between an int and an Inteval, IFF there are no overlapping intervals, and an equivalence relationship such that all n in an interval [i, j) are equivalent to that range and to each other. (Again, if there are overlapping intervals, there is no equivalence relationship, and the behavior is undefined.)

lower_bound should return the position before the first item that is equal or larger. The largest item in your map is actually smaller so end is returned.

In your operator < you only check mLow. If you want to check that 36 is in the range 30 to 40 then correct your operator <.

The definition of lower_bound is that it returns a location where you could insert the item and still keep the container sorted. Your comparison function only works with the low member; your container has the contents 0,10,20,30 for the lows. The only insertion point for 36 that keeps the container sorted is at the very end.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM