11.5 用 sort 排序 vector 與 string
sort 收的參數是「開頭位置」與「結尾的下一格位置」——對 vector 來說,這兩個東西你在 10.5 就認識了:begin() 與 end()。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {5, 1, 4, 2, 3};
sort(v.begin(), v.end()); // 由小到大
for (int i = 0; i < (int)v.size(); i++) {
cout << v[i] << ' ';
}
cout << '\n';
sort(v.begin(), v.end(), greater()); // 由大到小也一樣
for (int i = 0; i < (int)v.size(); i++) {
cout << v[i] << ' ';
}
cout << '\n';
return 0;
}
執行結果:
1 2 3 4 5
5 4 3 2 1
指標與 iterator 在這裡露出了「同一家人」的身分:陣列用指標當位置、容器用 iterator 當位置,sort 通吃——它只在乎你給它一段「從哪裡到哪裡」的範圍。
¶反著走:rbegin() 與 rend()
既然 sort 只認「從哪裡到哪裡」,那把範圍反過來給它呢?容器除了 begin()/end(),還有一組反向的 rbegin()/rend():rbegin() 指向最後一個元素、rend() 指向第一個元素的前一格,走起來由尾往頭:
sort(v.rbegin(), v.rend()); // 由大到小
sort(v.begin(), v.end(), greater()); // 同一件事的另一種寫法
兩行結果完全相同({3, 1, 2} 兩種寫法都得到 3 2 1),string 也適用。這種寫法在別人的程式碼裡很常見,看到要認得。
¶string 也能排
string 是字元的容器(10.7),所以 sort(s.begin(), s.end()) 會把字串裡的字元排好——依 ASCII 編號,8.1 的三大區段規則全部適用(數字 < 大寫 < 小寫):
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string s = "banana";
sort(s.begin(), s.end()); // 把字串裡的「字元」排序
cout << s << '\n';
return 0;
}
執行結果:
aaabnn
「判斷兩個字串是否由相同字母重組而成」這類題目,把兩邊各自排序再比較是否相等,一行 sort 就解決。
¶vector 排序:先 first,再 second
排序 pair 時,sort 用的還是 <——而 10.11 說過 pair 的比較規則:先比 first,first 相同才比 second:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
// (分數, 年齡)
vector<pair<int,int>> v = {{80, 14}, {95, 13}, {80, 12}, {70, 15}};
sort(v.begin(), v.end()); // 先比 first,一樣才比 second
for (int i = 0; i < (int)v.size(); i++) {
cout << v[i].first << ' ' << v[i].second << '\n';
}
return 0;
}
執行結果:
70 15
80 12
80 14
95 13
兩個 80 分的元素靠 second(年齡)分出先後。「主要條件相同時比次要條件」的雙關鍵字排序,用 pair 裝資料就自動到手——這是競程超常用的組合技。
動手試試看:解掉相鄰城市列表——每個城市要把相鄰城市依升序列出。提示:開一個「vector 的陣列」vector<int> adj[100005];,每讀入一條道路就把兩端互相 push_back;輸出前把每個城市的 vector 各自 sort 一次。