# [資演] 3-Array & Python list ###### tags: `資演` Array (陣列) 是一種最常用的資料結構,也是最簡單的資料結構。一個array是一種相同種類的物件的簡單集合。我們可以看到無數array的應用,包括但不僅限於在電腦科學中。 ## Array 是什麼? ![](https://hackmd.io/_uploads/S1L5H5Zst.png) 我們可以把array想做是一盒裝滿滿的馬卡龍: * 盒子只能裝馬卡龍(不能裝蛋糕、麻糬) * 所有在盒子裡的馬卡龍都是相鄰的(沒有空位) * 每個馬卡龍都可以用它的位置唯一地指定 * 盒子的大小不能改變 這對應到的array的性質分別是: * 一個array只能儲存單一型別的資料 * 在電腦的記憶體裡面,array占有的是連續的一串記憶體 * 每個array中的element(元素)都對應到一個index(索引) * 一個array的長度必須事先指定 :::info 在電腦科學中,array是一種用來儲存**同一種資料**的資料結構。在一個array裡面,每個element都可以用一個index唯一地指定。在電腦裡面,array占有的記憶體是連續並且相鄰的,這樣就可以藉由index的計算來知道下一個element是什麼。 ::: ## Array 的種類 * 單維 array 一個element可以用**一個單一的index**指定。 可以想成是一盒馬卡龍,我們只要知道那個馬卡龍在那個盒子裡面是排在第幾個,就能夠找到它。例如,第2個馬卡龍,我們就用`a[2]`表示。 * 多維 array 一個element必須用**一組index**指定。 想像現在有一個大箱子,裡面裝著很多很多盒馬卡龍。要找到一個特定的馬卡龍,我們除了必須知道它在盒子裡面的位子以外,還必須知道它在第幾盒,例如,第5盒的第2個馬卡龍,我們可以用`a[5][2]`來表示。這是一個2維array的例子。 我們當然可以繼續延伸。現在我們有一個貨櫃,裡面有很多很多箱馬卡龍,要找到一個特定的馬卡龍,除了前面提到的盒子和盒內位置外,還必須知道是在哪一個箱子裡面。例如,第10箱的第5盒中的第2個馬卡龍,表示為`a[10][5][2]`。這就是一個3維array。我們可以一直推廣下去,每次都用一個更大的容器把很多個相同的小容器放進去,當我們要特定其中一個element的時候,就把它在每一種容器的位置講清楚。我們也可以指定某一盒特定的馬卡龍(而不是某一顆馬卡龍),或著是某一箱馬卡龍在貨櫃中的位置。例如,在貨櫃中的第10箱馬卡龍,我們可以寫成`a[10]`;在貨櫃中的第10箱的第5盒,我們就寫成`a[10][5]`。 ## 電腦怎麼儲存array? 我們前面講到,array占有的記憶體是連續並且相鄰的。 這就像是我們知道每個馬卡龍的厚度是3公分,一個盒子可以裝10個馬卡龍,那麼這個盒子的長度就是30公分,其中第0個馬卡龍(沒錯,從0開始)占有的是0公分到3公分的位置,第1個馬卡龍是3公分到6公分...以此類推。所以我們如果知道馬卡龍的位置是在盒子從頭算起的的第6公分到第9公分,則我們可以回推,這顆馬卡龍的編號是2;反過來說,如果我們知道一顆馬卡龍的編號是3,那我們就能夠算出它在盒子中佔據的位置應該是第9公分至第12公分。 ![](https://hackmd.io/_uploads/SkBXOoHoY.png) 那麼,array是怎樣的情形呢?現在想像一個由整數組成的array。每個整數占有的記憶體是4個單位。另外,我們那個整數**在記憶體中占有的位置的起點**當作是那個整數的地址(位址, address),也就是說,第3顆馬卡龍佔據的位置是第9公分至第12公分,則他的位址就是在9公分處。一個array的位址相當於它的第0個element的起點位址。 至於多維array,我們就把它們**壓平**來儲存。這是什麼意思呢?以2維的array為例,我們可以把2維的array看成一個array的array,也就是一堆array的有序集合。所以在儲存這個2維array的時候,我們可以按照順序儲存這些array: ![](https://hackmd.io/_uploads/By0YfnSjF.png) 同樣地,對於3維array,我們可以看成是一堆2維array的有序集合。也就是說,我們只要遞迴地按照順序儲存那些2維array就可以了,這邊便不再贅述。 ## Python 裡的 array 在Python裡面,我們可以使用array模組或是numpy模組裡面的array來進行array的操作。以下以numpy的array為例。 ``` import numpy as np # 創建一個1維array a = np.array([1, 2, 3, 4, 5]) # 巡訪1維array a for elem in a: print(elem) # 創建一個2維array b = np.array([[11, 15, 10, 6], [10, 14, 11, 5], [12, 17, 12, 8], [15, 18, 14, 9]]) # 巡訪2維array b for row in b: for elem in row: print(elem) ``` ## 什麼是 Python list? 一個list是用來儲存一些東西的有序集合。注意在這邊我們並沒有強調需要單一型別,因為list可以儲存各種型別的東西。 ## Python list 的操作 ``` # 創建一個list my_list = [5, 2, 3, 1, 4] # 獲取 list 的 element print(my_list[2]) # 巡訪 my_list: 方法1 for i in range(len(my_list)): print(3 * my_list[i]) # 巡訪 my_list: 方法2 for elem in my_list: print(3 * elem) # 在第4個位置插入元素15 my_list.insert(4,15) # 在尾端位置新增元素55 my_list.append(55) # 讓這個list變成一個按照順序排好的list my_list.sort() ``` :::spoiler 我們現在如果想要加總這個list的所有元素呢? ``` def sum(a): sum = 0 for elem in my_list: sum += elem return sum ``` ::: ## 例題 * 尋找遺失的數字 給定一個不一定有排序好的 $n-1$ 個整數,已知這些整數中包含由 $1$ 到 $n$ 其中少了一個數字的所有整數,請找出那個少掉的整數。 Example: ``` Input: a = [1, 2, 4, 6, 3, 7, 8] Output: 5 Input: a = [1, 2, 3, 5] Output: 4 ``` :::spoiler 解答 在這邊我們應用到一個性質:從 $1$ 到 $n$ 的所有整數總和是 $n(1+n)/2$。我們把這個總和減去list所有element的總和,答案就是少掉的那個數。這邊我們利用到我們剛剛寫出來的sum函式: ``` def getMissingNo(a): n = len(a) + 1 total = n * (n + 1)/2 sum_of_a = sum(a) return total - sum_of_a ``` ::: * 轉置(transpose)一個2維的list 給定一個2維的list,找出他的轉置list。 ![](https://hackmd.io/_uploads/HkxFUarjK.png) ``` Input: a = [[1, 2], [3, 4]] Output: [[1, 3], [2, 4]] Input: a = [[1, 2, 3], [4, 5, 6]] Output: [[1, 4], [2, 5], [3, 6]] ``` :::spoiler 解答 首先,我們必須知道這個2維list的大小,也就是這個2維list底下有幾個1維list,每個1維list裡面又有幾個element。 ``` def transpose(a): # 這個2維list底下有幾個1維list m = len(a) # 每個1維list裡面有幾個element n = len(a[0]) # 初始化一個2維list # 底下有n個1維list,每個1維list有m個element b = [[0] * m]*n # 把 element 塞進去 for i in range(n): for j in range(m): b[i][j] = a[j][i] return b ``` 當然,如果使用numpy,你可以更簡單地進行同樣的工作: ``` import numpy as np def transpose(a): matrix = np.array(a) b = list(matrix.T) return b ``` :::