歡迎加入我的 Discord 群組與我討論程式相關的問題!

陣列 (Array)

陣列可以用來儲存很多個變數,比較方便儲存大量的資料。
例如: 今天題目給你 N 個正整數,並要你把這些正整數存小到大排序。
N 的大小可能會到 1000,甚至到 1000000!
看到題目的你,應該不會想要開 1000 個 int 變數把所有存下來吧? 這樣多累阿!
現在我們介紹一種新的工具能夠輕鬆的把這些資料全部存起來。
在陣列中,你可以一次開很多個變數,並且利用儲存的位置 (index) 來操作存下來的變數,
當你要操作陣列中的元素時,只要用中括號 [位置] 就可以取得存在該位置的 value。

語法

// 宣告: 資料型態 [陣列長度]; 
int a[1005];

// 對陣列的其中一個變數操作: [位置]
cin >> a[0]; 
a[0] += 2;
a[1] = a[0];
cout << a[0] << '\n';

提醒:

  • 陣列的 index 是從 0 ~ 長度 – 1
  • 在宣告的方法就可以看到,陣列中只能存一個資料型態
  • 在實作時可以開比題目給定的長度再大一點,比較不容易用到不存在的位置
  • 陣列的長度是不可以改變的

範例

#include <bits/stdc++.h>
using namespace std;
int a[1005]; // 宣告型別 int, 長度 1005 的陣列 
int main(){
    int n;
    cin >> n;
    for(int i = 0;i < n;i++){  // 存取陣列位置 [i] 的變數
        cin >> a[i]; // 將 n 個數字由位置 0 ~ n-1 依序輸入
    }
    sort(a, a+n); // 排序 (先不用懂), 3 - 4 級分的內容會再提到
    for(int i = 0;i < n;i++){
        cout << a[i];  // 將陣列中存的 value 全部輸出
        if(i == n-1) cout << "\n"; 
        else cout << " ";
    }
}

以上程式碼是最一開始提到的範例,可以輸入 N 個正整數並且將它排序完輸出。
假如今天我的輸入是:

5
5 2 3 1 4

陣列裡存的就會是:

a[index]01234
value52314

排序完過後就會變成:

a[index]01234
value12345

在 APCS 第二題中,50的部分分通常都會是用到上面提到的陣列。而剩下的50會需要用到二維陣列! 二維陣列你可以把他想像是一種棋盤格,而這種陣列可以儲存更多資料。通常我們會將陣列的第一個維度當作行 (row),第二個當作列 (column)。二維陣列的 index 也是從 0 開始的。以下是格子的 index。

(0, 0)(0, 1)(0, 2)(0, 3)(0, 4)
(1, 0)(1, 1)(1, 2)(1, 3)(1, 4)
(2, 0)(2, 1)(2, 2)(2, 3)(2, 4)
(3, 0)(3, 1)(3, 2)(3, 3)(3, 4)
(4, 0)(4, 1)(4, 2)(4, 3)(4, 4)
int a[50][50]; // 宣告二維陣列 行,列

for(int i = 0;i < 5;i++){
    for(int j = 0;j < 5;j++){ 
        cin >> a[i][j]; // 輸入每一個的值
    }
}
cout << a[0][0]; // 最左上角
cout << a[2][1]; // 第二行 第一列

其實二維陣列就是 陣列裡面再放一個陣列,可以用來模擬很多二維的狀況。

以下我們用這題當作例題。可以看到我們想要模擬倉庫裡存放的狀況。而要模擬二維的倉庫就需要用到二維陣列。

這題就當作給各位的練習題,這裡有AC程式碼,要先自己挑戰完再看喔!

以上就是陣列的用法,在後面的 C++ STL 也會介紹一種可以改變長度的動態陣列!

結構 (Structure)

看完陣列教學的你可能會想,如果今天題目給的輸入非常多,只有一種資料型態存不下怎麼辦?
開很多陣列? 可以! 只是在某些情況下可能不適用
例如…

  • 你會改變他們的 index,可是有很多個陣列所以改變會很麻煩
  • 有太多不同的資料,開很多陣列也很麻煩

例題 (之前 greenjudge的 b021):

T大資工系一直是國內各高中對資訊領域有興趣的學生心目中的第一志願,它的錄取標準是這樣的,首先比各科的總分,同分的情況下再比數學的分數。現在給你一群學生的指考成績,請你排出他們錄取的優先順序。

Input:
第一行有一個正整數 N (1<=N<=100),代表有 N 位學生的成績,接下來有 N 行的資料,每行一開始有一個正整數,代表他的編號,接下來有5個整數,代表國文、英文、數學、物理、化學這五科的成績,每科成績最低0分,最高100分。


Output:
請依照總分、數學的順序,排出這些學生的優先順序,並依序輸出他們的編號


Sample Input:

5
1 91 73 87 71 67
2 50 57 65 92 90
3 92 87 76 49 48
4 61 46 51 92 55
5 99 59 52 63 95

Sample Output :

1
5
2
3
4

這題要存的東西有點多,而且 index 還會因要排序而改變,所以不太適合用很多陣列來做。
這邊我們介紹一種新的語法 - Struct
Struct 很像是一個模板,你可以自己定義一種資料型態,下面的例子中,我就定義了一個 person 資料型態,一個person型態會有兩個double分別存取weight 和 height,還有一個 name 存取名字。

語法

// 宣告 struct
struct 名稱{
    // 內容變數,建構子
};
// Ex. 
struct person{
    double weight, height;
    string name;
};

// 建立實體: 名稱 變數; 
person a;
person people[50];

範例

#include <bits/stdc++.h>
using namespace std;
struct Person{
    int chi, math, eng, phy, chem;
    int tot, id;
    Person(){ // 建構函數
        chi = 0; math = 0; eng = 0; phy = 0; chem = 0;
        tot = 0; id = 0;
    }
    bool operator < (const Person& a) const { // 運算子多載
        if(tot == a.tot) { 
            return math < a.math;
        } else {
            return tot < a.tot;
        }
    }
};
Person a[105]; // 開一個長度為 105,資料型態為上面定義的 Person 陣列
int main(){
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin >> n;
    for(int i = 0;i < n;i++){ // 輸入每個學生的成績和 id
        cin >> a[i].id >> a[i].chi >> a[i].eng >> a[i].math >> a[i].phy >> a[i].chem;
        a[i].tot = a[i].chi + a[i].eng + a[i].math + a[i].phy + a[i].chem;
    }
    sort(a, a + n); // 將學生排序 
    reverse(a, a + n); // 把整個陣列反轉因為sort是從小到大排序,我們想要的是大到小
    for(int i = 0;i < n;i++){
        cout << a[i].id << '\n';
    }
}

這個程式碼可能會比較複雜一點,裡面有用到了建構函數以及運算子多載。
在我自己定義的 Person 資料型態中,我存取了六個整數變數,分別記錄一個學生的各科成績,id,以及總和成績。我開了一個陣列來記錄所有 N 個學生的資料,並且將它依照題目排序輸出答案。

接下來這邊是讀者可能比較陌生的兩個部分

  • 建構函數
    • 對函數不太熟的讀者建議先讀完後面的函數文章再回來觀看!
    • 如果有接觸其他程式語言的人應該對這個不陌生,其實struct就很像一個class。
    • 當你用struct建構開一個實體變數時,系統會呼叫所謂的建構函數,給你的實體一些預設 / 傳入的值。
    • 我在code裡面寫的就是 default constructor,只是將變數初始化而已。
    • 但是要用這種建構函數的話就需要用到 new 語法,功能就如其名,會創建一個新的 struct 實體。
    • 括號內可以放入你要的參數。一個struct可以有很多個 constructor。但是每個函數必須有所不同 (參數數量 / 參數型態不同),要不然系統會不知道你呼叫的是哪個。
    • 以下的例子就是可以傳入 score 當作參數把 chi 的值一開始就設定成 score。
Person(int score){
    chi = score; 
    math = 0; eng = 0; phy = 0; chem = 0;
    tot = 0; id = 0;
}
Person (int chiScore, int mathScore){
    chi = chiScore;
    math = mathScore; 
    eng = 0; phy = 0; chem = 0;
    tot = 0; id = 0;
}
Person a = new Person(100);
Person b = new Person(100, 100);
  • 運算子多載
    • 在struct裡面,你可以定義你的資料型態的運算子要怎麼判斷
    • 括號裡面的就是你的參數,而在範例程式碼中的例子就是給電腦看我的 Person 型態怎麼比大小
    • 語法的部分就要遵照上面的形式,原因有點太複雜,在這邊先不提
    • 可以定義其他的運算元,例如 +, -, *, /
    • 下面的例子是我多載 + 的運算子 (沒有實際用途,只是當參考)
int operator + (const Person& a) const {
    return chi + a.chi;
}

以上就是 struct 的用法! struct 在當你想要自己定義一種資料型態的時候非常的好用,這邊有幾個小提醒。

  • struct {} 後面要加上分號
  • 運算子多載的部分要注意語法
  • struct 的名字盡量都用大寫開頭,會比較容易判斷

Blog at WordPress.com.