想像一下…
一年一度的動漫展要開賣門票了!主辦單位開放 n 個售票口,每個窗口前都排著一條人龍。你是負責記錄的工作人員:不時有人加入某個窗口的隊尾,也不時有隊伍最尾端的人嫌隊太長、跳去別的窗口重排。所有變動結束後,你要回報每個窗口由隊首到隊尾的完整名單。
用上冊的陣列存這些隊伍,馬上撞牆。陣列的大小在宣告那一刻就得寫死(上冊 6.1 還特別強調:要用 const 常數),但每條隊伍最後會有多長,事先沒有人知道——極端情況下,所有人都擠在同一個窗口。窗口數和總人數都可能到 10^5,每條隊伍都按上限「開好開滿」(上冊 6.1 的慣例),就是 10^5 \times 10^5 = 10^{10} 格的二維陣列、約 40 GB 記憶體,根本開不出來;開小一點,人一多就越界(上冊 6.8 的 UB)。就算記憶體無限大,你還得替每一條隊伍自己記「現在排了幾個人」,加人、走人、跳隊全靠手動加減和搬值,錯一步整份紀錄就亂了。
我們真正想要的是「會自己長大、也會自己縮短的陣列」,而且要幾條有幾條:
vector<int> line[100005]; // 10^5 條隊伍,每條一開始都是空的
line[x].push_back(d); // 編號 d 的人加入 x 號窗口的隊尾
line[y].push_back(line[x].back()); // x 號隊尾的人跳去 y 號窗口的隊尾……
line[x].pop_back(); // ……並離開原本的隊伍
短短幾行,兩種變動都處理完了:每條隊伍各自伸縮、記憶體大致上用多少拿多少,想知道某個窗口排了幾個人,問一聲 line[x].size() 就有答案。這就是本單元的主角之一:vector。它跟另外兩位主角——把兩個值綁在一起的 pair、讓字串處理脫胎換骨的 string——都來自同一個工具箱:C++ 標準函式庫的容器。這個單元把工具箱打開,之前那些「明明只是想裝個資料,卻要顧東顧西」的麻煩,會一件一件消失。
順帶一提:上面的場景就是本站題目〈排隊模擬 1〉——學完這個單元,你就能親手解掉它。