數組,是平常開發中用到的最多的數據結構之一,說到這裏,可能有人就不贊同,「怎麼我學的數組不是數據結構呢?」,看完這篇文章,我相信你可以本身判斷!python
以前,本身一度認爲數組太簡單了,不就是經過下標拿元素,遍歷數組,查找排序等操做。直到看了大佬對數組的介紹,才發覺本身真是坐井觀天,對數組的理解太淺顯了。算法
數組專業一點來講是屬於「線性表」的一種,它用一組連續的內存空間來存儲具備相同類型的數據。編程
常見的線性表結構有鏈表、數組、棧、隊列,顧名思義線性表就是數據排列在一條線上,每一個線性表上的數據最多隻有先後兩個方向。windows
非線性表結構則比線性表更復雜,非線性表上的數據並非簡單的先後關係,常見的非線性表數據結構有圖、樹、堆等。數組
經過上面的圖,咱們能明顯看到線性表和非線性表的不一樣之處。線性表只有先後關係,非線性表則存在多種關係。網絡
繼續說數組,上面說到來數組是線性表的一種,而且數組是連續的內存空間,相同的數據類型,因此數組有經過下標「隨機訪問」的特性;數據結構
可是這也帶來了一些弊端,就是數組是連續的,致使刪除和插入的時候爲了保證連續性須要進行大量的數據遷移。編程語言
數據的訪問,那到底數組是怎麼經過下標隨機訪問元素的呢?學習
拿一個長度爲10的int類型數組 int[] a = new int[10]; 計算機會給數組分配連續的內存空間 1000~1039,其中內存首地址爲base_address = 1000;優化
咱們知道計算機會給每一個內存單元分配一個地址,計算機經過這個內存地址訪問內存中的數據。
數組中因爲內存是連續的,因此咱們能夠經過基地址直接計算出對應的下標所在的內存地址。數組中計算某個下標元素的內存地址公示以下:
a[i]_address = base_address + i * data_type_size
在本例子中,數組中存儲的是int類型數據(js中數組每一個元素大小能夠不一樣,是作了特殊處理,js中具體數組在內存中如何存儲待研究;
data_type_size爲4個字節,因此若是要獲取下標爲2的元素的地址,經過上面的公式計算就獲得來內存地址爲1008,因爲只計算一次就能獲取到準確的內存地址,因此訪問數組中某個元素的時間複雜度爲:
爲何說數組的「插入」和「刪除」操做很低效呢?咱們知道數組在內存中是連續的,若是進行插入或者刪除操做的話,因爲數組要保證內存數據的連續性,因此數據須要進行移動,來保證連續性。
下面舉例說明:
插入操做:假設數組的長度爲n,如今,若是咱們須要將一個數據插入到數組中的第k個位置。爲了把第k個位置騰出來,給新來的數據,咱們須要將第k~n這部分的元素都順 序地日後挪一位。那插入操做的時間複雜度是多少呢?咱們能夠分析一下。
分析:若是第k個位置是數組末尾,那麼不須要移動數據,此時最好時間複雜度爲:
若是在數組的開頭插入數據,則全部的數據都要日後移動一位,因此最壞時間複雜度爲:
由於咱們在每一個位置插入的元素的機率是同樣的,因此平均時間複雜度爲:
經過上面的分析咱們看到,插入操做的平均時間複雜度爲:
那麼有沒有可以優化的地方呢?
實際上是有的,可是隻能針對特定的狀況進行優化。
好比當數組僅僅做爲存儲數據的集合而不在意存儲的數據的順序時,咱們能夠直接將第k個元素放在數組最後,將要插入的元素放在第k個位置。這樣就能作到時間複雜度爲
但這也致使了快排是一個不穩定的排序算法
舉個例子來看一下上面的優化方式:假設數組a[10]中存儲了以下5個元素:a,b,c,d,e。 咱們如今須要將元素x插入到第3個位置。咱們只須要將c放入到a[5],將a[2]賦值爲x便可。最後,數組中的元素以下: a,b,x,d,e,c。以下圖所示:
刪除操做:存在長度爲n的數組,如今須要刪除第k個元素,刪除第k個元素後爲了保證數組中第k個位置不出現空洞致使數組內存不連續,因此須要將k~n的元素都要向前移動一位。咱們依舊分析一下時間刪除操做的時間複雜度。
分析:
若是刪除的是最後一個元素,則不須要移動,因此最好時間複雜度爲:
刪除的是第一個元素,那麼後面的k~n個元素都要向前移動一位,因此最壞時間複雜度爲:
在每一個位置刪除的機率是同樣的,那麼平均時間複雜度爲:
那麼刪除操做可否優化呢?
其實也是能夠的,咱們經過上面的例子知道每刪除一個元素就要移動後面的全部元素,那麼咱們能不能把多個刪除操做放在一塊兒,統一進行一次數據移動呢?
舉個例子來看一下上面的優化方式:數組a[10]中存儲了8個元素:a,b,c,d,e,f,g,h。如今,咱們要依次刪除a,b,c三個元素。
爲了不d,e,f,g,h這幾個元素被移動三次,每次刪除操做的時候並不去直接進行數據搬移,而是把要刪除的元素標誌爲已刪除,當數組中須要插入元素可是發現數組已經滿了以後,再將標記爲刪除的元素真正的刪除,這樣就大大減小了刪除操做致使的數據遷移。
咱們以前有計算過數組的內存尋址公式 a[i]_address = base_address + i * data_type_size;
若是從1開始的話,咱們能夠獲得對應的內存尋址公式爲 a[i]_address = base_address + (i - 1) * data_type_size,每次隨機訪問一個元素都多了一個減法操做。
數組是一種很基礎的數據結構,效率須要儘量的高,因此爲了減去一次減法操做,因此數組選擇了從0開始編號而不是從1開始。
固然也並非非0不可,有的語言甚至支持負數的下標,例如python。可是大多數都是從0開始編號,可能也和C語言以0做爲下標有關,後面的語言或多或少的都借鑑了C語言。
咱們今天學習了數組。它能夠說是最基礎、最簡單的數據結構了。
數組用一塊連續的內存空間,來存儲相同類型的一組數據,最大的特色就是支持隨機訪問,但插入、刪除操做也所以變得比較低效,平均狀況時間複雜度爲O(n),咱們也瞭解到了一維數組的尋址公式,和插入和刪除操做的優化方法。
今天咱們就學習到這裏吧!有什麼好的想法和建議歡迎下方評論留言~
無論你是轉行也好,初學也罷,進階也可
——【值得關注】個人C/C++編程學習進階俱樂部 ——
涉及到:C語言、C++、windows編程、網絡編程、QT界面開發、Linux編程、遊戲編程、黑客等等......