6 1 6 2 陣列 稀疏矩陣

2021-08-10 01:43:00 字數 4123 閱讀 9854

陣列是一種由相同資料型別構成的序列。陣列的本質就是乙個線性表。對於一維陣列,該線性表中的元素型別就是需要存放的型別elementtype,對於多維陣列,其維度d≥

2 ,則可以認為線性表的元素型別是d−

1 維陣列。這樣來看,陣列也是一種遞迴的定義。

對於陣列來說,通常只有以下兩種操作:讀取和寫入。這兩種操作需要乙個輸入向量來表示下標(即用來定位)。

陣列中最常見的是一維陣列和二維陣列,分別可以對應線性代數中的向量(vector)和矩陣(matrix)。

在教材採用的c/c++語言中,可以很自然的使用形如int a[10]這樣的語句來定義乙個陣列。對於這樣定義的陣列,其長度是定長的,不允許在執行時改變。這樣的陣列定義也只能出現在棧記憶體區或者全域性記憶體區。c99標準支援int n = 10; int a[n];這樣的用法,但是c++11標準不支援(因為stl容器是乙個更好的替代)。對於堆記憶體的陣列,可以使用指標,如int * a = (int *)malloc(sizeof(int) * 10);來建立乙個陣列(指標指向陣列的首位址);實際上,利用這種方式包裝好乙個容器,例如stl的std::vector類,是更方便、更安全、更有效率的。順帶一提,stl的std::vector類不支援直接存放二維(多維)陣列,只能使用vector>這種類似的方式進行存放。這樣在使用push_back方法將新的一維陣列壓入容器,可能存在反覆執行建構函式的開銷;對於c++11可以使用右值語義(r-value)和std::move以盡量減小這個問題帶來的效率損失。

陣列是一種線性表,因此需要連續的存放在儲存器的相應位置。對於一維陣列的儲存結構,其實比較簡單。在c/c++語言中,只需要乙個指標指向陣列的首位址,就能夠表示乙個陣列。當然,必須要有乙個標記,來指示陣列的結束(例如使用length字段標識陣列的長度,亦或c風格字串的\0結束符)。

因此,對於乙個一維陣列a,其元素a[i](按照c/c++的習慣i從0開始)的實際位址p=

a+ki

。 而對於乙個二維陣列,其輸入是乙個二元組,因此需要設法構造出一種對映,將其對映在儲存器的連續的相應位置。常用的方法有行優先列優先

對於乙個二維陣列a[m][n],行優先是指,a[i]取出的元素,是表示第

i 行的乙個一維陣列;而列優先a[i]表示的是第i列的乙個一維陣列。因此不難得出下面的計算公式:對於a[m][n]a[i][j]元素的位置

行優先:p=

a+k(

ni+j

)(i 個滿行,緊接著的一行的第

j個元素)

列優先:p=

a+k(

mi+j

) (

j 個滿列,緊接著的一列的第

j個元素)

c/c++的編譯器預設按照行優先存放。

最後需要提及的是,並不是所有語言的陣列都是使用一串連續儲存空間實現的。之前提及過的php的陣列,是使用的雜湊表來處理鍵值對(字串型別的鍵也可以作為陣列的下標)。

矩陣可以使用二維陣列進行儲存。對於特殊形式的矩陣(對稱陣、上下三角陣),只需要儲存一半的元素就可以了。

常見的儲存對映是,將二維降維,對映在一維陣列上。以下三角陣為例,對於am

n ,用乙個向量b=

(b1,

b2,.

..bm

n2) 來表示,則b1

=a11 ,b2

=a21 ,b3

=a22 ,以此類推,則ai

j=bi

(i−1

)2+j

(假設i,

j 都從

1 開始)。總計可以節省近一半的空間。

總之,特殊矩陣的儲存,在於根據問題的情況找到乙個合適的對映(能在o(

1)時間內計算出的),將二維降低到一維。

首先給出稀疏矩陣的乙個定義:乙個m×

n 的矩陣,當非零元個數t≪

mn,這樣的矩陣稱為稀疏矩陣(sparse matrix)。

稀疏矩陣的非零元分布具有隨機性,因此不能簡單作之前的對映。因此可以用鍵值對的線性表,來描述乙個稀疏矩陣。

這裡的鍵值對可以使用std::pair, elementtype>來表示,也可以使用c++11的std::tuple這個三元組類來表示。這裡我更傾向使用鍵值對的表示,前乙個pair作為二元輸入向量,而外層的pair的第二個元素作為下標為輸入向量的位置的值。

因此可以用std::vector, elementtype>>來表示乙個稀疏矩陣。

初始情況下,這個線性表容器是空的,表示這個矩陣是零矩陣

o (不含任何非零元)。對於乙個非零的稀疏矩陣表示,修改(i

,j)的元素值為v(

v≠0)

,即((

i,j)

,v) ,則首先需要判斷(i

,j) 是否已經為非零元,即遍歷一次容器,查詢一下是否存在鍵為(i

,j) 的元素。如果是,直接修改;否則,需要在合適位置將((

i,j)

,v) 放進去(為了維護有序性,按照下標的輸入向量排序,便於查詢)。

對於乙個稀疏矩陣而言,其賦值操作的最壞執行時間取決於非零元的個數

t 而並非矩陣的實際元素個數mn

。而稀疏矩陣本身就是為了避免空間浪費的,屬於時間換空間。此時無論是o(

lgn) 的紅黑樹的std::map還是o(

1)的雜湊表的std::unordered_map都是會造成大量空間浪費,違背了稀疏矩陣的初衷。

已知乙個二維陣列a[

m][n

] ,其每一行的元素都遞增,且每一行的第乙個元素都大於上一行的任意元素。求這個陣列中的指定元素

x 。

最原始的辦法是進行樸素搜尋,需要o(

mn)的時間。如果要利用有序性,可以採用二分法。對於每一行元素都遞增,可以對行使用二分。這樣就有了o(

mlgn)

的時間。由於每一行的第乙個元素都大於上一行的所有元素,因此可以先二分確定元素可能處在的行,然後再對行進行二分。這樣的時間複雜度為o(

lgm+lg

n)。

說起來很簡單,但是編碼還是很有難度的。尤其是第一層二分時,當元素不能在這一行時,必然滿足小於第乙個元素或者大於最後乙個元素。除此之外,lower_bound返回的是不小於指定值的最小下標。因此需要判斷返回的值是否等於需要查詢的值。

//not found constant define

const

std::pair not_found(-1, -1);

/*** @brief 查詢二維陣列中指定元素的位置

* @param std::vector> & matrix 輸入矩陣

* @param int val 待查詢元素的值

* @return std::pair返回二元組表示的元素的位置

*/std::pair matrix_find(std::vector

> & matrix, int val)

//定位元素可能在的行

int row = (cbegin == cend) ? cend : cmid;

//利用lower_bound查詢所在位置

std::vector

::iterator iter = lower_bound(matrix[row].begin(), matrix[row].end(), val);

if (iter != matrix[row].end() && *iter == val)

return make_pair(row, iter - matrix[row].begin());

else

return not_found;

}

陣列(稀疏矩陣)

typedef struct triple typedef struct xishuarray int triarray xishuarray m 建立乙個稀疏矩陣 輸入行數 列數,支援亂序輸入三元組,並計數 m.num num num的值即為此矩陣的非零元個數 for p 1 p m.num 1 ...

稀疏陣列,稀疏矩陣概念

稀疏矩陣 矩陣中非零元素的個數遠遠小於矩陣元素的總數,並且非零元素的分布沒有規律,通常認為矩陣中非零元素的總數比上矩陣所有元素總數的值小於等於0.05時,則稱該矩陣為稀疏矩陣 sparse matrix 該比值稱為這個矩陣的稠密度。稀疏陣列 稀疏陣列可以看做是普通陣列的壓縮,但是這裡說的普通陣列是值...

陣列的壓縮儲存(特殊矩陣 稀疏矩陣)

其實這裡就不應該說是陣列了,而應該是說行列式,對稱矩陣 a i j a j i 上下三角行列式 對角線以下 以上的元素均為0 帶狀行列式 這個可能不太好理解,舉乙個例子 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 這時看對角線比較舒服,可以看出...