語法書 / AA 競程語法書 上冊 / 第六單元 / 索引從 0 還是從 1?(0-base 與 1-base)

6.3 索引從 0 還是從 1?(0-base 與 1-base)

圖 6-2:0-base 與 1-base 的兩種存法

C++ 的陣列天生從 0 開始int a[5]; 的五個格子是 a[0] ~ a[4]。但題目描述一串資料時,有兩種數法:

  • 1-base:「第 1 張卡片、第 2 張卡片……第 n 張」——從 1 數起
  • 0-base:「位置 0、位置 1……位置 n-1」——從 0 數起

兩種都很常見。寫程式前先看清楚題目用哪一種,再決定資料放進哪些格子。

兩種存法

存法 A(1-base 存法):用 a[1] ~ a[n],讓 a[0] 空著

int a[105];                 // 要用到 a[n],記得多開一格
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
    cin >> a[i];            // 題目的「第 i 個」就住在 a[i]
}

存法 B(0-base 存法):用 a[0] ~ a[n-1]

int a[105];
int n;
cin >> n;
for (int i = 0; i < n; i++) {
    cin >> a[i];            // 題目的「位置 i」就住在 a[i]
}

兩種都對,重點只有一個:讓存放的格子跟題目的編號對齊。對齊之後,後面的程式就能照著題目敘述直接寫,不用一直在腦中「加一減一」換算。

範例程式碼

題目(1-base 敘述):給 n 張卡片上的數字,再給 L, R,請依序輸出第 L 張到第 R 張的數字。用存法 A,迴圈可以照抄題目:

#include <iostream>
using namespace std;

const int MAX_N = 105;
int a[MAX_N];

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];               // 1-base 存法:第 i 張 → a[i]
    }

    int L, R;
    cin >> L >> R;
    for (int i = L; i <= R; i++) {
        cout << a[i] << '\n';      // 「第 L 張到第 R 張」直接照抄
    }

    return 0;
}

執行結果(輸入 510 20 30 40 502 4):

20
30
40

同一題若改成 0-base 敘述(位置 L 到位置 R),只要把讀入迴圈換成存法 B,輸出段一個字都不用改——存法對齊了,程式自然對齊。

動手試試看:把範例程式碼整份改成存法 B 來解同一題(1-base 敘述不變)。數一數:總共有幾個地方要 -1?跟存法 A 比,哪個版本你比較有信心一次寫對?