6.7 用陣列收集與記錄資訊
前面的題目,陣列存的都是「題目給的輸入」。這一節換個角度:陣列也可以存你自己算出來的東西——收集結果、記錄狀態、統計次數。這是之後大量題目的地基。
¶收集不定長度的結果
站上的所有 x 的位置要先輸出 x 出現的次數,再由小到大列出每個出現位置——次數要印在最前面,但「有幾個」事先不知道。標準寫法:準備一個結果陣列 ans 和一個計數器 num:
int ans[100005]; // 收集所有符合條件的位置
int num = 0; // 目前收集到幾個
for (int i = 1; i <= n; i++) {
int x;
cin >> x; // 這題甚至不用把輸入整串存起來
if (x == target) {
ans[num] = i; // 放進第 num 格……
num++; // ……數量順便加一
}
}
cout << num << '\n';
for (int i = 0; i < num; i++) cout << ans[i] << '\n';
num 一人分飾兩角:既是「目前的數量」,也是「下一個空格的編號」。中間兩行也常合寫成 ans[num++] = i;——後置 ++ 先用舊值當索引、用完再加一(4.7 教過)。
¶陣列是一排「活的」變數
陣列不是唯讀倉庫:每一格都能隨時讀、隨時改。例如收到指令「把第 x 格加上 v,再回報它的新值」:
a[x] += v; // 第 x 格的「目前值」更新了
cout << a[x] << '\n';
索引=編號、值=這個編號目前的資訊——把這個對應感建立起來,很多題目會突然變簡單。
¶計數陣列:統計每個數字出現幾次
值域不大時(例如分數都在 0 \sim 100),開一個「出現次數」陣列,掃到什麼就把對應的格子加一:
int cnt[105] = {}; // cnt[v]=數字 v 目前出現的次數
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
cnt[x]++; // 又看到一次 x
}
統計完之後想怎麼用都行:哪個數字出現最多次?有沒有數字出現超過一次?哪些數字從沒出現?——全部變成 6.4 的基本遍歷(走訪的對象從輸入變成 cnt 陣列)。
計算數字個數 1 正是這個套路的實戰:先把每個數的出現次數統計進 cnt,之後每個詢問「b 出現幾次」直接回答 cnt[b]——比起每收到一個詢問就把整串數列重掃一遍,快上一大截。
¶範例程式碼
讀入 n 個 0 \sim 100 的分數,輸出「出現次數最多」的分數和它的次數(同票取分數較小者):
#include <iostream>
using namespace std;
int cnt[105]; // 全域:自動全 0
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
cnt[x]++;
}
int best = 0; // 目前出現最多次的分數
for (int v = 1; v <= 100; v++) {
if (cnt[v] > cnt[best]) best = v; // 用「嚴格大於」:同票保留較小的 v
}
cout << best << ' ' << cnt[best] << '\n';
return 0;
}
執行結果(輸入 7 和 60 90 60 100 90 60 77):
60 3
動手試試看:把範例改成輸出「所有出現至少兩次的分數」(由小到大、每行一個)。想想看:這一步遍歷的是 cnt 陣列(0 \sim 100),還是原始輸入?兩者有什麼差別?