線性表、棧、隊列和串都是線性表的數據結構,他們的邏輯結構特徵是:每個數據元素之多有一個直接前趨和直接後繼。對於多維數組和廣義表是一種複雜的非線性結構,它們的邏輯特徵是:一個元素可能有多個直接前趨和多個直接後繼。
一、數組概念
一維數組
可以看成是一個線性表或一個向量,在計算機中是一段連續的存儲單元,適用於隨機查找。二維數組
由圖可以知道,每個元素最多由兩個直接前驅和兩個直接後繼(邊界除外),故是一種典型的非線性結構。
多維數組最多有多個直接前驅和直接後繼,也是非線性結構。
- 多維數組在計算機內的存儲
由於內存結構是一維的,故必須按照某種次序將數組元素排成一個線性序列。
二、多維數組的存儲結構
數組是先定義後使用,且爲靜態分配存儲單元,所以採用順序存儲。有行優先順序存儲、列優先順序存儲。
對於數組 A(nxm)
1. 行優先 順序存儲
(a11->a12->a13->...a1n)->(a21->a22->a23->...a2n)->...(am1->...->amn)
最左邊下標變化慢,做for外層循環;右邊變化快,做for內層循環。
aij地址:前面有i行(i×n),改行前面有j個元素
LOC(aij) = LOC(a00) + (i*n+j)*d // d爲每個元素所佔空間大小
// 三維數組
LOC(aijk) = LOC(a000) + (i*n*p+j*p+k)*d
2 . 列優先 順序存儲
(a11->a21->a31->..an1)->(a12->a22->a32->...an2)->(a1m->a2m->...->anm)
最又邊下標變化慢,做for外層循環;左邊變化快,做for內層循環。
aij地址:前面有j列(j×m),改行前面有i個元素
LOC(aij) = LOC(a00) + (j*m+i)*d // d爲每個元素所佔空間大小
// 三維數組
LOC(aijk) = LOC(a000) + (j*m*p+i*p+k)*d
三、 特殊矩陣的壓縮存儲
所謂壓縮,即當矩陣元素分佈呈某種規律時,可以多個元素共用一個存儲單元,以實現節約存儲單元。簡單的說,就是多個值相同的元素只分配一個存儲單元,值爲0的不分配空間。
可以理解爲將二維數組(矩陣)壓縮到一個存儲單元數目少的一維數組中。而爲了方便存儲後直接找到某元素,還必須給出壓縮前下標和壓縮後下標之間的變換公式。
1. 對稱矩陣
aij = aji // A(nxn)
以矩陣的對角線爲分隔,分爲上三角和下三角 。壓縮存儲對稱矩陣存儲時只需要存儲上三角/下三角的數據,所以最多存儲n(n+1)/2個數據(相當於1+2+…+n,即等差數列求和)。
對稱矩陣和壓縮存儲的對應關係:
(1)下三角存儲
// 二維數組爲壓縮前的元素,一維數組放壓縮後的元素
int s[k], a[i][j];
if (i >= j)
a[i][j] = s[ i*(i+1)/2 + j ];
else(i < j)
a[i][j] = s[ j*(j+1)/2 + i ];
(2)上三角存儲
int s[k], a[i][j];
if (i <= j)
a[i][j] = s[ i*n-i(i-1)/2 + j-i ];
else(i > j)
a[i][j] = s[ j*n-j(j-1)/2 + i-j ];
2、 三角矩陣
(1)下三角
它的主對角線以上(不包括主對角線)的元素均爲常數0(或相同的常數)
其壓縮存儲與對稱矩陣的類似,但必須多一個存儲單元存放上三角部分元素,使用的存儲單元爲n(n+1)/2 + 1
假設仍按行優先存放,有
if (i >= j)
a[i][j] = s[ i(i+1)/2 + j ];
else (i < j)
a[i][j] = s[ n(n+1)/2 ];
(2)上三角
它的主對角線以下(不包括主對角線)的元素均爲常數0(或相同的常數)
if (i <= j)
a[i][j] = s[ i*n-i(i-1)/2 + j-i ];
else (i > j)
a[i][j] = s[ n(n+1)/2 ];
3、 對角矩陣
若矩陣中所有非零元素都集中在以主對角線爲中心的帶狀區域中,則爲對角矩陣,常見的有三對角矩陣,五對角矩陣,七對角矩陣等。
以三對角矩陣爲例:
在一個 n*n 的三對角矩陣中,只有 n+n-1+n-1
個非零元,共需要3n-2
個存儲單元,零元不佔存儲單元。
int s[3n-2], a[i][j];
if (i = j+1)
a[i][j] = s[3i-1];
else if (i = j)
a[i][j] = s[3i];
else (i = i-1)
a[i][j] = s[3i+1];
4、稀疏矩陣
矩陣階數大,非零元數目少,零元多,非零元的排列沒有規律的矩陣。
由於沒有規律,除了存放非零元的值外,還需要適當的輔助信息(行列號)才能迅速確定一個元素。有以下這些存儲方法:
(1)三元數組
所謂三元:元素值、行號、列號
整個稀疏矩陣中非零元的三元組合起來稱爲三元組表。
數據類型描述:
#include <iostream>
using namespace stdl;
const int maxsize = 100;
class node
{
public:
int i,j; // 非零元行、列號
int v; // 非零元值
};
class sparmatrix // 定義稀疏矩陣
{
public:
int rows,cols; // 行、列數
int terms; // 非零元個數
node data[maxsize]; // 三元組表
}
(2)帶行指針的鏈表
把具有相同行號的非零元用一個單鏈表連接起來,稀疏矩陣的N行組成N個單鏈表,合起來稱爲帶行指針的鏈表。
(3)十字鏈表
當係數矩陣中的非零元位置或個數經常變動時,三元組就不適合採用了,用鏈表作爲存儲結構更合適。
每個非零元既是第 i 行循環的一個節點,又是第 j 列的節點,相當於處在十字交叉路口,故稱爲十字鏈表。
class linknode
{
public:
int i,j; // 行、列號
linknode *cptr, *rptr; // 行、列指針
union vnext // 定義一個共同體
{
int v; // 表節點使用v域,表示非零元值
linknode *next; // 表頭節點使用next指向下一個表頭節點
} k;
...
};
5、 廣義表
廣義表是n個元素的有限序列,其中的每一項可以使原子項,也可以是一個子表。一般用LS表示廣義表,ls表示子表。
廣義表舉例:
F = ( ); // F 爲空表,長度爲0
G = (a, (b, c)); // 長度爲2,第一項爲原子,第二項爲子表
H = (x,y,z); // 長度爲3,都是原子
D = (B,C); // 都是子表
E = (a,E); // 長度爲2,一個原子一個子表
廣義表的深度 = 括號層數
分類:
(1)線性表:元素都是原子
(2)純表:元素都是原子或都是子表,與樹對應,如圖(a)(d)
(3)再入表:與圖對應,允許節點共享,如 (d)
(4)遞歸表:如圖(e) D = (a, D);