在編程語言中,常採用實現集合的兩種數據結構是數組和鏈表結構。這兩種類型的結構採用不同的方法在計算機內存中存儲和訪問數據。這些方法反過來導致了操作該集合的算法中的不同的時間/空間取捨。
目錄
1. 數組數據結構
引用維基百科上的定義:數組由相同類型的元素(element)的集合所組成的數據結構,分配一塊連續的內存來存儲。利用元素的索引(index)可以計算出該元素對應的存儲地址。數組表示的是可以在給定的索引位置訪問或替代的項的一個序列。這和python中的列表非常相似,實際上pyhon列表的底層數據結構就是數組。數組的長度或容量在創建的時候就固定下來了。python中的array模塊包含了array類,但是它只能存儲同種類型的變量。
接下來用python內置的array模塊簡單的創建一個數組實例對象,這不是我們本篇文章的重點,所以只做簡單演示:
python的第三方庫numpy中的array的功能也非常強大,有興趣的可以自己查閱下資料。
我們也可以自己定義一個Array類,來實現一些簡單的功能
1.1 隨機訪問和連續內存
計算機通過爲數組項分配一段連續的內存單元,從而支持對數組的隨機訪問。數組在內存中是按順序存放的,可以通過下標直接定位到某一個元素存放的位置。所以不管數組多大,它訪問第一個元素所需的時間和訪問最後一個元素需要的時間是一樣的。
上面是數組的內存圖。假設第一個元素的地址值爲2000,由於數組中存儲的是int整數,一個整數佔4個字節,那麼第二個元素的地址值爲2004,第三個爲2008,第四個爲2012。如果第i個位置處的地址值可以通過獲取。其中第一個元素的地址值爲數組的基本地址。
1.2 靜態內存和動態內存
有些語言中,數組是靜態數據結構。數組的長度和大小都是在編譯時確定的。但是在顯示情況中,數組的長度是變化的,我們並無法事先確定數組的大小。爲了保險起見我們可以定義一個長度儘量長的數組,但是如果我們只存儲一個元素,那麼這顯然是對空間的浪費;爲了節省空間我們定義一個長度較短的數組,同樣的,我們要存儲的元素數大於數組長度時,這個數組就"放不下”需要存儲的數據。在Java和C++中支持動態數組,動態數組同樣佔據了連續的內存塊並支持隨機訪問。在運行時不需要指定動態數組的長度,只需要在實例化的時候指定動態數組的長度即可。python中同樣支持動態數組。
1.3 物理大小和邏輯大小
物理大小:數組單元的總數,或者說創建數組的時候,用來指定其容量的數字。
邏輯大小:當前可供應用程序使用的項的數目,也就是被佔用的單元數
2. 數組的操作
2.1 增加數組的大小
當要插入新的項時,並且此時數組滿了,也就說物理大小和邏輯大小相等,這時候就需要增加數組的大小了。調整大小的過程包括3個步驟:
1. 創建一個新的,更大的數組
2. 將數組從舊的數組複製到新的數組中
3. 將舊的數組變量重新設置爲新的數組對象
DEFALUT_CAPACITY = 5
logicalSize = 0
a = Array(DEFALUT_CAPACITY)
if logicalSize == len(a):
temp = Array(len(a) + 1) # 創建臨時數組,長度爲原數組+1
for i in range(logicalSize): # 將舊數組中的元素拷貝到臨時數組中
temp[i] = a[i]
a = temp # 將臨時數組賦值給a
給數組添加n項的時間複雜度爲。當創建臨時數組的時候,我們double數組的大小,即爲:
temp = Array(len(a) * 2)
這樣時間複雜度降低了,但是犧牲的是內存空間。
2.2 減小數組的大小
當數組的邏輯大小縮小時,就會浪費內存空間。將數組中邏輯大小與物理大小之比定義爲裝載因子(load factor),當裝載因子小於0.25時,我們可以採取減小數組的操作,這是跟增加數組是相反的操作,其步驟如下:
1. 創建一個新的,更小的數組
2. 將數組從舊的數組複製到新的數組中
3. 將舊的數組變量重新設置爲新的數組對象
if logicalSize <= len(a) // 4 and len(a) >= DEFAULT_CAPACITY * 2: # 當裝載因子小於0.25且數組長度大於等於默認容量的2倍時
temp = Array(len(a) // 2) # 創建臨時數組,大小爲原數組長度的二分之一
for i in range(logicalSize):
temp[i] = a[i]
a = temp
2.3 向數組中插入元素
向數組中插入元素步驟如下:
1. 因爲插入元素會改變數組的邏輯大小,先看看數組的物理大小夠不夠,若不夠那麼增加數組大小
2. 從數組的邏輯末尾開始,直到目標索引位置,將每一項向後移動一位。
3. 將新的元素賦值給目標索引位置
4. 將邏輯大小增加1
# 如果需要,增加數組的物理大小
for i in range(logicalSize,targetIndex,-1): # 對數組中元素進行下移操作
a[i] = a[i-1]
a[targetIndex] = newItem # 將新的元素賦值給目標索引處
logicalSize += 1 # 將邏輯大小增加1
2.4 從數組中刪除元素
從數組中刪除元素是向數組中插入元素的反過程。步驟如下:
1. 從目標索引位置後一位置開始,到數組的邏輯末尾,將每一項都向前移動一位。
2. 將邏輯大小減1
3. 檢查浪費的空間,有必要的話,將物理大小減1
for i in range(targetIndex,logicalSize-1): # 對數組中元素進行上移操作
a[i] = a[i + 1]
logicalSize -= 1 # 將邏輯大小減1
# 如果需要,減小物理大小
3. 二維數組
前面講述的都是一維數組,二維數組類似於矩陣或者說是一個表格。使用兩個下標來指定其行和列的位置。
3.1 定義Grid類
3.2 劍指offer:二維數組中的查找
實際上數組在python語言都可以被列表來代替,或者直接使用numpy.array來將列表轉換爲數組格式。