這個題目有點兒無聊,跟茴香豆的“茴”字有幾種寫法一樣無聊,又是一個無聊的老掉牙的話題——排序,問題依然是無聊至極——把輸入的單詞按順序(我可沒說是什麼順序)排列。當作是一個總結吧。
按詞典排好說,如果你用vector<string> words儲存單詞,只需用algorithm裡的sort即可:sort(words.begin(),words.end())。這玩意兒我閉著眼也能敲出來。但假如你用list的話……編譯器發脾氣了吧?(發脾氣是好的,要是沒有任何抱怨,趁早兒扔了吧)。list容器不支援隨機訪問,標準庫的sort沒法用到它上面。幸好list自己帶了一個sort,所以要用words.sort()。
如果要按和詞典相反的順序排呢?對sort而言,預設是採用“<”來比較的,如果讓它採用“大於”不就行了嗎?標準庫當然為此“預留了介面”。你可以先寫一個比較函數,如compare,記著用大於符號“>”比較:
bool compare(string s1, string s2)...{
return s1>s2;
}
接著調用三個參數的sort版本:sort(words.begin(),words.end(),compare)就成了。對於list容器,這個方法也適用,把compare作為sort的參數就可以了,即:words.sort(compare)。
更進一步,我現在要讓這種操作更加能適應變化。也就是說,我希望能給比較函數一個參數,用來指示是按升序還是按降序排。這回輪到函數對象出場了。
為了描述方便,我先定義一個枚舉類型EnumComp用來表示升序和降序。很簡單:
enum EnumComp...{ASC, DESC};
然後開始用一個類來描述這個函數對象。它會根據它的參數來決定是採用“<”還是“>”。嗯……我想應該是這個樣子的:
class Compare
...{
private:
EnumComp comp;
public:
Compare(EnumComp c):comp(c)...{};
bool operator()(string s1, string s2)...{
switch(comp)...{
case DESC:
return s1>s2;
case ASC:
return s1<s2;
}
}
};
好了,想按升序排,那就sort(words.begin(),words.end().Compare(ASC));想按降序排,就sort(words.begin(),words.end(),Compare(DESC))。
其實對於這麼簡單的任務(類型支援“<”、“>”等比較子),完全沒必要自己寫一個類出來。標準庫裡已經有現成的了,就在functional裡,把它include進來就行了。functional提供了一堆基於模板的比較函數對象。它們是(看名字就知道意思了):equal_to<Type>、not_equal_to<Type>、greater<Type>、greater_equal<Type>、less<Type>、less_equal<Type>。對於這個問題來說,greater和less就足夠了,直接拿過來用:
- 升序:sort(words.begin(),words.end(),less<string>());
- 降序:sort(words.begin(),words.end(),greater<string>()).
上面的幾種方法都還算是“正大光明”,那麼偏方是什嗎?看看sort的參數,前兩個是迭代器……嗯,記不記得有個“反向迭代器”的玩意兒?沒錯,預設的sort會用小於符號比較,如果換成反向迭代器,不就“騙”過sort了嗎?sort(words.rbegin(),words.rend())。
再來考慮一個更難一點兒的問題:按長度遞增的順序排序,長度相同的按字典順序排。這回如果簡單地用兩次sort就不行了,第二次的排序很可能會把前一次的成果完全毀掉。根本原因是大多數的sort都是採用“快速排序”演算法實現的,而快速排序是一種不穩定排序,也就是說,如果兩個元素A和B的值相同,排序前A在B的前面,那麼經過快速排序後,並不能保證A仍然在B的前面。現在需要的是一種“穩定排序”的演算法,它可以保證在排序後相同值的元素的相對位置不變。
稍微想一下可知,應該先按字典順序排,再用穩定排序的方法按長度排。標準庫提供了一個stable_sort,用來實現穩定排序。先來定義一個字串長度的比較函數:
bool length_comp(string s1, string s2)...{
return s1.size()<s2.size();
}
接著兩行代碼就完成了任務:
sort(words.begin(),words.end());
stable_sort(words.begin(),words.end(),length_comp);