5.3矩陣的壓縮儲存(稀疏矩陣轉置和快速轉置)
在矩陣中有許多值相同的元素或者是零元素。有時為了節省儲存空間,可以對這類矩陣進行壓縮儲存。所謂的壓縮儲存是指:為多個值相同的元值分配一個儲存空間;對零元不分配空間。
5.32稀疏矩陣
在m*n的矩陣中,有t個元素不為零。零α=t/m*n,稱 α為矩陣的稀疏因子。通常認為α<=0.05時稱為稀疏矩陣。
對於稀疏矩陣的非零元我們有下面這個表示:
如:(,(1,2,12),(1,3,9),(3,1,-3),(3,6,14),(4,3,24),(5,2,18),(6,1,15),(6,4,-7))
如下圖所示:
下面我們來看下三元組的結構體:
下面是書中程式碼(嚴蔚敏版的資料結構)
分析下:#define MAXSIZE 12500 //非零元素個數的最大值為12500 typedef struct{ int i, j; //該非零元的行下標和列下標 ElemType e; }Triple; typedef struct{ Triple data[MAXSIZE + 1]; //非零元三元組表,data[0]未用 int mu, nu, tu; //矩陣的行數、列數和非零元個數 }TSMatrix;
學過線性代數的都知道,轉置運算是一種最簡單的矩陣運算。這裡的思路和我們在連結串列上看到的有相似之處,結點變成了非零元素,線性表程式設計了非零元三元組的表。但多出了矩陣的行數,列數和非零元個數
對應一個m*n的矩陣M它的轉置矩陣是T,如下圖所示:
P·S:所謂的置換就是行和列進行交換,也就是關於主對角線對稱(矩陣左上角到右下角的線稱為主對角線)
而在程式裡面,他將會是下面這張圖這樣:
這裡的i對應行,j對應列,v表示值。
從分析a和b之間的差異可見只要做到:
1.將矩陣的行列值相互轉換。
2.將每個三元組中的i和j交換。
3.重排三元組之間的次序便可實現矩陣的轉置。
下面是書中給我們提供的虛擬碼:
下面來分析下:Status TransposeSMatrix(TSMatrix M, TSMatrix &T) { // 採用三元組順序表儲存表示,求稀疏矩陣M的轉置矩陣T int p, q, col; T.mu = M.nu; T.nu = M.mu; T.tu = M.tu; if (T.tu) { q = 1; for (col = 1; col <= M.nu; ++col) for (p = 1; p <= M.tu; ++p) if (M.data[p].j == col) { T.data[q].i = M.data[p].j; T.data[q].j = M.data[p].i; T.data[q].e = M.data[p].e; ++q; } } return Ok; } // TransposeSMatrix
下面是矩陣的快速轉置方法:col表示列,從M矩陣的第一列開始。比如col=1時,他先檢索M的第一列,把非零元中第一列的換成T中的第一行,就這個思路。
原理是:如果能預先確定矩陣M中每一列(即T中每一行)的第一個非零元在b.data中(上面那圖是b.data)恰當位置。那麼在對a.data中的三元組一次做轉置時,便可直接放到b.data中恰當的位置上去。
設兩個向量:num和cpot
num[col]表示矩陣M中第col列中的非零元素個數。
cpot[col]指M中第col列的第一個非零元在b.data中的恰當位置。
有下面兩個公式:
cpot[1]=1;
cpot[col]=copt[col-1]+num[col-1] 2<=col<=a.nu
圖如下:
下面來分析下這個表:
下面是書中程式碼:cpot[1]=1.
cpot[2]=num[1]+cpot[1]=1+2=3
cpot[3]=num[2]+cpot[2]=2+3=5
cpot[4]=num[3]+cpot[3]=2+5=7
cpot[5]=num[4]+cpot[4]=1+7=8
cpot[6]=num[5]+cpot[5]=0+8=8
cpot[7]=num[6]+cpot[6]=1+8=9
這個表就是這麼填的,但是在程式碼裡面就不一樣了。我這裡先提一下,程式碼裡面有覆蓋和範圍這種概念,這是什麼意思,意思就是,大家看cpot[5]和cpot[6]都是8,那麼在最後,他只會保留cpot[6],而cpot[7]他這裡是9,但本身就只有8個元素,哪來第九個呢?所以這個cpot[9]在程式裡面是沒有用的。
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T)
{
// 採用三元組順序表儲存表示,求稀疏矩陣M的轉置矩陣T
int col, t, p, q;
int num[20], cpot[20];
T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
if (T.tu) {
for (col = 1; col <= M.nu; ++col) //對列數進行初始化
num[col] = 0;
for (t = 1; t <= M.tu; ++t) // 求 M 中每一列所含非零元的個數
++num[M.data[t].j];
cpot[1] = 1;
// 求 M 中每一列的第一個非零元在 b.data 中的序號
for (col = 2; col <= M.nu; ++col)
cpot[col] = cpot[col - 1] + num[col - 1];
for (p = 1; p <= M.tu; ++p)
{
col = M.data[p].j;
q = cpot[col];
T.data[q].i = M.data[p].j;
T.data[q].j = M.data[p].i;
T.data[q].e = M.data[p].e;
++cpot[col];
} // for
} // if
return OK;
} // FastTransposeSMatrix
分析如下:
如果還有同學不懂,下面我給出全部的程式碼。這個程式的關鍵就是他只用了一個for迴圈,而上面那個程式用了兩個for迴圈,這使得時間複雜度降低了。這個for(p=1;p<M.tu;++p).這個就是我剛剛在上表說的那個意思。這裡有個++cpot[col]這是個關鍵
現在來解釋下++cpot[col]:
我們可以看到上表中cpot[col]只有1,3,5,7,8而2,4,5沒有,所以用了這個++cpot[col]後他就把每一列的第一個元素移到了第二個。
不懂的同學單步除錯下。
#include <stdio.h>
#include <windows.h>
#define MAXSIZE 1250
#define OK 1
#define ERROR 0
#define TRUE 1
#define FLASE 0
typedef int Status;
typedef int ElemType;
typedef struct{
int i, j; //該非零元的行下標和列下標
ElemType e; //非零元對應的值
}Triple;
typedef struct{
Triple data[MAXSIZE + 1]; //非零元三元組表,data[0]未用
int mu, nu, tu; //矩陣的行數,列數,非零元個數
}TSMatrix;
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T) //快速轉置
{ //採用三元組順序表儲存表示,求稀疏矩陣M的轉置矩陣T
T.mu = M.nu;
T.nu = M.mu;
T.tu = M.tu;
if (T.tu)
{
int col;
int num[100], cpot[100];
for (col = 1; col <= M.nu; ++col)
num[col] = 0; //num陣列的初始化
for (int t = 1; t <= M.tu; ++t)
++num[M.data[t].j]; //求M中每一列含有的非零元個數
cpot[1] = 1;
for (col = 2; col <= M.nu; ++col)
cpot[col] = cpot[col - 1] + num[col - 1]; //求cpot向量
int q;
for (int p = 1; p <= M.tu; ++p)
{
col = M.data[p].j;
q = cpot[col];
T.data[q].i = M.data[p].j;
T.data[q].j = M.data[p].i;
T.data[q].e = M.data[p].e;
++cpot[col];
}//for
}//if
return OK;
}//FastTransposeSMatrix
Status main()
{
TSMatrix M;
TSMatrix T;
printf("請輸入原矩陣:\n");
printf("行數、列數: ");
scanf_s("%d%d", &M.mu, &M.nu);
printf("元素總數: ");
scanf_s("%d", &M.tu);
printf("輸入各個對應壓縮值:\n");
for (int i = 1; i <= M.tu; ++i)
scanf_s("%d%d%d", &M.data[i].i, &M.data[i].j, &M.data[i].e);
FastTransposeSMatrix(M, T);
printf("轉置後行數、列數、元素總數非別為:\n%d %d %d\n\n", T.mu, T.nu, T.tu);
printf("值為:\n");
for (int t = 1; t <= T.tu; ++t)
printf("%d %d %d\n", T.data[t].i, T.data[t].j, T.data[t].e);
system("pause");
return OK;
}
執行結果如下:
和下面這圖是不是一模一樣