14|參考與函式
相關主題: C++ 基礎速查
只限 C++|CPU 時間上限 1 秒
有 n 對整數。對每一對,把比較小的放前面輸出。全部輸出完之後,再輸出三行統計:
small:每一對裡比較小的那個數(共 n 個)large:每一對裡比較大的那個數(共 n 個)all:全部 2n 個數
每一行統計都要有:個數、最小值、最大值、總和。
這一題的重點不是演算法,而是函式的參數要怎麼傳:什麼時候傳值(複製一份)、什麼時候傳參考(&)、什麼時候傳 const 參考(const &),以及它們和 C 的「傳指標」有什麼不同。
請一定要寫出下面的 struct 和 4 個函式(名稱、參數型別要一樣),並且在 main 裡用它們完成工作:
struct Stats { long long sum; // 總和 int min; // 最小值 int max; // 最大值 int count; // 個數};
void order(int& a, int& b); // 讓 a ≤ b:如果 a > b 就把兩個交換void update(Stats& s, int x); // 把一個數字 x 加進統計 svoid add_all(Stats& s, const std::vector<int>& values); // 把 values 裡的每個數字都加進 s(呼叫 update)void print_stats(const std::string& name, const Stats& s); // 輸出一行統計Stats 的成員可以加上預設值(例如 long long sum = 0;),也可以在使用前自己設定初始值。
建議的 main 流程:讀一對 a b → 呼叫 order(a, b) → 輸出 → 把 a 存進 small、b 存進 large 這兩個 std::vector<int>;全部讀完之後,用 add_all 算出三組統計,再用 print_stats 輸出。
注意:評測只能比對輸出,沒辦法檢查你有沒有照規定寫這些函式,也分辨不出你用的是參考、指標還是全域變數。但這一題要練的就是這件事,請自己遵守規定;通過之後可以和參考解答比對看看。
為什麼 void order(int a, int b) 不行?
Section titled “為什麼 void order(int a, int b) 不行?”void order(int a, int b) { // a、b 是「複製出來的副本」 if (a > b) std::swap(a, b); // 交換的是副本} // 函式結束,副本消失;呼叫者的變數完全沒變
int x = 5, y = 2;order(x, y); // x 還是 5,y 還是 2傳值的時候,函式拿到的是一份副本,怎麼改都不會影響呼叫者。改成 void order(int& a, int& b) 之後,a、b 就是呼叫者那兩個變數的別名(參考),在函式裡交換 a、b,就是交換呼叫者的 x、y。
| 參數寫法 | 函式拿到的是 | 能改到呼叫者的變數嗎 | 會複製整個資料嗎 |
|---|---|---|---|
int a(傳值) | 一份副本 | 不能 | 會(int 很小,沒關係) |
int& a(參考) | 呼叫者變數的別名 | 能 | 不會 |
const std::vector<int>& v(const 參考) | 別名,但只能讀 | 不能(寫了會編譯錯誤) | 不會 |
int* a(指標,C 的寫法) | 變數的位址 | 能(要寫 *a) | 只複製位址 |
- 第 1 行:一個整數 n(1 ≤ n ≤ 100000)。
- 接下來 n 行:每行兩個整數 a、b,用一個空格分隔(-10^9 ≤ a, b ≤ 10^9)。a 可能大於、等於或小於 b。
一共 n + 3 行:
- 前 n 行:依照輸入的順序,每一對輸出一行
較小 較大(用一個空格分隔)。兩個數相等就照原樣輸出。 - 接著 3 行統計,順序是
small、large、all,格式是名稱 個數 最小值 最大值 總和,用一個空格分隔。
總和可能超過 int 的範圍(最大可能到 2×10^14),要用 long long。
輸入:
45 2-3 74 410 -10輸出:
2 5-3 74 4-10 10small 4 -10 4 -7large 4 4 10 26all 8 -10 10 19整理後,比較小的數是 2、-3、4、-10(總和 -7),比較大的數是 5、7、4、10(總和 26),全部 8 個數的總和是 19。
C++:
- 參考
int& r = x;:r是x的別名,對r做的任何事都是對x做。參考建立時一定要綁定一個變數,之後不能改綁別的變數,也不會有「空的參考」 - 函式參數寫成
int&:函式可以修改呼叫者的變數,用來「交換」或「一次傳回好幾個結果」(例如把結果寫進Stats&) - 函式參數寫成
const T&:不複製、也不能修改。std::vector、std::string、struct 這類可能很大的資料,只需要讀的時候就這樣傳 int、double這種小型別只需要讀的時候,直接傳值就好(例如update的int x)std::swap(a, b)(在<utility>)交換兩個變數- struct 成員可以有預設值:
long long sum = 0;,之後寫Stats s;就已經初始化好
對照 C:
- C 沒有參考,要修改呼叫者的變數只能傳指標:
void order(int *a, int *b),函式裡寫*a、*b,呼叫時寫order(&x, &y);struct 要寫s->sum - C 傳 struct 會整個複製一份,所以大的 struct 通常傳
const struct Stats * - 參考的呼叫端看起來和傳值一樣(
order(x, y)),光看呼叫看不出變數會不會被改。所以 C++ 的習慣是:只讀的參數一律加 const,這樣「沒有 const 的參考」就代表「這個函式會修改它」
提示 1:order 和 update 怎麼寫?
order:如果 a > b 就交換,可以用 std::swap(a, b),或用一個暫存變數自己交換。因為參數是參考,交換的就是呼叫者的變數。
update:用 s. 存取成員(參考不是指標,不用寫 ->)。總和加上 x、個數加 1,再看 x 有沒有比目前的最小值更小、比目前的最大值更大。
想一想:如果 update 寫成 void update(Stats s, int x)(傳值)會發生什麼事?
提示 2:Stats 一開始要設成什麼?
sum 和 count 從 0 開始。min、max 有兩種常見做法:
min設成int能表示的最大值、max設成最小值(std::numeric_limits<int>::max()和::min(),在<limits>),第一個數字進來時一定會更新- 或者在
update裡判斷:count是 0 的時候,直接把min、max都設成x
sum 一定要用 long long:三個 10^9 加起來就超過 int 的上限 2147483647 了(Sanitizer 會抓到「有號整數溢位」)。
提示 3:add_all 和 print_stats 為什麼要用 const 參考?
small 這個 vector 最多有 100000 個數字。如果寫成 void add_all(Stats& s, std::vector<int> values)(傳值),每呼叫一次就要把 100000 個數字整個複製一份,用完再丟掉;寫成 const std::vector<int>& 就不會複製,而且 const 保證函式不會不小心改到它。
add_all 裡面用範圍 for 迴圈把每個數字交給 update(s, x)。all 那一組只要對同一個 Stats 變數呼叫兩次 add_all(一次給 small、一次給 large),就會把兩邊的數字累積在一起。
print_stats 只需要讀,所以 name 和 s 都用 const 參考。