1. 程式人生 > 其它 >為什麼陣列從0開始編號

為什麼陣列從0開始編號

為什麼陣列從0開始編號

陣列對一個程式設計師來說再熟悉不過了,幾乎所有的程式語言都有陣列,它是最基本的資料結構之一。

剛開始學陣列的時候,總是很納悶,為什麼它從0開始編號,而不是從更符合我們思維習慣的1開始呢?帶著這個問題往下看。

什麼是陣列

在程式設計的過程中,我們經常要用到陣列,但是你能夠用專業的詞彙來描述什麼是陣列嗎?

陣列(Array)是一種線性表資料結構,它用一組連續的記憶體空間,來儲存一組具有相同型別的資料。

線性表

線性表(linear list)是指具有n個相同特性的資料元素組成的有限序列,你可以理解為“把所有資料用一根線串起來,再儲存到物理空間中”,從圖中可以明顯的看出它最多隻有前和後兩個方向(節點)。

除了陣列,連結串列、佇列和棧也是線性表結構(線性表既可以順序儲存,也可以鏈式儲存)

非線性表

與線性表對應,非線性表是指一個節點元素可能有多個直接前驅和多個直接後繼,比如樹和圖

連續的記憶體空間和相同的資料型別

同一個陣列中的資料型別都是一樣的,並且它們連續儲存在記憶體中,所以我們可以對陣列內容進行隨機訪問。隨機訪問這個過程是怎麼實現的呢?

計算機會給每個記憶體單元分配一個地址,通過地址來訪問記憶體中的資料。當它需要隨機訪問陣列中的某個元素時,首先會通過定址公式,計算出該元素儲存的記憶體地址,然後對其進行讀取操作,定址公式為

    a[i]_address = base_address + i * data_type_size    //其中i表示陣列中第幾個元素

我們拿一個長度為 10 的 int 型別的陣列 int[] a = new int[10]來舉例。在這個圖中,計算機給陣列 a[10]分配了一塊連續記憶體空間 1000~1039,其中,記憶體塊的首地址為 base_address = 1000,因為儲存的是整數,所以data_type_size = 4。根據公式,我們很容易就能得到這個陣列中某個元素在記憶體中的地址。

從記憶體上看,我們可以理解為什麼大部分程式語言中的陣列下標是0開始,更準確的講它不是下標而是代表偏移(offset)。如果用a來表示陣列的首地址,a[0]是它偏移0的位置(就是首地址),a[2]代表是偏移2 * data_type_size的位置,也就是第三個元素的地址,a[i]就表示偏移i * data_type_size

的位置。如果從1開始編號的話,那麼計算陣列a[i]的記憶體地址公式就會變為

    a[i]_address = base_address + (i-1) * data_type_size

對於CPU來說,多做了一次減法指令。

還有一個原因就是歷史遺留問題,因為C語言使用0開始計數陣列下標,後面的Java、C#等語言也都效仿了C語言,繼續沿用該習慣.但現在有的語言也不是從0開始計數,比如Lua,Matlab等.(部分內容及圖片參考王爭老師的資料結構與演算法之美)

陣列的增刪查操作

查詢

前面已經講過,可以通過下標訪問陣列中的元素,由於它是連續儲存的,訪問的時間複雜度是O(1).

新增

假設我們有一個數組,長度為n,要在它第k個位置上插入資料,要進行什麼操作呢?為了空出第k個位置給新插入的資料,k~n這部分的資料都需要往後挪一位,那麼它的平均時間複雜度為O(n).

刪除

刪除操作於新增類似,為了保持記憶體的連續,不出現空洞,需要將刪除元素後面的元素向前移,平均時間複雜度也為O(n)

如果對時間複雜度的概念不瞭解,可以檢視演算法複雜度分析

總結