2016年5月4日 星期三

給新手的C++教學 (上冊) - 6. 陣列 (Array)

回到「給新手的C++教學 (上冊)」

上一章

不知你有沒有想要實現過以下這件事,卻遇到阻礙呢?
輸入一個整數n,然後輸入n個數字
最後把這n個數字的順序顛倒,然後輸出

例如:輸入「4 24 5 105 411」,輸出為「411 105 5 24 」(注意:每個數字後面都有空格,最後一個數字也一樣)

你可能有想過要做類似以下的事情:
如果n是1就宣告 a,輸入 a,輸出 a (「宣告」是「定義變數」的專業說法)
如果n是2就宣告 a、b,輸入 a、b,輸出 b、a
如果n是3就宣告 a、b、c,輸入 a、b、c,輸出 c、b、a
如果n是4就宣告 a、b、c、d,輸入 a、b、c、d,輸出 d、c、b、a
......
然後你就會發現沒完沒了XD

事實上,我們可以直接指定一個整數n,讓電腦一次幫我們宣告n個變數!(並非任何情況都可行,見註7)
這就是「陣列 (Array)」的好用之處

用法如下 (假設你想宣告n個int變數、陣列的名稱取作「array_name」):
int array_name[n];

這樣一來電腦就幫你把n個變數命名為array_name[0]、array_name[1]、array_name[2]、......、array_name[n-1]了!
(心理偷偷的想:耶~小中大括號都用到了耶 \(^o^)/ )

請注意,第1個變數是array_name[0] (而不是array_name[1]),第n個變數是array_name[n-1] (而不是array_name[n])
不知你在第一次看到本教學時,有沒有注意到有一章叫做「0. 目錄 (Catagories)
這就是暗示了
中括號 (「[」和「]」) 裡面的數字,我們叫做「索引值 (Index)」
有別於數學上常見的正整數系統
陣列的「索引值」是從「0」開始的
也就是說:
索引值為0時代表第1個變數
索引值為1時代表第2個變數
索引值為2時代表第3個變數
索引值為3時代表第4個變數
索引值為4時代表第5個變數
......
以此類推
至於原因,之後的進階章節會講到 (建議你不要現在就直接看它),目前你只要記住有這個現象即可

因此
「輸入一個整數n,然後輸入n個數字
最後把這n個數字的順序顛倒,然後輸出」
這個問題就迎刃而解了:

#include<cstdio>
int main()
{
    int n;
    scanf("%d",&n);
    int a[n];
    int i=0;
    while(i<n)
    {
        scanf("%d",&a[i]);
        i=i+1;
    }
    i=n-1;
    while(i>=0)
    {
        printf("%d ",a[i]);
        i=i-1;
    }
    printf("\n");
}
 

←「i」要從0開始跑,因此先把i設定成0
←「i」會從0跑到n-1

    (這樣會讓程式照順序輸入a[0]~a[n-1])


←「i」要從n-1開始跑,因此先把i設定成n-1
←「i」會從n-1往回跑到0

    (這樣會讓程式倒序輸出a[n-1]~a[0])

那個「printf("\n");」可加可不加,因為題目沒有強制要求輸出格式
但這不代表「加」和「不加」的效果相同,建議讀者測試看看兩種情況的差異

好了,陣列講完了(?)


現在,給你一個練習題:
給你n個介於1和10的整數,請你統計每個數字的數量
輸入:一個整數n和n個介於1到10之間的整數
輸出:10個數字,第i個數字代表數字i的數量
例如:輸入「15 5 6 6 3 7 4 1 7 6 2 8 5 1 10 4」,要輸出「2 1 1 2 2 3 2 1 0 1」

警告:有個意外簡單的方法

提示:請善用陣列

參考解答:

#include<cstdio>
int main()
{
    int a[11];
    int i=1;
    while(i<=10)
    {
        a[i]=0;
        i=i+1;
    }
    int n;
    scanf("%d",&n);
    i=0;
    while(i<n)
    {
        int v;
        scanf("%d",&v);
        a[v]=a[v]+1;
        i=i+1;
    }
    i=1;
    while(i<=10)
    {
        printf("%d\n",a[i]);
        i=i+1;
    }
    return 0;
}

欲罷不能?
還是想雪恥?
再給你一個挑戰題:
給你n個整數,請你把這n個整數由小到大輸出
輸入:一個整數n和n個整數
輸出:由小到大的n個整數
例如:輸入「5 24 5 105 411 87」,要輸出「5 24 87 105 411 」

警告:挑戰性極高,考驗邏輯能力,做得出來堪稱天才 (?)

提示:每次找出最小的數字,然後把這個最小的數字拿掉

參考解答:

#include<cstdio>
int main()
{
    int n;
    scanf("%d",&n);
    int a[n];
    int i=0;
    while(i<n)
    {
        scanf("%d",&a[i]);
        i=i+1;
    }
    i=0;
    while(i<n)
    {
        int j=i,minimum=i;
        while(j<n)
        {
            if(a[j]<a[minimum])
            {
                minimum=j;
            }
            j=j+1;
        }
        printf("%d ",a[minimum]);
        a[minimum]=a[i];
        i=i+1;
    }
    printf("\n");
    return 0;
}

感覺已經可以完全掌控電腦了,對吧?
的確如此喔~ ^_^

接下來的章節教的東西,利用這6章所學也可以弄出來
只是,它們可以把一切變的更簡單、更好寫、更好想!

下一章

感謝:uzk、栢淵
(版權所有 All copyright reserved)

18 則留言:

  1. #include <iostream>
    using namespace std;
    int main()
    {
        int A[10]={1,2,3,4,5}, B[10];
        for (int i=0;i<10;i++)
            A[i]=B[i];
            
        cout<<B[1];
    }

    請問一下
    我要複製A陣列到B
    輸出B[1]=2
    但結果為何是0呢??

    回覆刪除
    回覆
    1. 啊啊我知道了
      我寫反了
      是B[i]=A[i]

      刪除
    2. 哈哈恭喜您自行解決問題 ^_^
      加油~~~

      刪除
    3. #include
      #include
      #include
      #include

      void shuffle(int wDeck[][13]);
      void deal(const int wDeck[][13],const char *wFace[], const char *wSuit[]);

      //using namespace std;

      int main()
      {
      const char *wSuit[4] = {"Hearts","Diamonds","Clubs","Spades"};
      const char *wFace[13]=
      {"Ace","Deuce","Three","Four","Five","Six","Seven",
      "Eight","Nine","Ten","Jack","Queen","King"};

      int deck[4][13]={0};
      srand(time(0));
      int face;
      int suit;
      shuffle(deck);
      deal(deck,face,suit);

      }

      void shuffle(int wDeck[][13])
      {
      int row,column;
      for(int card = 1; card <= 52; card++)
      {
      do
      {
      row = rand()%4;
      column = rand()%13;
      }
      while(wDeck[row][column] != 0);
      wDeck[row][column] = card;
      }
      }

      void deal(const int wDeck[][13], const char *wFace[], const char *wSuit[])
      {
      for(int card = 1; card <= 52; card++)

      for(int row = 0; row <= 3; row++)

      for(int column = 0; column<12 ; column++)

      if(wDeck[row][column] == card)
      cout<<setw(5)<<setiosflags(ios::right)
      <<wFace[column]<<"of"
      <<setw(5)<<setiosflags(ios::left)
      <<wSuit[row]
      <<(card%2 == 0?'\n':'\t');

      }
      這個請您看一下哪裡錯了?

      刪除
  2. C++的array居然可以動態規劃?!

    回覆刪除
    回覆
    1. GCC有幫我們實作哦!這陣列使用的記憶體應該是開在stack上(有錯請指正XD)

      刪除
  3. 不太了解

    int a[11];
    int i=1;
    while(i<=10)
    {
    a[i]=0;
    i=i+1;
    }

    表達的意思

    回覆刪除
    回覆
    1. int a[11] //a的陣列有11個(0~11)
      這整段的意思感覺是初始a[0]~a[11]皆設定為0
      可以一個一個設定int a[0]=0,int a[1]=0...
      但當有100個的時候,這樣初始比較快!
      (以上自我理解~

      刪除
    2. 感謝網友解釋,但a[11]不存在哦!因為從0開始有11個所以是a[0]~a[10]
      這段程式碼就是把a[0]到a[10]都變成0的意思!

      刪除
  4. 您好,最後的例題似乎沒辦法避免重複數字的出現。
    如:a = [4 1 1]
    第一步:min = 1。 印出:a[1] = 1。 a -> [4 4 1]。
    第二步:min = 2。 印出:a[2] = 1。 a -> [4 4 4]。
    第三部:min = 2。 印出:a[2] = 4。 a -> [4 4 4]。

    我的想法是,在 a[minimum] = a[i]; 這行前加上:

    j=i; //initial
    while(j < n) //把後面同樣的小值都變為 a[i]
    {
    if(a[j] == a[minimum])
    {
    a[j] = a[i];
    }
    j = j+1;
    }

    然後在同行 a[minimum] = a[i]; 後加上:

    int k = 0; //判斷用 若為0則停止大迴圈while(i<n),若為1則繼續
    j = i; //initial
    while(j < n)
    {
    if(a[j] != a[i])
    {
    k = 1;
    break;
    }
    j = j+1;
    }

    if(k == 0)
    {
    break;
    }

    不好意思,我還沒跑過,不過應該可行,謝謝!

    回覆刪除
    回覆
    1. 恩恩,謝謝您的留言,範例程式碼只會讓「輸出」的數字是排序好的,執行完「a裡面存的東西」並「不是」排序好的哦
      感謝補充,歡迎您寫出更厲害的程式碼!^_^

      刪除
  5. 作者已經移除這則留言。

    回覆刪除
  6. 看了很久才大概搞懂,有點困難~~~~~~~

    回覆刪除
    回覆
    1. 恭喜看懂!有任何改善建議也可以來跟我說哦~

      刪除
  7. 為甚麼第一個程式一定要2位數開頭才能成功
    第2個程式隨便輸入數字都失敗???
    請大神幫忙解惑

    回覆刪除

歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原

注意:只有此網誌的成員可以留言。