多維數組和廣義表(C++)

線性表、棧、隊列和串都是線性表的數據結構,他們的邏輯結構特徵是:每個數據元素之多有一個直接前趨和直接後繼。對於多維數組和廣義表是一種複雜的非線性結構,它們的邏輯特徵是:一個元素可能有多個直接前趨和多個直接後繼。

一、數組概念

  1. 一維數組
    可以看成是一個線性表或一個向量,在計算機中是一段連續的存儲單元,適用於隨機查找。

  2. 二維數組
    二維數組
    由圖可以知道,每個元素最多由兩個直接前驅和兩個直接後繼(邊界除外),故是一種典型的非線性結構。

多維數組最多有多個直接前驅和直接後繼,也是非線性結構。

  1. 多維數組在計算機內的存儲
    由於內存結構是一維的,故必須按照某種次序將數組元素排成一個線性序列。

二、多維數組的存儲結構

數組是先定義後使用,且爲靜態分配存儲單元,所以採用順序存儲。有行優先順序存儲、列優先順序存儲。

對於數組 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);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章