數據結構與算法(C/C++版)【數組】


第五章《數組》

一、概念 
根據數組中存儲的數據元素之間的邏輯關系,可以將數組分為 : 一維數組、二維數組、…、n維數組。
n維數組中,維數 n 的判斷依據是:根據數組中為確定元素所在位置使用的最少的下標個數。例如,二維數組中想唯一確定一個元素的位置,至少需要使用 2 個下標, a[1][1]:行坐標為 1,列坐標為 1 的數據元素的值。

二、數組VS順序表 
①數組作為一種數據類型,作用是將類型相同的數據存儲在一整塊內存中,數組中存儲的數據之間沒有任何邏輯關系,誰也不認識誰。
②順序表作為線性表的存儲結構,存儲的這些數據元素在物理存儲結構上相鄰的同時,在邏輯結構上也相鄰,每個數據元素都清楚地知道緊挨着它的前邊的元素和后邊的元素。
簡要概述:用數組來存儲的線性表是順序表。

三、數組的邏輯結構
數組它可以看作線性表的推廣。數組作為一種數據結構其特點是結構中的元素本身可以是具有某種結構的數據,但屬於同一數據類型,比如:一維數組可以看作一個線性表,二維數組可以看作“數據元素是一維數組”的一維數組,三維數組可以看作“數據元素是二維數組”的一維數組,依此類推。所以,n 維數組可以看作是線性表的一種擴展。
               
數組是一個具有固定格式和數量的數據有序集,每一個數據元素有唯一的一組下標來標識,因此,在數組上不能做插入、刪除數據元素的操作。通常在各種高級語言中數組一旦被定義,每一維的大小及上下界都不能改變。在數組中通常做下面兩種操作:
(1)取值操作:給定一組下標,讀其對應的數據元素。
(2)賦值操作:給定一組下標,存儲或修改與其相對應的數據元素。
我們着重研究二維和三維數組,因為它們的應用是廣泛的,尤其是二維數組。

四、數組的存儲

按元素的下標求m×n二維數組中某個數據元素 aij地址的計算:
  ①行優先:LOC(aij) = LOC(a00) + ( i*n + j ) * s
  ②列優先:LOC(aij) = LOC(a00) + ( i*m + j ) * s
其中,LOC(i,j) : aij 在內存中的地址;LOC(0,0) : a00 存儲的地址,其實就是整個二維數組存放的起始地址。

【例】設二維數組A[6][10],每個數組元素占4個存儲單元,若按行優先順序存放的數組元素A[3][5]的存儲地址是1000,求A[0][0]的存儲地址。
解: 1000= X + (10*3+5)*4   解的:X=860

五、特殊矩陣的壓縮存儲
如果矩陣中有很多數值相同的數據元素,在存儲時,可以考慮對其進行適當的壓縮存儲。
有必要壓縮存儲的矩陣大致分為兩大類:
  ①矩陣中含有大量的相同數值,稱為特殊矩陣(例如對稱矩陣和上下三角矩陣)。
  ②矩陣中只有極少量的元素是非 0 元素,稱為稀疏矩陣
兩類矩陣壓縮存儲的方法:
  ①特殊矩陣中,對於相同的數據元素,只存儲一個。
  ②稀疏矩陣中,只需要存儲非 0 元素。

(1)對稱矩陣 

定義:n階矩陣中的元素滿足: aij = aji ( i 為行標, j 為列標)

圖為 3 階對稱矩陣,圖中的虛線為矩陣的 “主對角線” ,主對角線上方區域稱為 “上三角” ;主對角線下方稱為 “下三角” ,沿主對角線對稱的數據元素一一相等,所以對於此矩陣來說,只需要存儲 6 個元素即可(1,2,3,4,5,6)。
原來需要n*n個存儲單元,現在只需要n(n+1)/2個存儲單元了,節約了n(n-1)/2個存儲單元,當n較大時,這是可觀的一部分存儲資源。

若將對稱矩陣壓縮存儲在一維數組 S[k] 中,矩陣中數據元素在數組中存儲的位置和所在的行標(用 i 表示)和列標(用 j 表示)有關。
對稱矩陣沿主對角線對稱的數據元素相等,任選一邊的數據元素進行存儲即可:
  ①存儲下三角區域的數據元素

                
  ②存儲上三角區域的數據元素

                                            

綜上所述,對於對稱矩陣中的任意元素aij,若令I=max(i,j),J=min(i,j),則將上面兩個式子綜合起來得到: k=I*(I-1)/2+J-1

(2)上下三角矩陣
定義:上下三角矩陣,和對稱矩陣類似,不同在於,上三角矩陣是指主對角線下方的元素(不包括主對角線上的)都是常數C(包括數值 0 );同理,下三角矩陣是指主對角線上方的元素都是常數C。

存儲時,上(下)三角存儲上(下)三角的數據元素,除此之外,額外存儲一個下(上)三角含有的常數C(圖中,C==0)

(3)對角矩陣
定義:除了主對角線和它的上下方若干條對角線的元素外,所有其他元素都為零(或同一個常數c)

 (4)稀疏矩陣

定義:矩陣中只含有少量的非 0 元素,相比於使用普通方式將矩陣中的所有數據元素一一存儲,只存儲非 0 元素更節省內存空間。

矩陣壓縮存儲的方式有 3 種,分別為:①三元組順序表、②行邏輯鏈接的順序表、③十字鏈表

①三元組順序表
定義:除了要存儲非 0 元素的值之外,還需要存儲元素所在矩陣中的行標 i 和列標 j ,三個元素構成三元組(行標,列標,元素值)

結構體:

//三元組結構體
typedef struct {
    int i,j;//行標i,列標j
    int data;//元素值
}triple;

每個稀疏矩陣的表示,需要存儲矩陣中所有非 0 元素的三元組,並且還需要記錄矩陣的行數和列數,這樣才能唯一確定一個稀疏矩陣。

矩陣的結構也需要使用結構體實現:

#define number 100
//矩陣的結構表示
typedef struct {
    triple data[number];//存儲該矩陣中所有非0元素的三元組
    int n,m,num;//n和m分別記錄矩陣的行數和列數,num記錄矩陣中所有的非0元素的個數
}TSMatrix;

//例如,對於圖 3 的稀疏矩陣來說,即將(2,2,3)、(2,3,4)、(3,2,5)存儲進 data 數組,並且存儲稀疏矩陣的行數 3 和列數 3 ,該稀疏矩陣中非 0 元素有 3 個。

                                                                  

②行邏輯鏈接的順序表
使用三元組順序表存儲矩陣后,當需要提取矩陣某一行的非 0 元素時,需要遍歷整個順序表。

為了提高查找的效率,在三元組順序表的基礎上,增加一個數組用於記錄每一行第一個非 0 元素的存儲位置,這樣的存儲結構,稱為:行邏輯鏈接的順序表。

#define number 100
typedef struct {
    int i,j;
    int data;
}triple;
typedef struct {
    triple data[number];
    int rpos[number];//存儲各行第一個非0元素在三元組表中的位置
    int n,m,num;
}TSMatrix;

③十字鏈表
以上兩種存儲稀疏矩陣的方法,說到底,還是操作數組,在進行矩陣運算過程中,如果有插入非 0 元素或者刪除某一個元素的操作,可能需要大量的移動數組中的三元組。
例如在進行“將矩陣 B 加到矩陣 A 上”的操作時,矩陣 A 中的數據元素會發生很大的變化,之前的 0 元素可能變成非 0 元素,非 0 元素也可能變成 0 (正負數相加為 0)。在這種情況下,就需要使用鏈表的存儲結構來存儲矩陣,這種存儲方式稱為:十字鏈表法。

例如,將下列矩陣以十字鏈表的方式存儲起來:

采用十字鏈表法存儲矩陣的非 0 元素時,鏈表中的結點由 5 部分組成:

兩個指針域:一個指向所在列的下一個元素,一個指向所在行的下一個元素。

typedef struct OLNode{
    int i,j;
    int data;
    struct OLNode * right,*down;
}OLNode;
//此結構體表示一個矩陣,其中包含矩陣的行數,列數,非0元素的個數以及用於存儲各行以及各列元素頭指針的動態數組rhead和chead。
typedef struct {
    OLNode * rhead,*chead;
    int n,m,num;
}CrossList;

總結:

稀疏矩陣的三種不同的存儲方法,采用哪種方法要看程序具體要實現的功能:
  --如果想完成例如矩陣的轉置這樣的操作,宜采用三元組順序表;
  --如果想實現矩陣的乘法這樣的功能,宜采用行邏輯鏈接的順序表;
  --如果矩陣運算過程中(例如矩陣的加法),需要不斷地插入非 0 元素或刪除變為 0 的元素,宜采用十字鏈表法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM