數據結構 第9講 數組與廣義表
數組是由相同類型的數據元素構成的有序集合。
一維數組看一看作一個線性表,例如:
圖1一維數組
二維數組也可以看作一個線性表,例如:
圖2二維數組(按列序)
是不是可以看作一個線性表X=(X0,X1,X2,…,Xn-1)?只不過每一個數據元素Xi也是一個線性表。
那麼,橫看成嶺側成峯:
圖3二維數組(按行序)
也可以看作一個線性表Y=(Y0,Y1,Y2,…,Ym-1)?只不過每一個數據元素Yi也是一個線性表。
數組一般採用順序存儲結構,因爲存儲單元是一維的,而數組可以是多維,如何用一組連續的存儲單元來存儲多維數組呢?以二維數組爲例,可以按行序存儲,即先存第一行,再存第二行,…;也可以按列序存儲,先存第一列,再存第二列,…;現在比較流行的C語言,Java都是按行序存儲的。
如果按行序存儲,怎麼找到aij的存儲位置呢?
先看看在存儲aij之前,前面已經存儲了多少個元素:
圖4二維數組(按行序存儲)
從圖4可以看出,在aij之前一共有i*n+j個元素,如果每個元素用L個字節,那麼需要(i*n+j)*L個字節,只需要用基地址加上這些字節就可以得到aij的存儲位置了。
按行序存儲,aij的存儲位置:
LOC(a00)表示第一個元素的存儲位置,即基地址,LOC(aij)表示aij的存儲位置。
授人以魚不如授人以漁,告訴你記住公式,就像送你一條魚,不如交給你捕魚的祕籍!
存儲位置計算祕籍:aij的存儲位置等於矩陣第一個元素的存儲位置,加上前面的元素個數*每個元素佔的空間數。
如果按列序存儲,怎麼找到aij的存儲位置呢?
先看看在存儲aij之前,前面已經存儲了多少個元素:
圖5二維數組(按列序存儲)
從圖5可以看出,在aij之前一共有j*m+i個元素,如果每個元素用L個字節,那麼需要(j*m+i)*L個字節,只需要用基地址加上這些字節就可以得到aij的存儲位置了。
按列序存儲,aij的存儲位置:
LOC(a00)表示第一個元素的存儲位置,即基地址,LOC(aij)表示aij的存儲位置。
需要特別注意:二維數組的下標是從1開始的,那麼畫風就變了~~~
在很多科學工程計算問題中,經常遇到一些階數很高的矩陣,而且這些矩陣的很多值是相同的,有的還有很多元素是0,爲了節省空間,可以對這類矩陣進行壓縮存儲。
什麼是壓縮存儲?
把多個相同的元素分配一個存儲空間,元素爲0的不分配空間。
什麼樣的矩陣能夠壓縮?
一些特殊矩陣,如:對稱矩陣,對角矩陣,三角矩陣,稀疏矩陣等。
什麼叫稀疏矩陣?矩陣中非零元素的個數較少,怎樣纔算是較少呢?一般認爲非零元素個數小於5%的矩陣爲稀疏矩陣。
下面介紹幾種特殊矩陣的壓縮存儲方式:
1.對角矩陣
對角矩陣是指在n´n的矩陣中,非零元素集中在主對角線及其兩側共L(奇數)條對角線的帶狀區域內—L對角矩陣。如圖13所示。
圖13 5對角矩陣
很明顯,L對角矩陣的帶寬爲L,半帶寬d=(L-1)/2,例如5對角矩陣,半帶寬d=2。當|i-j|>d時,aij=0。當|i-j|<=d時,aij≠0,爲對角矩陣的帶狀區域元素。
那麼L對角矩陣一共有多少個非零元素呢?
首先將每一行以對角線爲中心,補零,讓每一行都達到L個元素,如圖14所示。一共補了多少個零呢?第一行補d,第二行補d,…,1,左上角補零個數爲d (d+1)/2,同理,右下角補零個數也爲d (d+1)/2,總的補零個數爲d (d+1),那麼每行按L個元素計算,再減去補零元素個數即可。即帶狀區域元素個數爲:L*n-d (d+1),因爲d=(L-1)/2,即L=2d+1,所有帶狀區域元素個數也可以表達爲:(2d+1)*n-d (d+1)。
圖14 5對角矩陣
那麼,補零後每行都有L個元素,需要L*n個空間。爲了節省空間,第一行前面和最後一行後面的d個0可以不存儲,即"掐頭去尾",即需要L*n-2d個空間。如圖15所示,陰影部分就是要存儲的元素。
圖15 5對角矩陣(掐頭去尾)
如果按行序,用一維數組(下標從零開始)存儲L對角矩陣。
怎麼找到aij的存儲位置呢?
首先找到aii的存儲位置,因爲aii是對角線上的元素,以對角線爲中心,左右兩側都是d個元素,如圖16所示。因此aii之前有i-1行,每行L個元素,aii所在行左側有d個元素,如圖15所示。因此aii之前有(i-1)*L+d個元素,因爲第一行前面的d個0"掐頭去尾"沒有存儲,所以aii之前有(i-1)*L個元素。aii的存儲位置爲:(i-1)*L。而aij和aii相差j-i個元素,也就是說,aij的存儲位置爲:(i-1)*L+j-i。
圖16對角矩陣存儲(按行序)
總結公式:
按行序,用一維數組(下標從零開始)存儲L對角矩陣,aij的存儲位置:
例如:3對角矩陣,L=3,得到3對角矩陣中aij的存儲位置:k=3(i-1)+j-i=2i+j-3,同樣,5對角矩陣中aij的存儲位置:k=5(i-1)+j-i=4i+j-5。
如果一維數組的下標從1開始,公式後面再+1即可。
對角矩陣還有一種按對角線的順序存儲方式,如圖17所示:
圖17 5對角矩陣
即對角線作爲0行,左側分別爲1,2,…,d行,右側分別爲-1,-2,…,-d行,列值不變,相當於轉換爲L×n的矩陣,如圖18所示:
圖18 5對角矩陣存儲(按對角線)
那麼圖18中(b)矩陣,其它位置補零,用一維數組(下標從零開始)按行存儲,aij之前有iˊ+d行,aij所在行前面有j-1個元素,因此下標爲:
2. 稀疏矩陣
稀疏矩陣是指非零元素個數較少,且分佈沒有規律可言,那麼少到什麼程度纔算稀疏呢?一般認爲非零元素小於5%時,屬於稀疏矩陣,當然也沒那麼絕對噢。如圖19所示。
圖19 稀疏矩陣
稀疏矩陣如何存儲呢?
爲了節省空間,只需要記錄每個非零元素的行、列和數值即可。這就是三元組存儲法。如圖20所示。
圖20 稀疏矩陣三元組存儲
廣義表:
廣義表是線性表的推廣,也稱爲列表。它是n(n³0)個表元素組成的有限序列,記作LS= (a0, a1, a2, …,an-1),LS是表名,ai是表元素,它可以是表 (稱爲子表),可以是數據元素(稱爲原子)。n爲表的長度。n=0的廣義表爲空表。
廣義表最常見就是求表頭、表尾。
表頭GetHead(L):非空廣義表的第一個元素,可以是一個單元素,也可以是一個子表。
表尾GetTail(L):非空廣義表刪除表頭元素後餘下元素所構成的表。表尾一定是一個表。
例如D=(a,(b),(a,(b,c,d))),表長爲,表頭爲a,表尾爲( (b),(a,(b,c,d)))。如圖21所示。
圖21 廣義表