1 稀疏矩陣:
假設在m*n的矩陣中,有t個元素不為0.令q=t/(m*n),稱q為矩陣的稀疏因子。通常認為q<=0.05的時候就認為稀疏矩陣。
2 三元組順序表
如果對每一個元素都分配存儲空間的話,矩陣含有大量的0則會造成資源浪費。所以一般我們采用壓縮存儲的方式,除了存儲非0元素的值外,還要存儲相應的行和列。因此,稀疏矩陣可以表示成為非0元的三元組及行列數唯一確定 。
相關定義如下:
typedef int Status #define MAX_ROW 100 #define MAX_SIZE 101 typedef int elemtype; typedef struct { int row; //設定行索引值 int col; //設定列索引值 elemtype value; //設定元素值 }Triple; typedef struct { int rn; //設定行數 int cn; //設定列數 int tn; Triple data[MAX_SIZE]; // 設定 數組 }TMatrix;
轉置的操作(按照一定順序進行的轉置):
這里我們注意,存儲方式是按行存儲的。
時間復雜度為rn*tn. 當tn遠遠小於rn*cn的時候使用,否則時間復雜度相比傳統方法大的多。(犧牲時間來優化存儲)
//轉置操作 void TransposeSMatrix(TMatrix M, TMatrix T) { T.rn = M.cn; T.cn = M.rn; T.tn = M.tn; if (T.tn) { q = 1; for (col = 1; col < M.rn; ++col) for (p = 1; p < M.tn; ++p) if (M.data[p].cn == col) { T.data[q].rn = M.data[p].cn; T.data[q].cn = M.data[p].rn; T.data[q].value = M.data[p].value; } } }
快速轉置:
//快速轉置 void FastTranspodeSMtrix(TMatrix M, TMatrix T) { T.rn = M.cn; T.cn = M.rn; T.tn = M.tn; if (T.tn) { for (col = 1; col < M.rn; ++col) num[col] = 0; for (t = 1; t <= M.tnl++t) ++num[M.data[t].cn]; cpot[1] = 1; //求第col列中的第一個非0元素在M.data中的序號 for (col = 2; col <= M.tn; ++p) cpot[col] = cpot[col - 1] + num[col - 1]; for (p=1; p <= M.tn; ++p) { col = M.data[p].cn; q = cpot[col]; T.data[q].rn = M.data[p].cn; T.data[q].cn = M.data[p].rn; T.data[q].value = M.data[p].value; ++cpot[col]; } } }
這個算法相比前一個多了兩個輔助向量。有兩個單次循環,所以時間復雜度為rn+cn.在M的非0元素個數和rn*cn相同的數量級的時候,時間復雜度與經典算法相同。
3 行邏輯連接的順序表:
我們把指示“行”信息的輔佐數組cpot固定在稀疏矩陣的存儲結構當中。
下述為兩個矩陣相乘的算法:
typedef struct { Triple data[MAX_SIZE]; int rpos[MAX_ROW]; int rn, cn, tn; }RLSMatrix; //三元組定義 默認行從1開始 void MultsMatrix(PLSMatrix a, RLSMatrix b, RLSMatrix c) { elemtype ctemp[MAX_SIZE]; int p, q, arow, ccol, brow, t; if (a.cn != b.rn) { cout << "Error" << endl; } else { c.rn = a.rn; c.cn = b.cn; c.tn = 0; if (a.tn * b.tn != 0) { for (arow = 1; arow <= a.rn; ++arow) //處理a的每一行 { ctemp[arow] = 0; //當前行各元素累加器清0 c.rpos[arow] = c.tn + 1; p = a.rpos[arow]; for (; p < a.rpos[arrow + 1]; ++p) //對當前行中的每一個非0元 { brow = a.data[p].col; //找到對應在b中行號 if (brow < b.rn) t = b.rpos[brow + 1]; else t = b.tn + 1; for (q = b.rpos[brow]; q < t; ++q) { ccol = b.data[q].col; ctemp[ccol] += a.data[p].value * b.data[q].value; } } for(ccol=1;ccol<=c.cn;++ccol) //壓縮存儲在行的非0元 if (ctemp[ccol] != 0) { if (++c.tn > MAX_SIZE) { cout << "Error" << endl; exit(0); } else c.data[c.tn] = (arow, ccol, ctemp[ccol]); } } } } }
4 十字鏈表
當矩陣的非0元個數和位置在操作過程中變化較大時,就不宜采用順序結構來表示三元組的線性表。例如,將矩陣B加到矩陣A,由於非0元的操作會引起數組中的變化。所以,采用鏈性表更加方便。
除了行,列和值外,還有兩個指針,用以標識同行中的下一個非0元和同列中的下一個非0元。
typedef struct Clnode { int i, j; elemtype e; struct Clnode* down, * right; }OLNode; //下面是整個稀疏矩陣的定義 typedef struct { int mu; int nu; int tu; OLNode* rhead; OLNode* chead; }CrossList; void CreateSMatrix_OL(CressList& M) { int m, n, t; //創建稀疏矩陣。采用十字鏈表表示 if (M) free(M); cout << '請輸入矩陣的M的行數,列數 和非0元素個數' << endl; cin >> m >> n >> t; M.mu = m; M.nu = n; M.tu = t; //數額uM的行數,列數和非0元素個數 if (!(M.rhead = (OLNode*)malloc((m + 1) * sizeof(OLNode)))) cout << "輸入數據出現錯誤" << endl; if (!(M.chead = (OLNode*)malloc((n + 1) * sizeof(OLNode)))) cout << "輸入數據出現錯誤" << endl; M.rhead[] = M.chead[] = NULL; for (cin >> i >> j >> e; i != 0; cin >> i >> j >> e;) { if (!(p = (OLNode*)malloc(sizeof(OLNode))))cout << "數據分配出現錯誤" << endl; p->i = i; p->j = j; p->e = e; //生成節點 if (M.rhead[i] == NULL || M.rhead[i]->j > j) { p->right = M.rhead[i]; M.rhead[i] = p; } else { //尋找在行表中插入的位置 for (q = M.rhead[i]; (q->right) && q->right->j < j; q = q->right); p->right = q->right; q->right = p; } if (M.chead[j] == NULL || M.chead[j]->i > i) { p->down = M.chead[j]; M.chead[j] = p; } else { //尋找在列表中插入的位置 for (q = M.chead[i]; (q->down) && q->down->j < j; q = q->down); p->down = q->down; q->down = p; //完成列的插入 } } }
5 廣義表
廣義表是線性表的推廣。
廣義表一般記作:
LS = (a1, a2, a3,..., an)
每個元素可以是單個元素,也可以是廣義表,分別稱為廣義表的原子和子表。
當廣義表非空的時候,我們習慣稱第一個元素為表頭,稱其余的元素為表尾(a2, a3, a4,..., an)。
值得提醒的是列表()和(())不同。前者為空表,長度為0;后者長度為1,可分解得到表頭,表尾均為空表()。
這種靈活性就需要用到鏈式存儲結構。需要兩種結構的節點,一種是原子的節點,用以表示原子,由標志域,值域組成;另一種是列表,由標值域,標識表頭的指針域和值域組成。
1 求廣義表的深度(遞歸):
廣義表的深度就是 各個子表深度的最大值加一,注意區分長度;
我們用ATOM(Tag)為0表示原子,反之為子表。
typedef struct GLNode { ElemTag tag; //公共部分,用於區分原子節點和表節點 int atom; GLNode* hp; GLNode* tp; }*GList; //求解表深度 int GListDepth(GList L) { int dep, max; GList pp; //采用頭尾鏈表存儲結構,求廣義表L的深度 if (!L) return 1; //空表深度為一 if (L->tag == ATOM) retun 0; //原子深度為0 for (max = 0, pp = L; pp; pp = pp->tp) { dep = GListDepth(pp->hp); //求以pp->hp為頭指針的子表深度 if (dep > max)max = dep; } return max + 1; }
2 遞歸復制廣義表
分別遞歸的復制表頭和表尾:
//復制廣義表 void CopyGlist(GList& T, GList L) { if (!L)T = NULL; //復制空表 else { if(!(T=(GList)malloc(sizeof(GLNode)))cout<<"數據分配出現錯誤"<<endl; //建表節點 T->tag=L->tag; if(L->tag==0)T->atom=L->atom; //復制單原子 else { CopyGlist(T->hp,L->hp); CopyGlist(T->tp,L->tp); } } }