STL中改變map的默認比較方式

大家知道,STL中的map底層是用紅黑樹實現的,其泛型原型如下:

template <class _Key, class _Tp, class _Compare, class _Alloc>
class map {
......
}

其中_Key表示比較的鍵(key),_Tp表示值(value),_Compare表示比較方式,_Alloc表示內存分配器。

一般我們在寫map的時候總是類似於寫出如下代碼:

map<int, char*>* my_map = new map<int, char*>;

表示鍵爲int類型,值爲字符串類型。這裏之所以不對_Compare和_Alloc加以限制,是因爲int是C++內置類型,有默認比較方式,_Alloc也採用STL的

默認的內存方案。但是如果有如下結構體:

struct Term{
char* str;
int hashCode;
};

現在我們要將該Term作爲map的鍵,並假設Term所對應的值爲Term出現的頻率(int型),那麼能不能這樣寫:

map<Term, int>* my_map = new map<Term, int>;

顯然這樣寫map是無法正常運作的,原因是struct Term並非C++的內置類型,默認不知道如何去比較它。這時候就需要修改map的默認比較方式:

template <class T>
struct Compare
{
int operator()(const T& x, const T& k) const{
if(x.hashCode >= k.hashCode) return 0;
else return 1;
}
};

這裏採用的是函數對象(function object)的方式去加載map的比較方式,表示使用Term的hashCode作爲比較方式,以對紅黑樹進行查找、插入等操作。

這樣我們就可以把map寫成下面的形式:

map<Term, int, Compare<Term> >* my_map = new map<Term, int, Compare<Term> >;

這樣map就可以正常運作了,比如進行插入操作:

Term my_term;
my_map
->insert(make_pair(my_term, 1));

但是上面的struct Compare爲什麼要寫成這樣的形式,寫成這樣行不行:

template <class T>
struct Compare
{
int operator()(const T& x, const T& k) const{
if(x.hashCode >= k.hashCode) return 1;
else return 0;
}
};

這是不行的。爲什麼不行,首先來看一看map中find的源代碼:

template <class _Key, class _Value, class _KeyOfValue, 
class _Compare, class _Alloc>
typename _Rb_tree
<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator
_Rb_tree
<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::find(const _Key& __k)
{
_Link_type __y
= _M_header; // Last node which is not less than __k.
_Link_type __x = _M_root(); // Current node.

while (__x != 0)
if (!_M_key_compare(_S_key(__x), __k))
__y
= __x, __x = _S_left(__x);
else
__x
= _S_right(__x);

iterator __j
= iterator(__y);
return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ? end() : __j;
}

上面的代碼中_M_key_compare就表示我們的那個比較函數對象,_S_key(__x)表示取__x節點的key,並和__k比較。

if (!_M_key_compare(_S_key(__x), __k))
__y
= __x, __x = _S_left(__x);

表示如果_S_key(__x) >= __k即,如果節點的key大於或等於查找的key那麼就__x就等於它的左子節點,否則就爲右子節點。

但爲什麼等於的時候不直接返回呢,卻在繼續查找?舉個例子來說:

如果我們要查找key爲10的節點是否在樹中時,首先從根節點開始查找,由於8<10,這時_M_key_compare返回1,那麼此時,

轉向root的右子樹,然後由於10==10,_M_key_compare返回0,這時轉向左子樹,但左子樹是空的,循環停止。

 return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ? end() : __j;

由於此時__j表示"10"這個節點(其實是個迭代器),由於__k爲10,而__j._M_node的key爲10,_M_key_compare返回0,有三元運算符可知,

此時返回是__j,即表示找到了。因此我們的比較函數對象必需寫成:

當節點鍵大於等於所要查找或插入的鍵時,返回0(false),反之爲1(true),這是由內部源代碼所決定的。

轉載於:https://www.cnblogs.com/zjfdlut/archive/2011/08/12/2135698.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章