數組對一個程序員來說再熟悉不過了,幾乎所有的編程語言都有數組,它是最基本的數據結構之一。
剛開始學數組的時候,總是很納悶,為什么它從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)
如果對時間復雜度的概念不了解,可以查看
總結