數組

問題:爲何數組下標是從0開始,而不是從1開始;java

什麼是數組?

數組是一種線性表數據結構,它用一組連續的內存空間來存儲一組相同類型的數據算法

線性表結構:數據排成一條線同樣的結構,每一個線性表上最多隻有前和後兩個方向,除數組外,鏈表、隊列、棧也是線性結構。編程

非線性結構:好比:二叉樹、圖、堆等,在非線性結構中,數據之間並非簡單的先後關係。數組

連續的內存空間和相同類型的數據:正式由於這兩個特性,纔有了它的一大特性:「隨機訪問」;缺點:在數組中要進行刪除或者插入操做須要進行大量的遷移工做,效率低下;數據結構

關於數組的隨機訪問:例如一個長度爲10int類型的數組 int a[] = new int[10];在下圖中,計算機給數組 a[10]分配了一起連續的內存空間 1000~1039;其中,內存的首地址 base_address=1000編程語言

咱們知道,計算機會給每個內存單元分配一個地址,經過地址來訪問到內存中存儲的數據;當計算機須要隨機訪問數組中的元素時,會根據以下的公式找出該元素存儲的內存地址:優化

a[i]_address = base_address + i * data_type_size
複製代碼

其中 data_type_size表示數據中每一個元素的大小。在當前例子中存儲的爲 int 類型,因此 data_type_size的大小爲 4spa

「鏈表適合插入、刪除,時間複雜度 O(1);數組適合查找,查找時間複雜度爲 O(1)」。這種表述是不許確的。數組是適合查找操做,可是查找的時間複雜度並不爲 O(1)。即使是排好序的數組,你用二分查找,時間複雜度也是 O(logn)。因此,正確的表述應該是,數組支持隨機訪問,根據下標隨機訪問的時間複雜度爲 O(1)。3d

低效的插入和刪除

插入操做

假設數組arr的長度爲n,如今咱們要在數組arr的第k個位置插入一個元素,爲了把 k這個位置騰出來給新的元素,那麼咱們須要把 k~n這部分元素都順序日後移一位。code

若是是插入到數組的末尾,那麼咱們就不須要對移動數據,這時候時間複雜度爲O(1)。若是在數組的開頭插入元素,那全部的數據都要日後移一位,這時候的時間複雜度爲O(n)。那麼平均複雜度則爲O(n)

若是咱們插入的元素是有序的,那麼咱們就必須按照上邊的操做依次將數組日後移動一位。可是若是數組中的元素沒有任何規律,數組只是被當作一個存儲數據的集合。在這種狀況下,若是想要將某個數據插入到k這個位置,爲了不大量的數據遷移,最簡單的方法就是,直接將第k位的元素放到最後,把要插入的元素直接放到k的位置。

例如:現有數組 a[10]存儲了五個元素:a、b、c、d、e;咱們如今要將元素x插入到第三個位置,咱們只須要把c放到a[5],將a[2]賦值爲x便可;最終獲得的數組中的元素爲:a、b、x、d、e、c;利用這種處理技巧,在特定場景下,在第 k 個位置插入一個元素的時間複雜度就會降爲 O(1)

刪除操做

和插入操做相似,當咱們要刪除數組中的某個元素時,要對數據進行搬移操做,否則數據中間會出現空洞,內存就不連續了;

和插入相似,若是刪除數組末尾的數據,則最好狀況時間複雜度爲 O(1);若是刪除開頭的數據,則最壞狀況時間複雜度爲 O(n);平均狀況時間複雜度也爲 O(n)

實際上,在某些特殊場景下,咱們並不必定非得追求數組中數據的連續性。若是咱們將屢次刪除操做集中在一塊兒執行,刪除的效率是否是會提升不少呢?

例如:數組 a[10] 中存儲了 8 個元素:a,b,c,d,e,f,g,h。如今,咱們要依次刪除 a,b,c 三個元素;爲了不 d,e,f,g,h 這幾個數據會被搬移三次,咱們能夠先記錄下已經刪除的數據。每次的刪除操做並非真正地搬移數據,只是記錄數據已經被刪除。當數組沒有更多空間存儲數據時,咱們再觸發執行一次真正的刪除操做,這樣就大大減小了刪除操做致使的數據搬移。

這是 JVM 標記清除垃圾回收算法的核心思想

解題開篇

如今咱們來思考開篇的問題:爲何大多數編程語言中,數組要從 0 開始編號,而不是從 1 開始呢?

從數組存儲的內存模型上來看,「下標」最確切的定義應該是「偏移(offset)」。前面也講到,若是用 a 來表示數組的首地址,a[0] 就是偏移爲 0 的位置,也就是首地址,a[k] 就表示偏移 kdata_type_size 的位置,因此計算 a[k] 的內存地址只須要用這個公式:

a[k]_address = base_address + k * data_type_size
複製代碼

可是,若是數組從 1 開始計數,那咱們計算數組元素 a[k] 的內存地址就會變爲:

a[k]_address = base_address + (k - 1) * data_type_size
複製代碼

對比兩個公式,咱們不難發現,從 1 開始編號,每次隨機訪問數組元素都多了一次減法運算,對於 CPU 來講,就是多了一次減法指令。

數組做爲很是基礎的數據結構,經過下標隨機訪問數組元素又是其很是基礎的編程操做,效率的優化就要儘量作到極致。因此爲了減小一次減法操做,數組選擇了從 0 開始編號,而不是從 1 開始。

思考:

  1. JVM 的標記清除垃圾回收算法的核心理念。
  2. 前面我提到一維數組的內存尋址公式,思考、類比一下,二維數組的內存尋址公式是怎樣的呢?
相關文章
相關標籤/搜索